Skip to content

Commit e12f11c

Browse files
GiggleLiuisPANNclaude
authored
Fix #233: [Model] StrongConnectivityAugmentation (#652)
* Add plan for #233: [Model] StrongConnectivityAugmentation * feat: add directed strong connectivity check * feat: add strong connectivity augmentation model * feat: register strong connectivity augmentation * feat: add CLI support for strong connectivity augmentation * docs: add strong connectivity augmentation to paper * fix: address strong connectivity augmentation review findings * fix: address PR #652 review comments * docs: improve StrongConnectivityAugmentation CLI guidance * fix: add missing candidate_arcs field and suppress unused warning - Add candidate_arcs: None to empty_args() test helper in create.rs - Mark resolve_create_problem_ref as #[cfg(test)] since it's only used in test code Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: suppress unused warning on resolve_create_problem_ref This function exists on the PR branch but is currently only used in tests. Suppress with #[allow(dead_code)] rather than changing its visibility — not part of this PR's scope. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: clean up unrelated changes and dead code from SCA PR - Remove duplicate eswarantarjan1976 BibTeX entry - Revert unrelated README/docs workspace-local build instructions - Remove unused resolve_create_problem_ref and its tests - Revert unrelated KSat→Sat examples.json solution change - Remove redundant test_strong_connectivity_augmentation_paper_example Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: restore ProblemRef import removed by cleanup The import is needed by resolve_problem_ref which exists on main. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use correct DirectedGraph JSON paths in paper Typst template DirectedGraph serializes as {inner: {nodes, edges}}, not {num_vertices, arcs}. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Xiwei Pan <xiwei.pan@connect.hkust-gz.edu.cn> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 6802d3f commit e12f11c

15 files changed

Lines changed: 666 additions & 4 deletions

File tree

