use ferrotherm::host::Timing;
use ferrotherm::rng::Pcg;
use ferrotherm::{bound, gset::Instance, schedule::Schedule, sdp, tempering};
fn builtin() -> String {
let (w, h) = (8usize, 8);
let mut rng = Pcg::new(0x6_5E7, 0xC0DE);
let mut edges = Vec::new();
for y in 0..h {
for x in 0..w {
let a = y * w + x + 1; let right = y * w + (x + 1) % w + 1;
let down = ((y + 1) % h) * w + x + 1;
for b in [right, down] {
if a != b {
edges.push((a, b, if rng.f64() < 0.5 { -1i32 } else { 1 }));
}
}
}
}
let mut s = format!("{} {}\n", w * h, edges.len());
for (a, b, j) in edges {
s.push_str(&format!("{a} {b} {j}\n"));
}
s
}
fn main() {
let mut args = std::env::args().skip(1);
let path = args.next();
let best_known: Option<f64> = args.next().and_then(|v| v.parse().ok());
let (path, text) = match &path {
Some(p) => match std::fs::read_to_string(p) {
Ok(t) => (p.clone(), t),
Err(e) => {
eprintln!("cannot read {p}: {e}");
std::process::exit(2);
}
},
None => {
println!(
"no file given, so this is the BUILT-IN 8x8 torus. It has no published best-known \n\
cut, and the numbers below are not comparable to anything. For the real thing:\n\
\n cargo run --release --example gset_gap -- <G-set file> [best-known-cut]\n\
\nG-set lives at https://web.stanford.edu/~yyye/yyye/Gset/\n"
);
("builtin-8x8-torus".to_string(), builtin())
}
};
let inst = match Instance::parse(&text) {
Ok(i) => i,
Err(e) => {
eprintln!("{path}: {e}");
std::process::exit(2);
}
};
let name = path.rsplit('/').next().unwrap_or(&path);
let degree = 2.0 * inst.edges as f64 / inst.nodes as f64;
println!("{name}: {} nodes, {} edges, mean degree {degree:.1}, W = {}",
inst.nodes, inst.edges, inst.total_weight);
let ladder = Schedule::geometric(0.05, 6.0, 200, 120);
let mut best = f64::NEG_INFINITY;
let mut best_state = Vec::new();
let (_, t_sample) = Timing::around(|| {
for seed in 0..8u64 {
let (s, _) = tempering::anneal_scheduled(&inst.graph, &ladder, seed, None);
let c = inst.cut(&s);
if c > best {
best = c;
best_state = s;
}
}
});
let verified = inst.cut(&best_state);
assert!((verified - best).abs() < 1e-9, "reported {best}, state gives {verified}");
println!(" cut found {best:>12.0} (8 restarts x 200-stage ladder, {t_sample})");
let sweeps = std::env::var("FERROTHERM_SDP_SWEEPS")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(sdp::Params::default().sweeps);
let params = sdp::Params { sweeps, ..sdp::Params::default() };
let ((f, c, sd, cert), t_bound) = Timing::around(|| {
let f = bound::forest(&inst.graph, 40);
let c = bound::odd_cycle(&inst.graph, 6);
let (sd, cert) = sdp::certified(&inst.graph, ¶ms, 1);
(f, c, sd, cert)
});
let sdp_ok = cert.verify(&inst.graph);
println!(" forest ub {:>12.0} ({} forests, peak at round {} of {})",
inst.cut_upper_bound(f.value), f.parts, f.best_round, f.rounds);
println!(" odd-cycle ub {:>12.0} ({} edge-disjoint frustrated cycles)",
inst.cut_upper_bound(c.value), c.parts);
match &sdp_ok {
Ok(v) => println!(" sdp ub {:>12.0} (rank {}, {sweeps} sweeps, re-verified independently)",
inst.cut_upper_bound(*v), cert.rank),
Err(e) => println!(" sdp ub -- REFUSED: {e}"),
}
let mut best_bound = &f;
let mut which = "forest";
if c.value >= best_bound.value { best_bound = &c; which = "odd-cycle"; }
if sdp_ok.is_ok() && sd.value >= best_bound.value { best_bound = &sd; which = "sdp"; }
let ub = inst.cut_upper_bound(best_bound.value);
println!(" upper bound {ub:>12.0} ({which}, {t_bound})");
let gap = (ub - best) / ub * 100.0;
println!(" gap {gap:>11.1}% of the upper bound");
for c in [t_sample.caveat(), t_bound.caveat()].into_iter().flatten().take(1) {
println!("\n NOTE ON THE TIMINGS: {c}");
}
if best > ub + 1e-6 {
eprintln!(
"\n ** UNSOUND: this run FOUND a cut of {best:.0}, and the upper bound is {ub:.0}. A \
cut that has been achieved cannot exceed a bound on the maximum. **"
);
std::process::exit(1);
}
if let Some(bk) = best_known {
let pct = best / bk * 100.0;
println!(" best known {bk:>12.0} -- this run reached {pct:.2}% of it");
if bk > ub + 1e-6 {
eprintln!(
"\n ** the published best-known cut {bk} EXCEEDS this upper bound {ub:.0}. A cut \
that has actually been achieved cannot be above a valid upper bound, so the bound \
is unsound. **"
);
std::process::exit(1);
}
}
}