Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

GrishaVar
Copy link
Contributor

Implements #2684

  • By submitting this pull request, I assign the copyright of my contribution to The igraph development team.

@GrishaVar GrishaVar force-pushed the grigory_bound-diameter branch from af29a01 to 8482c56 Compare March 25, 2025 13:47
@GrishaVar
Copy link
Contributor Author

@szhorvat This PR now also implements igraph_set_remove; doing that was the path of least resistance. See #2287: I guess these sets will be deprecated/replaced at some point (which I think is a good idea), but you'll need an interface for the remove function either way. The implementation has the same issues with iteration as igraph_set_add, though I think in my specific case this should only affect performance.

I was hoping everything I do would stay in one PR, let me know if you want the sets in a different one (or if you want a different solution entirely)

Copy link

codecov bot commented Mar 25, 2025

Codecov Report

Attention: Patch coverage is 94.67681% with 14 lines in your changes missing coverage. Please review.

Project coverage is 84.72%. Comparing base (9f727ac) to head (e506aba).

Files with missing lines Patch % Lines
src/paths/dijkstra.c 82.92% 7 Missing ⚠️
src/core/set.c 91.54% 6 Missing ⚠️
src/paths/distances.c 99.17% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #2739      +/-   ##
===========================================
+ Coverage    84.68%   84.72%   +0.04%     
===========================================
  Files          380      380              
  Lines        62314    62577     +263     
  Branches     12192    12248      +56     
===========================================
+ Hits         52770    53021     +251     
- Misses        9544     9556      +12     
Files with missing lines Coverage Δ
src/paths/unweighted.c 96.34% <100.00%> (+0.57%) ⬆️
src/paths/distances.c 99.78% <99.17%> (-0.22%) ⬇️
src/core/set.c 94.08% <91.54%> (-1.84%) ⬇️
src/paths/dijkstra.c 95.58% <82.92%> (-1.20%) ⬇️

... and 2 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9f727ac...e506aba. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Member

@szhorvat szhorvat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few small comments now, we can discuss more in person.

@GrishaVar GrishaVar force-pushed the grigory_bound-diameter branch from 9fa9bf4 to 63f4199 Compare March 26, 2025 18:21
@GrishaVar
Copy link
Contributor Author

GrishaVar commented Mar 26, 2025

@szhorvat Added more tests, including Barabasi; they pass. Added clean-up for containers as well, without it the address sanitiser kept complaining. Also, I found an OBOE in the set_remove - it was accessing one element too many, but the unit tests didn't detect it because it was the element that got removed at the end. Wouldn't have found it without the sanitiser!

Remaining big TODOs:

  • Deal with disconnected graphs
  • Deal with directed graphs
  • Add vertex-choosing heuristic

@szhorvat
Copy link
Member

I wrote up some tips on efficient development: https://github.com/igraph/igraph/wiki/Tips-for-quicker-development

@szhorvat
Copy link
Member

szhorvat commented Mar 30, 2025

@GrishaVar Do you need any input from me right now? I hope I did not miss any questions.

@szhorvat szhorvat self-assigned this Mar 31, 2025
@szhorvat
Copy link
Member

szhorvat commented Apr 3, 2025

@GrishaVar Can you please push what you have so far? This is the equivalent of the draft report submission for you.

@szhorvat
Copy link
Member

szhorvat commented Apr 5, 2025

@GrishaVar Can you please push what you have so far? This is the equivalent of the draft report submission for you.

Please don't forget to push.

@GrishaVar
Copy link
Contributor Author

GrishaVar commented Apr 8, 2025

