-
Notifications
You must be signed in to change notification settings - Fork 175
Open
Description
Hi. I'm having fun with egg.
I encountered an infeasible error with CBC that prevents LpExtractor from processing cyclic e-graphs.
use egg::*;
fn main() {
env_logger::init();
let mut egraph = EGraph::<SymbolLang, ()>::default();
let g = egraph.add_expr(&"(g v x)".parse().unwrap());
let v = egraph.add_expr(&"v".parse().unwrap());
let f = egraph.add(SymbolLang::new("f", vec![v, g]));
let top = egraph.add(SymbolLang::new("list", vec![f, g]));
egraph.rebuild();
let runner = Runner::default()
.with_iter_limit(100)
.with_node_limit(10_000)
.with_egraph(egraph)
.run(&vec![rewrite!("f"; "(f ?v (g ?v ?a))" => "?a")]);
runner.egraph.dot().to_dot("tmp.dot").unwrap();
LpExtractor::new(&runner.egraph, AstSize).solve(top);
}
Log produced:
[INFO egg::egraph] REBUILT! in 0.000s
Old: hc size 5, eclasses: 5
New: hc size 5, eclasses: 5
unions: 0, trimmed nodes: 0
[INFO egg::egraph] REBUILT! in 0.000s
Old: hc size 5, eclasses: 5
New: hc size 5, eclasses: 5
unions: 0, trimmed nodes: 0
[INFO egg::run]
Iteration 0
[INFO egg::run] Search time: 0.000048125
[INFO egg::run] Apply time: 0.000038416
[INFO egg::egraph] REBUILT! in 0.000s
Old: hc size 5, eclasses: 4
New: hc size 6, eclasses: 4
unions: 0, trimmed nodes: 0
[INFO egg::run] Rebuild time: 0.000590618
[INFO egg::run] Size: n=6, e=4
[INFO egg::run]
Iteration 1
[INFO egg::run] Search time: 0.000033264
[INFO egg::run] Apply time: 0.000013224
[INFO egg::egraph] REBUILT! in 0.000s
Old: hc size 6, eclasses: 4
New: hc size 6, eclasses: 4
unions: 0, trimmed nodes: 0
[INFO egg::run] Rebuild time: 0.000198928
[INFO egg::run] Size: n=6, e=4
[INFO egg::run] Stopping: Saturated
[/home/user/.cargo/registry/src/github.com-1ecc6299db9ec823/egg-0.9.0/src/lp_extract.rs:138] max_order = 50.0
Welcome to the CBC MILP Solver
Version: 2.10
Build Date: Jun 30 2022
command line - Cbc_C_Interface -solve -quit (default strategy 1)
Problem is infeasible - 0.00 seconds
Total time (CPU seconds): 0.01 (Wallclock seconds): 0.00
[INFO egg::lp_extract] CBC status Finished, LinearRelaxationInfeasible
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', /home/user/.cargo/registry/src/github.com-1ecc6299db9ec823/egg-0.9.0/src/lp_extract.rs:192:80
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
This find_cycles
extracts g
as a result. But I might need f
instead.
https://github.com/egraphs-good/egg/blob/main/src/lp_extract.rs#L209
For the moment, I'm avoiding the error with code like is_dag
. Perhaps, it works for others.
fn find_cycles<L, N>(egraph: &EGraph<L, N>, mut f: impl FnMut(Id, usize))
where
L: Language,
N: Analysis<L>,
{
for class in egraph.classes() {
let id = class.id;
for (i, node) in egraph[id].iter().enumerate() {
for &child in node.children() {
if usize::from(child) >= usize::from(id) {
f(id, i);
break;
}
}
}
}
}
(Could be related: #196 )
Activity
[-]LpExtractor creates an infeasible solution with CBC[/-][+]LpExtractor creates an infeasible problem with CBC[/+][-]LpExtractor creates an infeasible problem with CBC[/-][+]LpExtractor creates an infeasible problem for CBC[/+]khaki3 commentedon Oct 2, 2022
The code above does not support additional nodes. I ended up with this unpolished one:
mwillsey commentedon Oct 3, 2022
Great to hear! Thanks for providing a workaround that seems to work here. I'm not quite sure I fully understand what is deficient in the existing find_cycles. Do you?
khaki3 commentedon Oct 3, 2022
The current one starts the search from the top of
classes()
and preserves them in its order. For allowing my case, it would need to construct a RecExpr-like order. Expressions fed into an egraph originally have such orders and recovering them is partially possible while some of them become cycles during rewrites (e.g.f
is unioned withx
in my example) but always have acyclic nodes in the same eclass so can be ignored for the extraction.khaki3 commentedon Oct 5, 2022
My code is missing some optimal cases. The first one could be supported by checking actual cycles (not existing in this case so no remove) after constructing a RecExpr-like order. But the second one having interdependent cycles would need a cost function to choose but it's an expensive search.
mwillsey commentedon Oct 5, 2022
Hmm, this seems tricky indeed. @Bastacyclop do you think this approach could solve #196?
Bastacyclop commentedon Oct 6, 2022
Hi @mwillsey , I'll investigate next week!
try fix from egraphs-good#207
Bastacyclop commentedon Oct 12, 2022
Using @khaki3 's
find_cycles
on one of my problematic benchmark fixes the issue (at least a solution is found). I'll use this change in more benchmarks and report if it holds up.Bastacyclop commentedon Apr 11, 2023
Using this solution on a broader set of benchmarks produced mitigated results for us: extraction times are increased (leading to timeouts), and extracted solutions are not always better. I'm currently attempting to implement an alternative solution, if you have ideas on benchmarks to evaluate on.
CbcPruneExtractor
extraction-gym#7