1use crate::termdag::{TermDag, TermId};
16use crate::util::{HashMap, HashSet};
17use crate::*;
18use std::collections::VecDeque;
19
20pub trait Cost: Clone + Ord {}
22
23impl<T: Clone + Ord> Cost for T {}
24
25pub trait MonoidCost: Cost {
31 fn identity() -> Self;
33
34 fn combine(self, other: &Self) -> Self;
36}
37
38macro_rules! monoid_unsigned_cost {
39 ($($cost:ty),* $(,)?) => {$(
40 impl MonoidCost for $cost {
41 fn identity() -> Self {
42 0
43 }
44
45 fn combine(self, other: &Self) -> Self {
46 self.saturating_add(*other)
47 }
48 }
49 )*};
50}
51
52monoid_unsigned_cost!(u8, u16, u32, u64, u128, usize);
53
54macro_rules! monoid_exact_cost {
55 ($($cost:ty),* $(,)?) => {$(
56 impl MonoidCost for $cost {
57 fn identity() -> Self {
58 use num::Zero;
59 Self::zero()
60 }
61
62 fn combine(self, other: &Self) -> Self {
63 self + other
64 }
65 }
66 )*};
67}
68
69monoid_exact_cost!(num::BigInt, num::BigRational);
70
71pub trait DagCostModel<C: MonoidCost> {
77 fn base_value_cost(&self, egraph: &EGraph, sort: &ArcSort, value: Value) -> C;
79 fn enode_cost(&self, egraph: &EGraph, func: &Function, enode: &Enode<'_>) -> C;
81
82 fn container_cost(&self, egraph: &EGraph, sort: &ArcSort, value: Value) -> C {
84 let _ = (egraph, sort, value);
85 C::identity()
86 }
87}
88
89pub trait TreeCostModel<C: Cost> {
98 type EnodeCost;
100 type ContainerCost;
102
103 fn base_value_cost(&self, egraph: &EGraph, sort: &ArcSort, value: Value) -> C;
105
106 fn enode_cost(&self, egraph: &EGraph, func: &Function, enode: &Enode<'_>) -> Self::EnodeCost;
108
109 fn container_cost(&self, egraph: &EGraph, sort: &ArcSort, value: Value) -> Self::ContainerCost;
111
112 fn fold_enode_cost(&self, enode_cost: Self::EnodeCost, child_costs: &[C]) -> C;
114
115 fn fold_container_cost(&self, container_cost: Self::ContainerCost, element_costs: &[C]) -> C;
117}
118
119trait TreeExtractorCostModel<C: Cost> {
120 fn base_value_cost(&self, egraph: &EGraph, sort: &ArcSort, value: Value) -> C;
121 fn total_enode_cost(
122 &self,
123 egraph: &EGraph,
124 func: &Function,
125 enode: &Enode<'_>,
126 child_costs: &[C],
127 ) -> C;
128 fn total_container_cost(
129 &self,
130 egraph: &EGraph,
131 sort: &ArcSort,
132 value: Value,
133 element_costs: &[C],
134 ) -> C;
135}
136
137impl<C: Cost, M: TreeCostModel<C>> TreeExtractorCostModel<C> for M {
138 fn base_value_cost(&self, egraph: &EGraph, sort: &ArcSort, value: Value) -> C {
139 TreeCostModel::base_value_cost(self, egraph, sort, value)
140 }
141
142 fn total_enode_cost(
143 &self,
144 egraph: &EGraph,
145 func: &Function,
146 enode: &Enode<'_>,
147 child_costs: &[C],
148 ) -> C {
149 self.fold_enode_cost(self.enode_cost(egraph, func, enode), child_costs)
150 }
151
152 fn total_container_cost(
153 &self,
154 egraph: &EGraph,
155 sort: &ArcSort,
156 value: Value,
157 element_costs: &[C],
158 ) -> C {
159 self.fold_container_cost(self.container_cost(egraph, sort, value), element_costs)
160 }
161}
162
163#[derive(Clone, Debug)]
165pub struct TreeCostModelFromDag<M>(pub M);
166
167impl<C: MonoidCost, M: DagCostModel<C>> TreeCostModel<C> for TreeCostModelFromDag<M> {
168 type EnodeCost = C;
169 type ContainerCost = C;
170
171 fn base_value_cost(&self, egraph: &EGraph, sort: &ArcSort, value: Value) -> C {
172 self.0.base_value_cost(egraph, sort, value)
173 }
174
175 fn enode_cost(&self, egraph: &EGraph, func: &Function, enode: &Enode<'_>) -> C {
176 self.0.enode_cost(egraph, func, enode)
177 }
178
179 fn container_cost(&self, egraph: &EGraph, sort: &ArcSort, value: Value) -> C {
180 self.0.container_cost(egraph, sort, value)
181 }
182
183 fn fold_enode_cost(&self, enode_cost: C, child_costs: &[C]) -> C {
184 child_costs
185 .iter()
186 .fold(enode_cost, |cost, child| cost.combine(child))
187 }
188
189 fn fold_container_cost(&self, container_cost: C, element_costs: &[C]) -> C {
190 element_costs
191 .iter()
192 .fold(container_cost, |cost, element| cost.combine(element))
193 }
194}
195
196pub type DefaultCost = u64;
198
199#[derive(Clone, Debug)]
204pub struct AdditiveCostModel {
205 pub node_cost: DefaultCost,
207}
208
209impl Default for AdditiveCostModel {
210 fn default() -> Self {
211 Self { node_cost: 1 }
212 }
213}
214
215pub const DEFAULT_COST_MODEL: TreeCostModelFromDag<AdditiveCostModel> =
217 TreeCostModelFromDag(AdditiveCostModel { node_cost: 1 });
218
219impl DagCostModel<DefaultCost> for AdditiveCostModel {
220 fn base_value_cost(&self, _egraph: &EGraph, _sort: &ArcSort, _value: Value) -> DefaultCost {
221 self.node_cost
222 }
223
224 fn enode_cost(&self, egraph: &EGraph, func: &Function, _enode: &Enode<'_>) -> DefaultCost {
225 func.extraction_head_cost(egraph).unwrap_or(self.node_cost)
226 }
227}
228
229#[derive(Clone, Debug)]
236pub struct ExtractedTerm<C> {
237 pub cost: C,
239 pub term: TermId,
241}
242
243#[derive(Clone, Debug)]
247pub struct ExtractedTerms<C> {
248 pub termdag: TermDag,
250 pub terms: Vec<Option<ExtractedTerm<C>>>,
255}
256
257#[derive(Clone, Debug)]
261pub struct ExtractedTermVariants<C> {
262 pub termdag: TermDag,
264 pub variants: Vec<Vec<ExtractedTerm<C>>>,
266}
267
268pub struct TreeExtractor<'g, C: Cost> {
274 egraph: &'g EGraph,
275 funcs: Vec<String>,
276 cost_model: Box<dyn TreeExtractorCostModel<C> + 'g>,
277 costs: HashMap<String, HashMap<Value, C>>,
278 topo_rnk_cnt: usize,
279 topo_rnk: HashMap<String, HashMap<Value, usize>>,
280 parent_edge: HashMap<String, HashMap<Value, (String, Vec<Value>)>>,
281}
282
283impl<'g, C: Cost> TreeExtractor<'g, C> {
284 pub fn compute_costs_from_rootsorts(
295 rootsorts: Option<Vec<ArcSort>>,
296 egraph: &'g EGraph,
297 cost_model: impl TreeCostModel<C> + 'g,
298 ) -> Self {
299 let extract_all_sorts = rootsorts.is_none();
301
302 let mut rootsorts = rootsorts.unwrap_or_default();
303
304 let mut rev_index: HashMap<String, Vec<String>> = Default::default();
308 for func in egraph.functions.iter() {
309 let unextractable = func.1.decl.unextractable;
310 let hidden = func.1.decl.internal_hidden;
311
312 if !unextractable
316 && !hidden
317 && (func.1.decl.subtype == FunctionSubtype::Constructor
318 || func.1.decl.term_constructor.is_some())
319 {
320 let func_name = func.0.clone();
321 let output_sort_name = func.1.extraction_output_sort().name();
323 if let Some(v) = rev_index.get_mut(output_sort_name) {
324 v.push(func_name);
325 } else {
326 rev_index.insert(output_sort_name.to_owned(), vec![func_name]);
327 if extract_all_sorts {
328 rootsorts.push(func.1.extraction_output_sort().clone());
329 }
330 }
331 }
332 }
333
334 let mut q: VecDeque<ArcSort> = VecDeque::new();
336 let mut seen: HashSet<String> = Default::default();
337 for rootsort in rootsorts.iter() {
338 if seen.insert(rootsort.name().to_owned()) {
339 q.push_back(rootsort.clone());
340 }
341 }
342
343 let mut funcs_set: HashSet<String> = Default::default();
344 let mut funcs: Vec<String> = Vec::new();
345 while !q.is_empty() {
346 let sort = q.pop_front().unwrap();
347 if sort.is_container_sort() {
348 let inner_sorts = sort.inner_sorts();
349 for s in inner_sorts {
350 if !seen.contains(s.name()) {
351 q.push_back(s.clone());
352 seen.insert(s.name().to_owned());
353 }
354 }
355 } else if sort.is_eq_sort()
356 && let Some(head_symbols) = rev_index.get(sort.name())
357 {
358 for h in head_symbols {
359 if !funcs_set.contains(h) {
360 let func = egraph.functions.get(h).unwrap();
361 let num_children = func.extraction_num_children();
363 for ch in func.func_type.input.iter().take(num_children) {
364 let ch_name = ch.name();
365 if !seen.contains(ch_name) {
366 q.push_back(ch.clone());
367 seen.insert(ch_name.to_owned());
368 }
369 }
370 funcs_set.insert(h.clone());
371 funcs.push(h.clone());
372 }
373 }
374 }
375 }
376
377 let mut costs: HashMap<String, HashMap<Value, C>> = Default::default();
379 let mut topo_rnk: HashMap<String, HashMap<Value, usize>> = Default::default();
380 let mut parent_edge: HashMap<String, HashMap<Value, (String, Vec<Value>)>> =
381 Default::default();
382
383 for func_name in funcs.iter() {
384 let func = egraph.functions.get(func_name).unwrap();
385 let output_sort_name = func.extraction_output_sort().name();
386 if !costs.contains_key(output_sort_name) {
387 costs.insert(output_sort_name.to_owned(), Default::default());
388 topo_rnk.insert(output_sort_name.to_owned(), Default::default());
389 parent_edge.insert(output_sort_name.to_owned(), Default::default());
390 }
391 }
392
393 let mut extractor = TreeExtractor {
394 egraph,
395 funcs,
396 cost_model: Box::new(cost_model),
397 costs,
398 topo_rnk_cnt: 0,
399 topo_rnk,
400 parent_edge,
401 };
402
403 extractor.bellman_ford(egraph);
404
405 extractor
406 }
407
408 fn compute_cost_node(&self, egraph: &EGraph, value: Value, sort: &ArcSort) -> Option<C> {
412 if sort.is_container_sort() {
413 let elements = sort.inner_values(egraph.backend.container_values(), value);
414 let mut ch_costs: Vec<C> = Vec::new();
415 for ch in elements.iter() {
416 ch_costs.push(self.compute_cost_node(egraph, ch.1, &ch.0)?);
417 }
418 Some(
419 self.cost_model
420 .total_container_cost(egraph, sort, value, &ch_costs),
421 )
422 } else if sort.is_eq_sort() {
423 self.costs.get(sort.name())?.get(&value).cloned()
424 } else {
425 Some(self.cost_model.base_value_cost(egraph, sort, value))
427 }
428 }
429
430 fn compute_cost_hyperedge(
432 &self,
433 egraph: &EGraph,
434 row: &egglog_bridge::ScanEntry,
435 func: &Function,
436 ) -> Option<C> {
437 let mut ch_costs: Vec<C> = Vec::new();
438 let sorts = &func.func_type.input;
439 let num_children = func.extraction_num_children();
440 for (value, sort) in row.vals.iter().take(num_children).zip(sorts.iter()) {
441 ch_costs.push(self.compute_cost_node(egraph, *value, sort)?);
442 }
443 let head_name = func.extraction_term_name();
444 let output_idx = func.extraction_output_index();
445 let enode = Enode {
446 name: head_name,
447 children: &row.vals[..output_idx],
448 eclass: row.vals[output_idx],
449 subsumed: row.subsumed,
450 };
451 let cost_func = func
452 .decl
453 .term_constructor
454 .as_ref()
455 .and_then(|name| egraph.functions.get(name))
456 .unwrap_or(func);
457 Some(
458 self.cost_model
459 .total_enode_cost(egraph, cost_func, &enode, &ch_costs),
460 )
461 }
462
463 fn compute_topo_rnk_node(&self, egraph: &EGraph, value: Value, sort: &ArcSort) -> usize {
464 if sort.is_container_sort() {
465 sort.inner_values(egraph.backend.container_values(), value)
466 .iter()
467 .fold(0, |ret, (sort, value)| {
468 usize::max(ret, self.compute_topo_rnk_node(egraph, *value, sort))
469 })
470 } else if sort.is_eq_sort() {
471 if let Some(t) = self.topo_rnk.get(sort.name()) {
472 *t.get(&value).unwrap_or(&usize::MAX)
473 } else {
474 usize::MAX
475 }
476 } else {
477 0
478 }
479 }
480
481 fn compute_topo_rnk_hyperedge(
482 &self,
483 egraph: &EGraph,
484 row: &egglog_bridge::ScanEntry,
485 func: &Function,
486 ) -> usize {
487 let sorts = &func.func_type.input;
488 let num_children = func.extraction_num_children();
489 row.vals
490 .iter()
491 .take(num_children)
492 .zip(sorts.iter())
493 .fold(0, |ret, (value, sort)| {
494 usize::max(ret, self.compute_topo_rnk_node(egraph, *value, sort))
495 })
496 }
497
498 fn bellman_ford(&mut self, egraph: &EGraph) {
509 let mut ensure_fixpoint = false;
510
511 let funcs = self.funcs.clone();
512
513 while !ensure_fixpoint {
514 ensure_fixpoint = true;
515
516 for func_name in funcs.iter() {
517 let func = egraph.functions.get(func_name).unwrap();
518 let target_sort = func.extraction_output_sort();
519
520 let output_idx = func.extraction_output_index();
521 let relax_hyperedge = |row: egglog_bridge::ScanEntry| {
522 if !row.subsumed {
523 let target = &row.vals[output_idx];
524 let mut updated = false;
525 if let Some(new_cost) = self.compute_cost_hyperedge(egraph, &row, func) {
526 match self
527 .costs
528 .get_mut(target_sort.name())
529 .unwrap()
530 .entry(*target)
531 {
532 HEntry::Vacant(e) => {
533 updated = true;
534 e.insert(new_cost);
535 }
536 HEntry::Occupied(mut e) => {
537 if new_cost < *(e.get()) {
538 updated = true;
539 e.insert(new_cost);
540 }
541 }
542 }
543 }
544 if updated {
548 ensure_fixpoint = false;
549 self.topo_rnk_cnt += 1;
550 self.topo_rnk
551 .get_mut(target_sort.name())
552 .unwrap()
553 .insert(*target, self.topo_rnk_cnt);
554 }
555 }
556 };
557
558 egraph.backend.for_each(func.backend_id, relax_hyperedge);
559 }
560 }
561
562 for func_name in funcs.iter() {
564 let func = egraph.functions.get(func_name).unwrap();
565 let target_sort = func.extraction_output_sort();
566 let output_idx = func.extraction_output_index();
567
568 let save_best_parent_edge = |row: egglog_bridge::ScanEntry| {
569 if !row.subsumed {
570 let target = &row.vals[output_idx];
571 if let Some(best_cost) = self.costs.get(target_sort.name()).unwrap().get(target)
572 && Some(best_cost.clone())
573 == self.compute_cost_hyperedge(egraph, &row, func)
574 {
575 let target_topo_rnk = *self
577 .topo_rnk
578 .get(target_sort.name())
579 .unwrap()
580 .get(target)
581 .unwrap();
582 if target_topo_rnk > self.compute_topo_rnk_hyperedge(egraph, &row, func) {
583 if let HEntry::Vacant(e) = self
585 .parent_edge
586 .get_mut(target_sort.name())
587 .unwrap()
588 .entry(*target)
589 {
590 e.insert((func.decl.name.clone(), row.vals.to_vec()));
591 }
592 }
593 }
594 }
595 };
596
597 egraph
598 .backend
599 .for_each(func.backend_id, save_best_parent_edge);
600 }
601 }
602
603 fn reconstruct_termdag_node_helper(
605 &self,
606 egraph: &EGraph,
607 termdag: &mut TermDag,
608 value: Value,
609 sort: &ArcSort,
610 cache: &mut HashMap<(Value, String), TermId>,
611 ) -> TermId {
612 let key = (value, sort.name().to_owned());
613 if let Some(term) = cache.get(&key) {
614 return *term;
615 }
616
617 let term = if sort.is_container_sort() {
618 let elements = sort.inner_values(egraph.backend.container_values(), value);
619 let mut ch_terms: Vec<TermId> = Vec::new();
620 for ch in elements.iter() {
621 ch_terms.push(
622 self.reconstruct_termdag_node_helper(egraph, termdag, ch.1, &ch.0, cache),
623 );
624 }
625 sort.reconstruct_termdag_container(
626 egraph.backend.container_values(),
627 value,
628 termdag,
629 ch_terms,
630 )
631 } else if sort.is_eq_sort() {
632 let (func_name, hyperedge) = self
633 .parent_edge
634 .get(sort.name())
635 .unwrap()
636 .get(&value)
637 .unwrap();
638 let func = egraph.functions.get(func_name).unwrap();
639 let ch_sorts = &func.func_type.input;
640
641 let num_children = func.extraction_num_children();
642 let output_name = func.extraction_term_name();
643
644 let mut ch_terms: Vec<TermId> = Vec::new();
645 for (value, sort) in hyperedge.iter().take(num_children).zip(ch_sorts.iter()) {
646 ch_terms.push(
647 self.reconstruct_termdag_node_helper(egraph, termdag, *value, sort, cache),
648 );
649 }
650 termdag.app(output_name.to_string(), ch_terms)
651 } else {
652 sort.reconstruct_termdag_base(egraph.backend.base_values(), value, termdag)
654 };
655
656 cache.insert(key, term);
657 term
658 }
659
660 pub fn extract_best_with_sort(
665 &self,
666 termdag: &mut TermDag,
667 value: Value,
668 sort: ArcSort,
669 ) -> Option<ExtractedTerm<C>> {
670 self.extract_best_with_sort_cached(termdag, &mut Default::default(), value, sort)
671 }
672
673 fn extract_best_with_sort_cached(
674 &self,
675 termdag: &mut TermDag,
676 cache: &mut HashMap<(Value, String), TermId>,
677 value: Value,
678 sort: ArcSort,
679 ) -> Option<ExtractedTerm<C>> {
680 let egraph = self.egraph;
681 let canonical_value = self.find_canonical(egraph, value, &sort);
683
684 let best_cost = self.compute_cost_node(egraph, canonical_value, &sort)?;
685 let term =
686 self.reconstruct_termdag_node_helper(egraph, termdag, canonical_value, &sort, cache);
687
688 Some(ExtractedTerm {
689 cost: best_cost,
690 term,
691 })
692 }
693
694 fn find_canonical(&self, egraph: &EGraph, value: Value, sort: &ArcSort) -> Value {
698 let Some(uf_name) = egraph.proof_state.uf_parent.get(sort.name()) else {
700 return value;
701 };
702
703 let Some(uf_func) = egraph.functions.get(uf_name) else {
705 return value;
706 };
707
708 let mut canonical = value;
710 egraph
711 .backend
712 .for_each(uf_func.backend_id, |row: egglog_bridge::ScanEntry| {
713 if row.vals[0] == value {
715 canonical = row.vals[1];
716 }
717 });
718
719 canonical
720 }
721
722 pub fn extract_variants_with_sort(
727 &self,
728 termdag: &mut TermDag,
729 value: Value,
730 nvariants: usize,
731 sort: ArcSort,
732 ) -> Vec<ExtractedTerm<C>> {
733 if nvariants == 0 {
734 return vec![];
735 }
736
737 let egraph = self.egraph;
738 if sort.is_eq_sort() {
739 let canonical_value = self.find_canonical(egraph, value, &sort);
741
742 let mut root_variants: Vec<(C, String, Vec<Value>)> = Vec::new();
743
744 for func_name in self.funcs.iter().filter(|func_name| {
745 sort.name()
747 == egraph
748 .functions
749 .get(*func_name)
750 .unwrap()
751 .extraction_output_sort()
752 .name()
753 }) {
754 let func = egraph.functions.get(func_name).unwrap();
755 let output_idx = func.extraction_output_index();
756
757 let find_root_variants = |row: egglog_bridge::ScanEntry| {
758 if !row.subsumed {
759 let target = &row.vals[output_idx];
760 if *target == canonical_value
765 && let Some(cost) = self.compute_cost_hyperedge(egraph, &row, func)
766 {
767 root_variants.push((cost, func_name.clone(), row.vals.to_vec()));
768 }
769 }
770 };
771
772 egraph.backend.for_each(func.backend_id, find_root_variants);
773 }
774
775 let mut res: Vec<ExtractedTerm<C>> = Vec::new();
776 let mut cache: HashMap<(Value, String), TermId> = Default::default();
777 root_variants.sort();
778 root_variants.truncate(nvariants);
779 for (cost, func_name, hyperedge) in root_variants {
780 let mut ch_terms: Vec<TermId> = Vec::new();
781 let func = egraph.functions.get(&func_name).unwrap();
782 let ch_sorts = &func.func_type.input;
783 let num_children = func.extraction_num_children();
784 for (value, sort) in hyperedge.iter().zip(ch_sorts.iter()).take(num_children) {
786 ch_terms.push(self.reconstruct_termdag_node_helper(
787 egraph, termdag, *value, sort, &mut cache,
788 ));
789 }
790 res.push(ExtractedTerm {
792 cost,
793 term: termdag.app(func.extraction_term_name().to_string(), ch_terms),
794 });
795 }
796
797 res
798 } else {
799 log::warn!(
800 "extracting multiple variants for containers or primitives is not implemented, returning a single variant."
801 );
802 if let Some(res) = self.extract_best_with_sort(termdag, value, sort) {
803 vec![res]
804 } else {
805 vec![]
806 }
807 }
808 }
809}
810
811impl Function {
812 pub(crate) fn extraction_head_cost(&self, egraph: &EGraph) -> Option<DefaultCost> {
816 if let Some(term_constructor) = &self.decl.term_constructor {
817 egraph
818 .functions
819 .get(term_constructor)
820 .and_then(|func| func.decl.cost)
821 } else {
822 self.decl.cost
823 }
824 }
825
826 pub(crate) fn extraction_output_sort(&self) -> &ArcSort {
830 if self.decl.term_constructor.is_some() {
831 self.func_type.input.last().unwrap()
832 } else {
833 &self.func_type.output
834 }
835 }
836
837 pub(crate) fn extraction_num_children(&self) -> usize {
840 if self.decl.term_constructor.is_some() {
841 self.func_type.input.len() - 1
842 } else {
843 self.func_type.input.len()
844 }
845 }
846
847 pub(crate) fn extraction_term_name(&self) -> &str {
850 self.decl
851 .term_constructor
852 .as_ref()
853 .unwrap_or(&self.decl.name)
854 }
855
856 pub(crate) fn extraction_output_index(&self) -> usize {
860 if self.decl.term_constructor.is_some() {
861 self.func_type.input.len() - 1
865 } else {
866 self.func_type.input.len()
868 }
869 }
870}
871
872impl EGraph {
873 pub fn extract_best(
876 &self,
877 roots: Vec<(ArcSort, Value)>,
878 ) -> Result<ExtractedTerms<DefaultCost>, Error> {
879 self.extract_best_with_cost_model(roots, DEFAULT_COST_MODEL)
880 }
881
882 pub fn extract_best_with_cost_model<C: Cost, M: TreeCostModel<C>>(
888 &self,
889 roots: Vec<(ArcSort, Value)>,
890 cost_model: M,
891 ) -> Result<ExtractedTerms<C>, Error> {
892 let rootsorts = roots.iter().map(|(sort, _)| sort.clone()).collect();
893 let extractor =
894 TreeExtractor::compute_costs_from_rootsorts(Some(rootsorts), self, cost_model);
895 let mut termdag = TermDag::default();
896 let mut cache = Default::default();
897 let extracted_roots = roots
898 .into_iter()
899 .map(|(sort, value)| {
900 extractor.extract_best_with_sort_cached(&mut termdag, &mut cache, value, sort)
901 })
902 .collect();
903
904 Ok(ExtractedTerms {
905 termdag,
906 terms: extracted_roots,
907 })
908 }
909
910 pub fn extract_variants(
913 &self,
914 roots: Vec<(ArcSort, Value)>,
915 nvariants: usize,
916 ) -> Result<ExtractedTermVariants<DefaultCost>, Error> {
917 self.extract_variants_with_cost_model(roots, nvariants, DEFAULT_COST_MODEL)
918 }
919
920 pub fn extract_variants_with_cost_model<C: Cost, M: TreeCostModel<C>>(
925 &self,
926 roots: Vec<(ArcSort, Value)>,
927 nvariants: usize,
928 cost_model: M,
929 ) -> Result<ExtractedTermVariants<C>, Error> {
930 if nvariants == 0 {
931 return Ok(ExtractedTermVariants {
932 termdag: TermDag::default(),
933 variants: roots.iter().map(|_| Vec::new()).collect(),
934 });
935 }
936
937 let rootsorts = roots.iter().map(|(sort, _)| sort.clone()).collect();
938 let extractor =
939 TreeExtractor::compute_costs_from_rootsorts(Some(rootsorts), self, cost_model);
940 let mut termdag = TermDag::default();
941 let variants = roots
942 .into_iter()
943 .map(|(sort, value)| {
944 extractor.extract_variants_with_sort(&mut termdag, value, nvariants, sort)
945 })
946 .collect();
947
948 Ok(ExtractedTermVariants { termdag, variants })
949 }
950
951 pub fn extract_value(
953 &self,
954 sort: &ArcSort,
955 value: Value,
956 ) -> Result<(TermDag, TermId, DefaultCost), Error> {
957 let sort_name = sort.name().to_owned();
958 let mut extracted = self.extract_best(vec![(sort.clone(), value)])?;
959 let root = extracted
960 .terms
961 .pop()
962 .expect("one requested root produces one result")
963 .ok_or_else(|| {
964 Error::ExtractError(format!(
965 "Unable to find any valid extraction for sort {sort_name}"
966 ))
967 })?;
968 Ok((extracted.termdag, root.term, root.cost))
969 }
970
971 pub fn function_to_dag(
973 &self,
974 sym: &str,
975 n: usize,
976 include_output: bool,
977 ) -> Result<(Vec<TermId>, Option<Vec<TermId>>, TermDag), Error> {
978 let func = self
979 .functions
980 .get(sym)
981 .ok_or(TypeError::UnboundFunction(sym.to_owned(), span!()))?;
982 let mut rootsorts = func.func_type.input.clone();
983 if include_output {
984 rootsorts.push(func.func_type.output.clone());
985 }
986 let extractor =
987 TreeExtractor::compute_costs_from_rootsorts(Some(rootsorts), self, DEFAULT_COST_MODEL);
988
989 let mut termdag = TermDag::default();
990 let mut inputs: Vec<TermId> = Vec::new();
991 let mut output: Option<Vec<TermId>> = if include_output {
992 Some(Vec::new())
993 } else {
994 None
995 };
996
997 let extract_row = |row: egglog_bridge::ScanEntry| {
998 if inputs.len() < n {
999 let mut children: Vec<TermId> = Vec::new();
1001 for (value, sort) in row.vals.iter().zip(&func.func_type.input) {
1002 let term = extractor
1003 .extract_best_with_sort(&mut termdag, *value, sort.clone())
1004 .map(|extracted| extracted.term)
1005 .unwrap_or_else(|| termdag.var("Unextractable".into()));
1006 children.push(term);
1007 }
1008 inputs.push(termdag.app(sym.to_owned(), children));
1009 if include_output {
1010 let value = row.vals[func.func_type.input.len()];
1011 let term = extractor
1012 .extract_best_with_sort(&mut termdag, value, func.func_type.output.clone())
1013 .map(|extracted| extracted.term)
1014 .unwrap_or_else(|| termdag.var("Unextractable".into()));
1015 output.as_mut().unwrap().push(term);
1016 }
1017 true
1018 } else {
1019 false
1020 }
1021 };
1022
1023 self.backend.for_each_while(func.backend_id, extract_row);
1024
1025 Ok((inputs, output, termdag))
1026 }
1027}