@szhorvat update:

  • I've added a heuristic for choosing the next vertex to explore. As the paper suggests, the first vertex is of max degree, after than we take largest upper and smallest lower eccentricity bound in a loop.
  • Disconnected graphs are supported now. We explore one component at a time until none are left; infinities from distance calculations are always ignored except to detect the components themselves, once per component. There is one new test for disconnected graphs, the same one igraph_diameter uses. Since the algorithm for doing this is not in the paper, the comments in that section are a bit more verbose.
  • The adjacency list thing is done, but only unweighted graphs. It looks like doing it for weighted graphs is a bit annoying because they use igraph_lazy_inclist_t instead. I think we discussed this at some point but I don't remember much about it; we should sync on this.
  • Remaining todos:
    • Convert everything to (optionally) weighted diameter
    • Move function to more appropriate places [will be done by @szhorvat]
    • Add igraph_set_difference, would be an O(nlogn) -> O(n) speedup
    • Figure out weird maxdegree_arg issue
    • Documentation and more tests (if needed?)
    • Get rid of all the prints

These are all pretty minor now, hopefully I can get this wrapped up soon 🙂

@szhorvat
Copy link
Member

szhorvat commented Apr 8, 2025

  • Convert everything to (optionally) weighted diameter

Let's put this to the bottom of the priority list, as it is very straightforward.

  • Get rid of all the prints

Optionally, you can use a debug macro that makes it possible to turn this on or off, on demand. You can grep the source tree for DEBUG to see a number of variations on this. The style used by MATCHING_DEBUG is probably one of the better ones.

It is up to you whether you want to do this. I don't have a strong opinion. You can judge best if people might feel the need to turn on debug logging in the future for this function.


Something that would be quite nice to have is a basic benchmark. You can see how these work in tests/benchmarks. I suggest looking at the more recent benchmark additions, and copying one.

The purpose of this is to make future tuning of this function easier.

I would put this second-to-last on the priority list, just above the weighted version. Actually, I would prioritize this over igraph_set_difference().

@GrishaVar GrishaVar force-pushed the grigory_bound-diameter branch 2 times, most recently from 4a87cbe to 24090e8 Compare April 11, 2025 14:31
@szhorvat
Copy link
Member

szhorvat commented Apr 11, 2025

  • Decide if set_remove needs to return an error code.
  • Add example (or remove example from docs).
  • Extend benchmarks (sparse, dense, Barabasi-Albert model)
  • Finish error checking, replace weights ? ... with if (weights) ...
  • Change lazy_inclist to inclist
  • Change argument order, https://github.com/igraph/igraph/wiki/Guidelines-for-function-argument-ordering
  • Include \experimental on a separate line at the beginning of the doc comment

@szhorvat
Copy link
Member

szhorvat commented Apr 12, 2025

Could you please factor out the cleanup of debug() macros into a separate PR?

@GrishaVar GrishaVar force-pushed the grigory_bound-diameter branch from 80d39f5 to 9b7e6dd Compare April 12, 2025 16:05
@GrishaVar GrishaVar requested a review from szhorvat April 12, 2025 16:06
@GrishaVar
Copy link
Contributor Author

@szhorvat I rebased to remove the changes in this commit. See #2749 for the new PR.

I can't check your checkboxes, but all the things you listed are done.

@szhorvat szhorvat added this to the 1.0 milestone Apr 25, 2025
@szhorvat
Copy link
Member

Copilot reviews got support for C a few days ago. Let's see if produces anything useful.

@szhorvat szhorvat requested a review from Copilot April 25, 2025 22:08
Copilot

This comment was marked as outdated.

@szhorvat szhorvat force-pushed the grigory_bound-diameter branch from 9b7e6dd to 75f846e Compare May 11, 2025 00:52
@ntamas
Copy link
Member

ntamas commented May 15, 2025

@szhorvat Is this ready for merging, do you still want to have another review round, or shall I review it before merging?

@szhorvat
Copy link
Member

It'd be great if you could do a review, but I want to go through it again in detail before merging. It's best if I do that only after I'm back in Reykjavík. Note: I did not check the set removal function at all yet.

@szhorvat szhorvat requested a review from Copilot July 2, 2025 23:19
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

Implements a faster algorithm for computing graph diameters based on Borassi et al. (2015) paper, offering potential O(|E|) complexity for real-world networks versus the traditional O(|V||E|).

  • Added igraph_diameter_bound() in src/paths/distances.c with optimized BFS-based diameter computation
  • Enhanced src/core/set.c with three new operations: push_back, remove, and difference for O(log n) set manipulations
  • Added specialized single-source variants igraph_distances_1 and igraph_distances_dijkstra_1 in unweighted.c/dijkstra.c for improved performance
  • Included comprehensive test suite in tests/unit/igraph_diameter_bound.c covering various graph types and edge cases
  • Currently supports only undirected graphs, with thorough validation against original diameter calculation methods