docs/paper/reductions.typ

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
"ShortestCommonSupersequence": [Shortest Common Supersequence],
109109
"MinimumSumMulticenter": [Minimum Sum Multicenter],
110110
"SteinerTree": [Steiner Tree],
111+
"StrongConnectivityAugmentation": [Strong Connectivity Augmentation],
111112
"SubgraphIsomorphism": [Subgraph Isomorphism],
112113
"PartitionIntoTriangles": [Partition Into Triangles],
113114
"FlowShopScheduling": [Flow Shop Scheduling],
@@ -1070,6 +1071,49 @@ is feasible: each set induces a connected subgraph, the component weights are $2
10701071
]
10711072
]
10721073
}
1074+
#{
1075+
let x = load-model-example("StrongConnectivityAugmentation")
1076+
let nv = x.instance.graph.inner.nodes.len()
1077+
let ne = x.instance.graph.inner.edges.len()
1078+
let arcs = x.instance.graph.inner.edges.map(e => (e.at(0), e.at(1)))
1079+
let candidates = x.instance.candidate_arcs
1080+
let bound = x.instance.bound
1081+
let sol = x.optimal.at(0)
1082+
let chosen = candidates.enumerate().filter(((i, _)) => sol.config.at(i) == 1).map(((i, arc)) => arc)
1083+
let arc = chosen.at(0)
1084+
let blue = graph-colors.at(0)
1085+
[
1086+
#problem-def("StrongConnectivityAugmentation")[
1087+
Given a directed graph $G = (V, A)$, a set $C subset.eq (V times V backslash A) times ZZ_(> 0)$ of weighted candidate arcs, and a bound $B in ZZ_(>= 0)$, determine whether there exists a subset $C' subset.eq C$ such that $sum_((u, v, w) in C') w <= B$ and the augmented digraph $(V, A union {(u, v) : (u, v, w) in C'})$ is strongly connected.
1088+
][
1089+
Strong Connectivity Augmentation models network design problems where a partially connected directed communication graph may be repaired by buying additional arcs. Eswaran and Tarjan showed that the unweighted augmentation problem is solvable in linear time, while the weighted variant is substantially harder @eswarantarjan1976. The decision version recorded as ND19 in Garey and Johnson is NP-complete @garey1979. The implementation here uses one binary variable per candidate arc, so brute-force over the candidate set yields a worst-case bound of $O^*(2^m)$ where $m = "num_potential_arcs"$. #footnote[No exact algorithm improving on brute-force is claimed here for the weighted candidate-arc formulation implemented in the codebase.]
1090+
1091+
*Example.* The canonical instance has $n = #nv$ vertices, $|A| = #ne$ existing arcs, #candidates.len() weighted candidate arcs, and bound $B = #bound$. The base graph already contains the directed 3-cycle $v_0 -> v_1 -> v_2 -> v_0$ and the strongly connected component on ${v_3, v_4, v_5}$, with only the forward bridge $v_2 -> v_3$ between them. The unique satisfying augmentation under this bound selects the single candidate arc $(v_#arc.at(0), v_#arc.at(1)))$ of weight #arc.at(2), closing the cycle $v_2 -> v_3 -> v_4 -> v_5 -> v_2$ and making every vertex reachable from every other. The all-zero configuration is infeasible because no path returns from ${v_3, v_4, v_5}$ to ${v_0, v_1, v_2}$.
1092+
1093+
#figure({
1094+
let verts = ((0, 1), (1.2, 1.6), (1.2, 0.4), (3.4, 1.0), (4.6, 1.5), (4.6, 0.5))
1095+
canvas(length: 1cm, {
1096+
for (u, v) in arcs {
1097+
draw.line(verts.at(u), verts.at(v),
1098+
stroke: 1pt + black,
1099+
mark: (end: "straight", scale: 0.4))
1100+
}
1101+
draw.line(verts.at(arc.at(0)), verts.at(arc.at(1)),
1102+
stroke: 1.6pt + blue,
1103+
mark: (end: "straight", scale: 0.45))
1104+
for (k, pos) in verts.enumerate() {
1105+
let highlighted = k == arc.at(0) or k == arc.at(1)
1106+
g-node(pos, name: "v" + str(k),
1107+
fill: if highlighted { blue.transparentize(65%) } else { white },
1108+
label: [$v_#k$])
1109+
}
1110+
})
1111+
},
1112+
caption: [Strong Connectivity Augmentation on a #{nv}-vertex digraph. Black arcs are present in $A$; the added candidate arc $(v_#arc.at(0), v_#arc.at(1)))$ is shown in blue. With bound $B = #bound$, this single augmentation makes the digraph strongly connected.],
1113+
) <fig:strong-connectivity-augmentation>
1114+
]
1115+
]
1116+
}
10731117
#{
10741118
let x = load-model-example("MinimumMultiwayCut")
10751119
let nv = graph-num-vertices(x.instance)

docs/paper/references.bib

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,17 @@ @book{garey1979
6464
year = {1979}
6565
}
6666

67+
@article{eswarantarjan1976,
68+
author = {K. P. Eswaran and Robert E. Tarjan},
69+
title = {Augmentation Problems},
70+
journal = {SIAM Journal on Computing},
71+
volume = {5},
72+
number = {4},
73+
pages = {653--665},
74+
year = {1976},
75+
doi = {10.1137/0205044}
76+
}
77+
6778
@article{gareyJohnsonStockmeyer1976,
6879
author = {Michael R. Garey and David S. Johnson and Larry Stockmeyer},
6980
title = {Some Simplified {NP}-Complete Graph Problems},

docs/src/cli.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ pred create Factoring --target 15 --bits-m 4 --bits-n 4 -o factoring.json
352352
pred create Factoring --target 21 --bits-m 3 --bits-n 3 -o factoring2.json
353353
pred create X3C --universe 9 --sets "0,1,2;0,2,4;3,4,5;3,5,7;6,7,8;1,4,6;2,5,8" -o x3c.json
354354
pred create MinimumTardinessSequencing --n 5 --deadlines 5,5,5,3,3 --precedence-pairs "0>3,1>3,1>4,2>4" -o mts.json
355+
pred create StrongConnectivityAugmentation --arcs "0>1,1>2,2>0,3>4,4>3,2>3,4>5,5>3" --candidate-arcs "3>0:5,3>1:3,3>2:4,4>0:6,4>1:2,4>2:7,5>0:4,5>1:3,5>2:1,0>3:8,0>4:3,0>5:2,1>3:6,1>4:4,1>5:5,2>4:3,2>5:7,1>0:2" --bound 1 -o sca.json
355356
```
356357
357358
For `LengthBoundedDisjointPaths`, the CLI flag `--bound` maps to the JSON field

problemreductions-cli/src/cli.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ Flags by problem type:
249249
LCS --strings
250250
FAS --arcs [--weights] [--num-vertices]
251251
FVS --arcs [--weights] [--num-vertices]
252+
StrongConnectivityAugmentation --arcs, --candidate-arcs, --bound [--num-vertices]
252253
FlowShopScheduling --task-lengths, --deadline [--num-processors]
253254
MinimumTardinessSequencing --n, --deadlines [--precedence-pairs]
254255
SCS --strings, --bound [--alphabet-size]
@@ -447,6 +448,9 @@ pub struct CreateArgs {
447448
/// Total budget for selected potential edges
448449
#[arg(long)]
449450
pub budget: Option<String>,
451+
/// Candidate weighted arcs for StrongConnectivityAugmentation (e.g., 2>0:1,2>1:3)
452+
#[arg(long)]
453+
pub candidate_arcs: Option<String>,
450454
/// Deadlines for MinimumTardinessSequencing (comma-separated, e.g., "5,5,5,3,3")
451455
#[arg(long)]
452456
pub deadlines: Option<String>,

problemreductions-cli/src/commands/create.rs

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use problemreductions::export::{ModelExample, ProblemRef, ProblemSide, RuleExamp
1010
use problemreductions::models::algebraic::{ClosestVectorProblem, BMF};
1111
use problemreductions::models::graph::{
1212
GraphPartitioning, HamiltonianPath, LengthBoundedDisjointPaths, MinimumMultiwayCut,
13-
MultipleChoiceBranching, SteinerTree,
13+
MultipleChoiceBranching, SteinerTree, StrongConnectivityAugmentation,
1414
};
1515
use problemreductions::models::misc::{
1616
BinPacking, FlowShopScheduling, LongestCommonSubsequence, MinimumTardinessSequencing,
@@ -78,6 +78,7 @@ fn all_data_flags_empty(args: &CreateArgs) -> bool {
7878
&& args.pattern.is_none()
7979
&& args.strings.is_none()
8080
&& args.arcs.is_none()
81+
&& args.candidate_arcs.is_none()
8182
&& args.potential_edges.is_none()
8283
&& args.budget.is_none()
8384
&& args.precedence_pairs.is_none()
@@ -288,6 +289,9 @@ fn example_for(canonical: &str, graph_type: Option<&str>) -> &'static str {
288289
"--arcs \"0>2,0>3,1>2,1>3,2>4,2>5,3>4,3>5\" --capacities 1,1,1,1,1,1,1,1 --source-1 0 --sink-1 4 --source-2 1 --sink-2 5 --requirement-1 1 --requirement-2 1"
289290
}
290291
"MinimumFeedbackArcSet" => "--arcs \"0>1,1>2,2>0\"",
292+
"StrongConnectivityAugmentation" => {
293+
"--arcs \"0>1,1>2\" --candidate-arcs \"2>0:1\" --bound 1"
294+
}
291295
"RuralPostman" => {
292296
"--graph 0-1,1-2,2-3,3-0 --edge-weights 1,1,1,1 --required-edges 0,2 --bound 4"
293297
}
@@ -1422,6 +1426,32 @@ pub fn create(args: &CreateArgs, out: &OutputConfig) -> Result<()> {
14221426
)
14231427
}
14241428

1429+
// StrongConnectivityAugmentation
1430+
"StrongConnectivityAugmentation" => {
1431+
let usage = "Usage: pred create StrongConnectivityAugmentation --arcs \"0>1,1>2\" --candidate-arcs \"2>0:1\" --bound 1 [--num-vertices N]";
1432+
let arcs_str = args.arcs.as_deref().ok_or_else(|| {
1433+
anyhow::anyhow!(
1434+
"StrongConnectivityAugmentation requires --arcs\n\n\
1435+
{usage}"
1436+
)
1437+
})?;
1438+
let (graph, _) = parse_directed_graph(arcs_str, args.num_vertices)?;
1439+
let candidate_arcs = parse_candidate_arcs(args, graph.num_vertices())?;
1440+
let bound = args.bound.ok_or_else(|| {
1441+
anyhow::anyhow!(
1442+
"StrongConnectivityAugmentation requires --bound\n\n\
1443+
{usage}"
1444+
)
1445+
})? as i32;
1446+
(
1447+
ser(
1448+
StrongConnectivityAugmentation::try_new(graph, candidate_arcs, bound)
1449+
.map_err(|e| anyhow::anyhow!(e))?,
1450+
)?,
1451+
resolved_variant.clone(),
1452+
)
1453+
}
1454+
14251455
// MinimumSumMulticenter (p-median)
14261456
"MinimumSumMulticenter" => {
14271457
let (graph, n) = parse_graph(args).map_err(|e| {
@@ -2280,6 +2310,51 @@ fn parse_arc_weights(args: &CreateArgs, num_arcs: usize) -> Result<Vec<i32>> {
22802310
}
22812311
}
22822312

2313+
/// Parse `--candidate-arcs` as `u>v:w` entries for StrongConnectivityAugmentation.
2314+
fn parse_candidate_arcs(
2315+
args: &CreateArgs,
2316+
num_vertices: usize,
2317+
) -> Result<Vec<(usize, usize, i32)>> {
2318+
let arcs_str = args.candidate_arcs.as_deref().ok_or_else(|| {
2319+
anyhow::anyhow!(
2320+
"StrongConnectivityAugmentation requires --candidate-arcs (e.g., \"2>0:1,2>1:3\")"
2321+
)
2322+
})?;
2323+
2324+
arcs_str
2325+
.split(',')
2326+
.map(|entry| {
2327+
let entry = entry.trim();
2328+
let (arc_part, weight_part) = entry.split_once(':').ok_or_else(|| {
2329+
anyhow::anyhow!(
2330+
"Invalid candidate arc '{}': expected format u>v:w (e.g., 2>0:1)",
2331+
entry
2332+
)
2333+
})?;
2334+
let parts: Vec<&str> = arc_part.split('>').collect();
2335+
if parts.len() != 2 {
2336+
bail!(
2337+
"Invalid candidate arc '{}': expected format u>v:w (e.g., 2>0:1)",
2338+
entry
2339+
);
2340+
}
2341+
2342+
let u: usize = parts[0].parse()?;
2343+
let v: usize = parts[1].parse()?;
2344+
anyhow::ensure!(
2345+
u < num_vertices && v < num_vertices,
2346+
"candidate arc ({}, {}) references vertex >= num_vertices ({})",
2347+
u,
2348+
v,
2349+
num_vertices
2350+
);
2351+
2352+
let w: i32 = weight_part.parse()?;
2353+
Ok((u, v, w))
2354+
})
2355+
.collect()
2356+
}
2357+
22832358
/// Handle `pred create <PROBLEM> --random ...`
22842359
fn create_random(
22852360
args: &CreateArgs,
@@ -2613,6 +2688,7 @@ mod tests {
26132688
arcs: None,
26142689
potential_edges: None,
26152690
budget: None,
2691+
candidate_arcs: None,
26162692
deadlines: None,
26172693
precedence_pairs: None,
26182694
task_lengths: None,

problemreductions-cli/src/dispatch.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ mod tests {
184184
use problemreductions::models::graph::MaximumIndependentSet;
185185
use problemreductions::models::misc::BinPacking;
186186
use problemreductions::topology::SimpleGraph;
187+
use serde_json::json;
187188

188189
#[test]
189190
fn test_load_problem_alias_uses_registry_dispatch() {
@@ -208,6 +209,29 @@ mod tests {
208209
assert!(loaded.is_err());
209210
}
210211

212+
#[test]
213+
fn test_load_problem_rejects_invalid_strong_connectivity_augmentation_instance() {
214+
let variant = BTreeMap::from([("weight".to_string(), "i32".to_string())]);
215+
let data = json!({
216+
"graph": {
217+
"inner": {
218+
"edge_property": "directed",
219+
"nodes": [null, null, null],
220+
"node_holes": [],
221+
"edges": [[0, 1, null], [1, 2, null]]
222+
}
223+
},
224+
"candidate_arcs": [[0, 3, 1]],
225+
"bound": 1
226+
});
227+
228+
let loaded = load_problem("StrongConnectivityAugmentation", &variant, data);
229+
assert!(loaded.is_err());
230+
let err = loaded.err().unwrap().to_string();
231+
assert!(err.contains("candidate arc"), "err: {err}");
232+
assert!(err.contains("num_vertices"), "err: {err}");
233+
}
234+
211235
#[test]
212236
fn test_serialize_any_problem_round_trips_bin_packing() {
213237
let problem = BinPacking::new(vec![3i32, 3, 2, 2], 5i32);

src/example_db/fixtures/examples.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
{"problem":"ShortestCommonSupersequence","variant":{},"instance":{"alphabet_size":3,"bound":4,"strings":[[0,1,2],[1,0,2]]},"samples":[{"config":[1,0,1,2],"metric":true}],"optimal":[{"config":[0,1,0,2],"metric":true},{"config":[1,0,1,2],"metric":true}]},
4040
{"problem":"SpinGlass","variant":{"graph":"SimpleGraph","weight":"i32"},"instance":{"couplings":[1,1,1,1,1,1,1],"fields":[0,0,0,0,0],"graph":{"inner":{"edge_property":"undirected","edges":[[0,1,null],[1,2,null],[3,4,null],[0,3,null],[1,3,null],[1,4,null],[2,4,null]],"node_holes":[],"nodes":[null,null,null,null,null]}}},"samples":[{"config":[1,0,1,1,0],"metric":{"Valid":-3}}],"optimal":[{"config":[0,0,1,1,0],"metric":{"Valid":-3}},{"config":[0,1,0,0,1],"metric":{"Valid":-3}},{"config":[0,1,0,1,0],"metric":{"Valid":-3}},{"config":[0,1,1,1,0],"metric":{"Valid":-3}},{"config":[1,0,0,0,1],"metric":{"Valid":-3}},{"config":[1,0,1,0,1],"metric":{"Valid":-3}},{"config":[1,0,1,1,0],"metric":{"Valid":-3}},{"config":[1,1,0,0,1],"metric":{"Valid":-3}}]},
4141
{"problem":"SteinerTree","variant":{"graph":"SimpleGraph","weight":"i32"},"instance":{"edge_weights":[2,5,2,1,5,6,1],"graph":{"inner":{"edge_property":"undirected","edges":[[0,1,null],[0,3,null],[1,2,null],[1,3,null],[2,3,null],[2,4,null],[3,4,null]],"node_holes":[],"nodes":[null,null,null,null,null]}},"terminals":[0,2,4]},"samples":[{"config":[1,0,1,1,0,0,1],"metric":{"Valid":6}}],"optimal":[{"config":[1,0,1,1,0,0,1],"metric":{"Valid":6}}]},
42+
{"problem":"StrongConnectivityAugmentation","variant":{"weight":"i32"},"instance":{"bound":1,"candidate_arcs":[[3,0,5],[3,1,3],[3,2,4],[4,0,6],[4,1,2],[4,2,7],[5,0,4],[5,1,3],[5,2,1],[0,3,8],[0,4,3],[0,5,2],[1,3,6],[1,4,4],[1,5,5],[2,4,3],[2,5,7],[1,0,2]],"graph":{"inner":{"edge_property":"directed","edges":[[0,1,null],[1,2,null],[2,0,null],[3,4,null],[4,3,null],[2,3,null],[4,5,null],[5,3,null]],"node_holes":[],"nodes":[null,null,null,null,null,null]}}},"samples":[{"config":[0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0],"metric":true},{"config":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"metric":false}],"optimal":[{"config":[0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0],"metric":true}]},
4243
{"problem":"TravelingSalesman","variant":{"graph":"SimpleGraph","weight":"i32"},"instance":{"edge_weights":[1,3,2,2,3,1],"graph":{"inner":{"edge_property":"undirected","edges":[[0,1,null],[0,2,null],[0,3,null],[1,2,null],[1,3,null],[2,3,null]],"node_holes":[],"nodes":[null,null,null,null]}}},"samples":[{"config":[1,0,1,1,0,1],"metric":{"Valid":6}}],"optimal":[{"config":[1,0,1,1,0,1],"metric":{"Valid":6}}]},
4344
{"problem":"UndirectedTwoCommodityIntegralFlow","variant":{},"instance":{"capacities":[1,1,2],"graph":{"inner":{"edge_property":"undirected","edges":[[0,2,null],[1,2,null],[2,3,null]],"node_holes":[],"nodes":[null,null,null,null]}},"requirement_1":1,"requirement_2":1,"sink_1":3,"sink_2":3,"source_1":0,"source_2":1},"samples":[{"config":[1,0,0,0,0,0,1,0,1,0,1,0],"metric":true}],"optimal":[{"config":[0,0,1,0,1,0,0,0,1,0,1,0],"metric":true},{"config":[1,0,0,0,0,0,1,0,1,0,1,0],"metric":true}]}
4445
],

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub mod prelude {
4848
BicliqueCover, BiconnectivityAugmentation, BoundedComponentSpanningForest,
4949
DirectedTwoCommodityIntegralFlow, GraphPartitioning, HamiltonianPath,
5050
IsomorphicSpanningTree, LengthBoundedDisjointPaths, SpinGlass, SteinerTree,
51-
SubgraphIsomorphism,
51+
StrongConnectivityAugmentation, SubgraphIsomorphism,
5252
};
5353
pub use crate::models::graph::{
5454
KColoring, MaxCut, MaximalIS, MaximumClique, MaximumIndependentSet, MaximumMatching,

src/models/graph/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
//! - [`SubgraphIsomorphism`]: Subgraph isomorphism (decision problem)
3131
//! - [`DirectedTwoCommodityIntegralFlow`]: Directed two-commodity integral flow (satisfaction)
3232
//! - [`UndirectedTwoCommodityIntegralFlow`]: Two-commodity integral flow on undirected graphs
33+
//! - [`StrongConnectivityAugmentation`]: Strong connectivity augmentation with weighted candidate arcs
3334
3435
pub(crate) mod biclique_cover;
3536
pub(crate) mod biconnectivity_augmentation;
@@ -57,6 +58,7 @@ pub(crate) mod partition_into_triangles;
5758
pub(crate) mod rural_postman;
5859
pub(crate) mod spin_glass;
5960
pub(crate) mod steiner_tree;
61+
pub(crate) mod strong_connectivity_augmentation;
6062
pub(crate) mod subgraph_isomorphism;
6163
pub(crate) mod traveling_salesman;
6264
pub(crate) mod undirected_two_commodity_integral_flow;
@@ -87,6 +89,7 @@ pub use partition_into_triangles::PartitionIntoTriangles;
8789
pub use rural_postman::RuralPostman;
8890
pub use spin_glass::SpinGlass;
8991
pub use steiner_tree::SteinerTree;
92+
pub use strong_connectivity_augmentation::StrongConnectivityAugmentation;
9093
pub use subgraph_isomorphism::SubgraphIsomorphism;
9194
pub use traveling_salesman::TravelingSalesman;
9295
pub use undirected_two_commodity_integral_flow::UndirectedTwoCommodityIntegralFlow;
@@ -118,5 +121,6 @@ pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::M
118121
specs.extend(steiner_tree::canonical_model_example_specs());
119122
specs.extend(directed_two_commodity_integral_flow::canonical_model_example_specs());
120123
specs.extend(undirected_two_commodity_integral_flow::canonical_model_example_specs());
124+
specs.extend(strong_connectivity_augmentation::canonical_model_example_specs());
121125
specs
122126
}

0 commit comments

Comments
 (0)