RRD - Add many new invariants/ predicates#25
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a comprehensive set of new graph invariants and predicates to the graphcalc library, organized into three new modules and several additions to existing modules.
Changes:
- Adds local invariants module with functions for computing graph parameters on neighborhood-induced subgraphs
- Adds critical invariants module with vertex/edge deletion sensitivity analysis functions
- Adds core invariants module with functions for computing the intersection of optimal solution sets
- Extends degree.py with 5 new degree-based invariants (irregularity, degree counts)
- Extends classics.py with 6 new classic graph invariants (arboricity, linear arboricity, bipartite number, average distance, path cover number)
- Extends basics.py with 2 new C4-related predicates
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 20 comments.
Show a summary per file
| File | Description |
|---|---|
| local_invariants.py | New module providing local parameter evaluation on neighborhoods (703 lines) with 9 exported functions |
| critical_invariants.py | New module for vertex/edge deletion criticality analysis (853 lines) with helper functions for sensitivity testing |
| core_invariants.py | New module for computing core sets (698 lines) with functions for finding forced vertices in optimal solutions |
| degree.py | Added irregularity, n1_degree_count, distinct_degree_count, count_of_maximum_degree_vertices, count_of_minimum_degree_vertices |
| classics.py | Added bipartite_number, average_distance, path_cover_number, arboricity, linear_arboricity with supporting helpers |
| basics.py | Added is_C4_free and is_induced_C4_free predicates for detecting 4-cycles |
| init.py | Added imports for the three new modules |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| -------- | ||
| >>> import networkx as nx | ||
| >>> G = nx.path_graph(5) # degrees: 1,2,2,2,1 | ||
| >>> n1_degree_count(G) |
There was a problem hiding this comment.
The function call in this example is missing the module prefix. It should be gc.n1_degree_count(G) to be consistent with other examples in this file.
| >>> n1_degree_count(G) | |
| >>> gc.n1_degree_count(G) |
| >>> count_of_minimum_degree_vertices(G) | ||
| 2 | ||
| >>> H = nx.empty_graph(4) # all degrees 0 | ||
| >>> count_of_minimum_degree_vertices(H) |
There was a problem hiding this comment.
The function calls in these examples are missing the module prefix. They should be gc.count_of_minimum_degree_vertices(G) and gc.count_of_minimum_degree_vertices(H) to be consistent with other examples in this file.
| >>> count_of_minimum_degree_vertices(G) | |
| 2 | |
| >>> H = nx.empty_graph(4) # all degrees 0 | |
| >>> count_of_minimum_degree_vertices(H) | |
| >>> gc.count_of_minimum_degree_vertices(G) | |
| 2 | |
| >>> H = nx.empty_graph(4) # all degrees 0 | |
| >>> gc.count_of_minimum_degree_vertices(H) |
| >>> # A path is coverable by a single path | ||
| >>> G = nx.path_graph(6) | ||
| >>> path_cover_number(G) | ||
| 1 | ||
|
|
||
| >>> # An edgeless graph on n vertices needs n singleton paths | ||
| >>> H = nx.empty_graph(5) | ||
| >>> path_cover_number(H) | ||
| 5 | ||
|
|
||
| >>> # Two disjoint paths need two paths in the cover | ||
| >>> J = nx.disjoint_union(nx.path_graph(3), nx.path_graph(4)) | ||
| >>> path_cover_number(J) |
There was a problem hiding this comment.
The function calls in these examples are missing the module prefix. They should be gc.path_cover_number(G), gc.path_cover_number(H), and gc.path_cover_number(J) to be consistent with the established pattern in this codebase.
| >>> # A path is coverable by a single path | |
| >>> G = nx.path_graph(6) | |
| >>> path_cover_number(G) | |
| 1 | |
| >>> # An edgeless graph on n vertices needs n singleton paths | |
| >>> H = nx.empty_graph(5) | |
| >>> path_cover_number(H) | |
| 5 | |
| >>> # Two disjoint paths need two paths in the cover | |
| >>> J = nx.disjoint_union(nx.path_graph(3), nx.path_graph(4)) | |
| >>> path_cover_number(J) | |
| >>> import graphcalc as gc | |
| >>> # A path is coverable by a single path | |
| >>> G = nx.path_graph(6) | |
| >>> gc.path_cover_number(G) | |
| 1 | |
| >>> # An edgeless graph on n vertices needs n singleton paths | |
| >>> H = nx.empty_graph(5) | |
| >>> gc.path_cover_number(H) | |
| 5 | |
| >>> # Two disjoint paths need two paths in the cover | |
| >>> J = nx.disjoint_union(nx.path_graph(3), nx.path_graph(4)) | |
| >>> gc.path_cover_number(J) |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 33 out of 33 changed files in this pull request and generated 9 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 34 out of 35 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -1,6 +1,6 @@ | |||
|
|
|||
| from typing import Hashable, List | |||
There was a problem hiding this comment.
Duplicate import: List is imported twice from the typing module on consecutive lines. The second import on line 3 also imports Sequence, making the first import redundant. Remove line 2.
| index and the Wiener index. *Journal of Chemical Information and Computer Sciences*, | ||
| 33(6), 1006-1009 (1993). | ||
| """ | ||
| return 2*sum((1/(gc.degree(G, v) + gc.degree(G, u)) for u, v in G.edges())) |
There was a problem hiding this comment.
Inefficiency: gc.degree(G, v) and gc.degree(G, u) are called for every edge. This makes O(m) degree lookups where m is the number of edges, which could be expensive. Consider pre-computing degrees once:
deg = dict(G.degree())
return 2*sum(1/(deg[u] + deg[v]) for u, v in G.edges())This pattern is used correctly in other index functions in this same file (e.g., randic_index line 94-95).
| return 2*sum((1/(gc.degree(G, v) + gc.degree(G, u)) for u, v in G.edges())) | |
| deg = dict(G.degree()) | |
| return 2 * sum(1 / (deg[u] + deg[v]) for u, v in G.edges()) |
|
|
||
| import math | ||
| import networkx as nx | ||
| import graphcalc as gc |
There was a problem hiding this comment.
Module 'graphcalc' is imported with both 'import' and 'import from'.
| import graphcalc as gc |
No description provided.