14 files reviewed, 12 comments
Edit PR Review Bot Settings | Greptile

IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_set_add(igraph_set_t *set, igraph_integer_t e);
IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_set_push_back(igraph_set_t *set, igraph_integer_t e);
IGRAPH_PRIVATE_EXPORT void igraph_set_remove(igraph_set_t *set, igraph_integer_t e);
IGRAPH_PRIVATE_EXPORT void igraph_set_difference(igraph_set_t *set, igraph_set_t *to_remove);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: consider marking to_remove parameter as const since it's not modified

Suggested change
IGRAPH_PRIVATE_EXPORT void igraph_set_difference(igraph_set_t *set, igraph_set_t *to_remove);
IGRAPH_PRIVATE_EXPORT void igraph_set_difference(igraph_set_t *set, const igraph_set_t *to_remove);

#define DENS 3.0/VCOUNT
#define REP 1000

igraph_erdos_renyi_game_gnp(&g, VCOUNT, DENS, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Graph created with IGRAPH_DIRECTED but all diameter computations use IGRAPH_UNDIRECTED mode. Either the graph creation should be undirected or tests should cover directed case too.

Comment on lines +281 to +282
IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes));
IGRAPH_FINALLY(igraph_2wheap_destroy, &Q);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Missing FINALLY cleanup handler for Q heap if vector_resize fails on line 284

igraph_destroy(&g);

#undef VCOUNT
#undef DENS
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: DENS macro undefined but used in Barabási graph section - should be ECOUNT instead

Suggested change
#undef DENS
#undef ECOUNT

Comment on lines +294 to +296
* \ingroup set
* \function igraph_set_remove
* \brief Adds an element to the set.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Documentation header incorrectly says 'Adds an element to the set' but this is the remove function

Suggested change
* \ingroup set
* \function igraph_set_remove
* \brief Adds an element to the set.
/**
* \ingroup set
* \function igraph_set_remove
* \brief Removes an element from the set.
*

Comment on lines +89 to +91
igraph_diameter_bound(&g, NULL, &result, IGRAPH_UNDIRECTED, false);
igraph_diameter(&g, NULL, &reference, NULL, NULL, NULL, NULL, IGRAPH_DIRECTED, 0);
IGRAPH_ASSERT(result == reference);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: IGRAPH_UNDIRECTED used for bound but IGRAPH_DIRECTED used for reference - this inconsistency could mask errors

igraph_destroy(&g);

// weighted ring graph
igraph_vector_init_real(&weights, 9, 1.0, 2.675, 3.0, 4.0, 5.5, 1.0, 1.0, 1.0, 1.0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: weights vector initialized with 9 elements but ring graph has 10 nodes (10 edges) - potential buffer overflow

Comment on lines +1056 to +1058
if (!weights && igraph_set_size(&current_component) <= *diameter) {
break;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Possible early termination bug - if !weights check should be comparing against current best bound not global diameter

Comment on lines +1168 to +1170
update_to_max(&VECTOR(ecc_lower)[w], ecc_v-d);
update_to_max(&VECTOR(ecc_lower)[w], d);
update_to_min(&VECTOR(ecc_upper)[w], ecc_v+d);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Update of ecc_lower twice using different formulas - these should be combined with a max() operation for clarity

Comment on lines +932 to +937
const igraph_t *graph, // input graph
const igraph_vector_t *weights, // optional weights
igraph_real_t *diameter, // output diameter value
igraph_bool_t directed, // treating this graph as undirected
igraph_bool_t unconn // false: disconnected returns INF; true: returns largest diameter
) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Parameter 'directed' description seems reversed - says 'treating this graph as undirected' but parameter indicates directedness

Suggested change
const igraph_t *graph, // input graph
const igraph_vector_t *weights, // optional weights
igraph_real_t *diameter, // output diameter value
igraph_bool_t directed, // treating this graph as undirected
igraph_bool_t unconn // false: disconnected returns INF; true: returns largest diameter
) {
const igraph_t *graph, // input graph
const igraph_vector_t *weights, // optional weights
igraph_real_t *diameter, // output diameter value
igraph_bool_t directed, // whether to consider directed paths
igraph_bool_t unconn // false: disconnected returns INF; true: returns largest diameter

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Implements the diameter‐bounding algorithm (#2684) and adds supporting set utilities, shortest‐path helpers, tests, benchmarks, example, and documentation updates.

  • Added igraph_diameter_bound (and helper functions) to compute graph diameter with bounding/pruning.
  • Introduced igraph_distances_1 and igraph_distances_dijkstra_1 for single‐source BFS/Dijkstra.
  • Expanded set interface with igraph_set_push_back/remove/difference and added unit tests and benchmarks.

Reviewed Changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/paths/distances.c Core implementation of igraph_diameter_bound.
src/paths/unweighted.c Added igraph_distances_1 single‐source BFS helper.
src/paths/dijkstra.c Added igraph_distances_dijkstra_1 single‐source Dijkstra helper.
src/core/set.h / src/core/set.c Extended set API: push_back, remove, difference.
tests/unit/igraph_diameter_bound.c Unit tests for igraph_diameter_bound.
tests/unit/set.c / tests/unit/set.out Unit tests and expected output for new set operations.
tests/benchmarks/igraph_diameter_bound.c Benchmarks for bound vs. original diameter routines.
include/igraph_paths.h / interfaces/functions.yaml API and header updates for new functions.
examples/simple/igraph_diameter_bound.c Example for new function.
doc/structural.xxml Documentation inclusion for igraph_diameter_bound.
Comments suppressed due to low confidence (6)

src/core/set.c:355

  • The doc comment for this block is labeled \function igraph_set_remove but the implementation below is igraph_set_difference. Please update the \function tag to igraph_set_difference.
 * \function igraph_set_remove

src/paths/dijkstra.c:252

  • The doc comment for igraph_distances_dijkstra_1 is just TODO. Please provide a proper function description, parameters, and return value.
 * TODO

examples/simple/igraph_diameter_bound.c:33

  • The example calls igraph_diameter_bound but does not display or verify result. Consider adding printf or assertion to show the computed diameter in this example.
    igraph_diameter_bound(&g, NULL, &result, IGRAPH_UNDIRECTED, 0);

interfaces/functions.yaml:439

  • The parameter in code is named unconn but here it’s labeled unconnected. To avoid confusion for API consumers, align the YAML parameter name with the code (unconn) or vice versa.
igraph_diameter_bound:

src/paths/unweighted.c:608

  • No unit tests cover igraph_distances_1. Please add tests for this new helper to ensure correct behavior, including unreachable vertices and small graphs.
igraph_error_t igraph_distances_1(const igraph_adjlist_t *adjlist, igraph_vector_t *res, igraph_integer_t from) {

src/paths/dijkstra.c:254

  • igraph_distances_dijkstra_1 is introduced without any tests. Add unit tests that validate single‐source weighted distances, negative/NaN weight handling, and empty graphs.
igraph_error_t igraph_distances_dijkstra_1(const igraph_t *graph,

// component cannot increase the lower bound. So we can safely skip it
// without exploring
if (!weights && igraph_set_size(&current_component) <= *diameter) {
break;
Copy link

Copilot AI Jul 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using break here exits the outer while (!igraph_set_empty(&to_inspect)) loop, so later components are never inspected. Replace break with continue to skip only this component.

Suggested change
break;
continue;

Copilot uses AI. Check for mistakes.

@ntamas ntamas modified the milestones: 1.0, 1.1 Aug 28, 2025
@ntamas
Copy link
Member

ntamas commented Aug 28, 2025

Retargeting for 1.1 as it is not API-breaking and we need to clear the issue list for the 1.0 milestone now that we are getting closer to the release date.

@ntamas ntamas changed the base branch from develop to master September 19, 2025 13:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants