1use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
6use std::collections::HashMap;
7
8use super::functions::Oracle;
9use super::functions::*;
10
11#[allow(dead_code)]
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct RefinementType {
15 pub base: String,
17 pub predicate: String,
19}
20#[allow(dead_code)]
21impl RefinementType {
22 pub fn new(base: impl Into<String>, predicate: impl Into<String>) -> Self {
30 Self {
31 base: base.into(),
32 predicate: predicate.into(),
33 }
34 }
35 pub fn is_trivial(&self) -> bool {
37 self.predicate.trim().is_empty()
38 }
39 pub fn strengthen(&self, extra: impl Into<String>) -> Self {
41 let new_pred = format!("({}) && ({})", self.predicate, extra.into());
42 Self {
43 base: self.base.clone(),
44 predicate: new_pred,
45 }
46 }
47 pub fn weaken(&self, extra: impl Into<String>) -> Self {
49 let new_pred = format!("({}) || ({})", self.predicate, extra.into());
50 Self {
51 base: self.base.clone(),
52 predicate: new_pred,
53 }
54 }
55}
56#[allow(dead_code)]
58#[derive(Debug, Clone, Default)]
59pub struct ConstraintSynthEngine {
60 pub hard_constraints: Vec<String>,
62 pub soft_constraints: Vec<(String, u32)>,
64 pub template: Option<String>,
66}
67#[allow(dead_code)]
68impl ConstraintSynthEngine {
69 pub fn new() -> Self {
71 Self::default()
72 }
73 pub fn add_hard(&mut self, constraint: impl Into<String>) {
75 self.hard_constraints.push(constraint.into());
76 }
77 pub fn add_soft(&mut self, constraint: impl Into<String>, weight: u32) {
79 self.soft_constraints.push((constraint.into(), weight));
80 }
81 pub fn set_template(&mut self, template: impl Into<String>) {
83 self.template = Some(template.into());
84 }
85 pub fn num_constraints(&self) -> usize {
87 self.hard_constraints.len() + self.soft_constraints.len()
88 }
89 pub fn encode_pbo(&self) -> String {
91 let hard: Vec<String> = self
92 .hard_constraints
93 .iter()
94 .map(|c| format!("HARD: {}", c))
95 .collect();
96 let soft: Vec<String> = self
97 .soft_constraints
98 .iter()
99 .map(|(c, w)| format!("SOFT[{}]: {}", w, c))
100 .collect();
101 [hard, soft].concat().join("\n")
102 }
103 pub fn solve(&self) -> Option<String> {
106 if self.hard_constraints.is_empty() {
107 self.template.clone()
108 } else {
109 None
110 }
111 }
112}
113#[derive(Debug, Clone, PartialEq, Eq)]
118pub enum Spec {
119 Logic(String),
121 Examples(Vec<(Vec<String>, String)>),
123 Grammar(CFG),
125 Conjunction(Box<Spec>, Box<Spec>),
127 Disjunction(Box<Spec>, Box<Spec>),
129}
130impl Spec {
131 pub fn logic(pred: impl Into<String>) -> Self {
139 Spec::Logic(pred.into())
140 }
141 pub fn from_examples(ex: Vec<(Vec<String>, String)>) -> Self {
150 Spec::Examples(ex)
151 }
152 pub fn constraint_count(&self) -> usize {
154 match self {
155 Spec::Logic(_) => 1,
156 Spec::Examples(ex) => ex.len(),
157 Spec::Grammar(_) => 1,
158 Spec::Conjunction(a, b) => a.constraint_count() + b.constraint_count(),
159 Spec::Disjunction(a, b) => a.constraint_count().max(b.constraint_count()),
160 }
161 }
162}
163#[derive(Debug, Clone)]
165pub struct CegisState {
166 pub spec: Spec,
168 pub counterexamples: Vec<Vec<String>>,
170 pub candidate: Option<Candidate>,
172 pub iterations: usize,
174 pub max_iterations: usize,
176}
177impl CegisState {
178 pub fn new(spec: Spec, max_iterations: usize) -> Self {
186 Self {
187 spec,
188 counterexamples: Vec::new(),
189 candidate: None,
190 iterations: 0,
191 max_iterations,
192 }
193 }
194 pub fn propose(&mut self, candidate: Candidate) {
196 self.candidate = Some(candidate);
197 self.iterations += 1;
198 }
199 pub fn add_counterexample(&mut self, ce: Vec<String>) {
201 self.counterexamples.push(ce);
202 self.candidate = None;
203 }
204 pub fn is_solved(&self) -> bool {
206 self.candidate.is_some()
207 }
208 pub fn is_exhausted(&self) -> bool {
210 self.iterations >= self.max_iterations
211 }
212}
213#[allow(dead_code)]
215#[derive(Debug, Clone, PartialEq, Eq)]
216pub struct MWSynthGoal {
217 pub pre: String,
219 pub post: String,
221 pub output_var: String,
223 pub subgoals: Vec<MWSynthGoal>,
225}
226#[allow(dead_code)]
227impl MWSynthGoal {
228 pub fn leaf(
236 pre: impl Into<String>,
237 post: impl Into<String>,
238 output_var: impl Into<String>,
239 ) -> Self {
240 Self {
241 pre: pre.into(),
242 post: post.into(),
243 output_var: output_var.into(),
244 subgoals: Vec::new(),
245 }
246 }
247 pub fn with_subgoals(
249 pre: impl Into<String>,
250 post: impl Into<String>,
251 output_var: impl Into<String>,
252 subgoals: Vec<MWSynthGoal>,
253 ) -> Self {
254 Self {
255 pre: pre.into(),
256 post: post.into(),
257 output_var: output_var.into(),
258 subgoals,
259 }
260 }
261 pub fn depth(&self) -> usize {
263 if self.subgoals.is_empty() {
264 0
265 } else {
266 1 + self.subgoals.iter().map(|sg| sg.depth()).max().unwrap_or(0)
267 }
268 }
269 pub fn is_leaf(&self) -> bool {
271 self.subgoals.is_empty()
272 }
273}
274#[derive(Debug, Clone, PartialEq, Eq)]
276pub struct IOExample {
277 pub inputs: Vec<String>,
279 pub output: String,
281}
282impl IOExample {
283 pub fn new(inputs: Vec<String>, output: impl Into<String>) -> Self {
285 Self {
286 inputs,
287 output: output.into(),
288 }
289 }
290}
291#[derive(Debug, Clone)]
293pub struct EnumerativeSolver {
294 pub depth_limit: usize,
296 pub enumerated: usize,
298}
299impl EnumerativeSolver {
300 pub fn new(depth_limit: usize) -> Self {
302 Self {
303 depth_limit,
304 enumerated: 0,
305 }
306 }
307 pub fn enumerate(&mut self, grammar: &CFG, depth: usize) -> Vec<String> {
309 if depth == 0 {
310 let terms: Vec<String> = grammar.terminals.clone();
311 self.enumerated += terms.len();
312 terms
313 } else {
314 let mut result = Vec::new();
315 for prod in &grammar.productions.clone() {
316 if prod.rhs.iter().all(|s| grammar.terminals.contains(s)) {
317 let term = prod.rhs.join(" ");
318 result.push(term);
319 self.enumerated += 1;
320 }
321 }
322 result
323 }
324 }
325 pub fn solve(&mut self, _problem: &SyGuSProblem) -> SyGuSResult {
327 SyGuSResult::Timeout
328 }
329}
330#[derive(Debug, Clone, PartialEq, Eq)]
332pub struct Hole {
333 pub id: usize,
335 pub expected_type: String,
337 pub constraint: Option<String>,
339}
340impl Hole {
341 pub fn new(id: usize, expected_type: impl Into<String>) -> Self {
343 Self {
344 id,
345 expected_type: expected_type.into(),
346 constraint: None,
347 }
348 }
349 pub fn constrained(
351 id: usize,
352 expected_type: impl Into<String>,
353 constraint: impl Into<String>,
354 ) -> Self {
355 Self {
356 id,
357 expected_type: expected_type.into(),
358 constraint: Some(constraint.into()),
359 }
360 }
361}
362#[derive(Debug, Clone, Default)]
364pub struct TableOracle {
365 pub table: HashMap<Vec<String>, String>,
367}
368impl TableOracle {
369 pub fn new() -> Self {
371 Self::default()
372 }
373 pub fn insert(&mut self, input: Vec<String>, output: impl Into<String>) {
375 self.table.insert(input, output.into());
376 }
377}
378#[allow(dead_code)]
380#[derive(Debug, Clone)]
381pub struct ProgramSketch {
382 pub template: String,
384 pub num_holes: usize,
386 pub hole_types: Vec<String>,
388}
389#[allow(dead_code)]
390impl ProgramSketch {
391 pub fn new(template: &str, num_holes: usize) -> Self {
393 ProgramSketch {
394 template: template.to_string(),
395 num_holes,
396 hole_types: vec!["Any".to_string(); num_holes],
397 }
398 }
399 pub fn set_hole_type(&mut self, i: usize, ty: &str) {
401 if i < self.hole_types.len() {
402 self.hole_types[i] = ty.to_string();
403 }
404 }
405 pub fn fill(&self, completions: &[&str]) -> String {
407 let mut result = self.template.clone();
408 for (i, completion) in completions.iter().enumerate() {
409 result = result.replace(&format!("{{hole_{}}}", i), completion);
410 }
411 result
412 }
413 pub fn is_complete(&self, program: &str) -> bool {
415 !(0..self.num_holes).any(|i| program.contains(&format!("{{hole_{}}}", i)))
416 }
417 pub fn sketch_space_size(&self, num_operations: u64) -> u64 {
420 num_operations.saturating_pow(self.num_holes as u32)
421 }
422}
423#[derive(Debug, Clone)]
425pub struct VersionSpace {
426 pub candidates: Vec<String>,
428 pub examples: Vec<IOExample>,
430}
431impl VersionSpace {
432 pub fn new(candidates: Vec<String>) -> Self {
434 Self {
435 candidates,
436 examples: Vec::new(),
437 }
438 }
439 pub fn refine(&mut self, example: IOExample) {
441 self.examples.push(example);
442 }
443 pub fn unique_solution(&self) -> Option<&str> {
445 if self.candidates.len() == 1 {
446 Some(&self.candidates[0])
447 } else {
448 None
449 }
450 }
451 pub fn size(&self) -> usize {
453 self.candidates.len()
454 }
455}
456#[derive(Debug, Clone)]
458pub struct SketchSolver {
459 pub candidates_per_hole: usize,
461}
462impl SketchSolver {
463 pub fn new(candidates_per_hole: usize) -> Self {
465 Self {
466 candidates_per_hole,
467 }
468 }
469 pub fn complete(&self, sketch: &Sketch, _spec: &Spec) -> Option<String> {
473 if sketch.is_complete() {
474 return Some(sketch.source.clone());
475 }
476 None
477 }
478}
479#[allow(dead_code)]
481#[derive(Debug, Clone)]
482pub struct HoudiniInvariantSynth {
483 pub candidates: Vec<String>,
485 pub iterations: usize,
487}
488#[allow(dead_code)]
489impl HoudiniInvariantSynth {
490 pub fn new(candidates: Vec<String>) -> Self {
498 Self {
499 candidates,
500 iterations: 0,
501 }
502 }
503 pub fn add_candidate(&mut self, pred: impl Into<String>) {
505 self.candidates.push(pred.into());
506 }
507 pub fn step(&mut self) -> usize {
512 self.iterations += 1;
513 self.candidates.len()
514 }
515 pub fn current_invariants(&self) -> &[String] {
517 &self.candidates
518 }
519 pub fn is_fixpoint(&self) -> bool {
521 self.iterations > 0
522 }
523}
524#[derive(Debug, Clone, Default)]
526pub struct ComponentLibrary {
527 pub components: Vec<Component>,
529}
530impl ComponentLibrary {
531 pub fn new() -> Self {
533 Self::default()
534 }
535 pub fn register(&mut self, comp: Component) {
537 self.components.push(comp);
538 }
539 pub fn candidates_for(&self, target: &str) -> Vec<&Component> {
541 self.components
542 .iter()
543 .filter(|c| c.output_matches(target))
544 .collect()
545 }
546}
547#[derive(Debug, Clone, PartialEq, Eq)]
549pub enum VerifierResult {
550 Correct,
552 Counterexample(Vec<String>),
554 Unknown,
556}
557#[derive(Debug, Clone)]
559pub struct DerivationNode {
560 pub goal: (String, String),
562 pub rule: Option<DeductiveRule>,
564 pub children: Vec<DerivationNode>,
566 pub program: Option<String>,
568}
569impl DerivationNode {
570 pub fn leaf(
572 pre: impl Into<String>,
573 post: impl Into<String>,
574 program: impl Into<String>,
575 ) -> Self {
576 Self {
577 goal: (pre.into(), post.into()),
578 rule: None,
579 children: Vec::new(),
580 program: Some(program.into()),
581 }
582 }
583 pub fn internal(
585 pre: impl Into<String>,
586 post: impl Into<String>,
587 rule: DeductiveRule,
588 children: Vec<DerivationNode>,
589 ) -> Self {
590 Self {
591 goal: (pre.into(), post.into()),
592 rule: Some(rule),
593 children,
594 program: None,
595 }
596 }
597 pub fn extract_program(&self) -> String {
599 if let Some(ref p) = self.program {
600 return p.clone();
601 }
602 if let Some(ref rule) = self.rule {
603 match rule.name.as_str() {
604 "seq" => {
605 let parts: Vec<String> =
606 self.children.iter().map(|c| c.extract_program()).collect();
607 parts.join("; ")
608 }
609 "if-then-else" => {
610 let cond = self
611 .children
612 .first()
613 .map(|c| c.extract_program())
614 .unwrap_or_default();
615 let then_b = self
616 .children
617 .get(1)
618 .map(|c| c.extract_program())
619 .unwrap_or_default();
620 let else_b = self
621 .children
622 .get(2)
623 .map(|c| c.extract_program())
624 .unwrap_or_default();
625 format!("if {} then {} else {}", cond, then_b, else_b)
626 }
627 _ => self
628 .children
629 .iter()
630 .map(|c| c.extract_program())
631 .collect::<Vec<_>>()
632 .join(" "),
633 }
634 } else {
635 String::new()
636 }
637 }
638 pub fn depth(&self) -> usize {
640 if self.children.is_empty() {
641 0
642 } else {
643 1 + self.children.iter().map(|c| c.depth()).max().unwrap_or(0)
644 }
645 }
646}
647#[derive(Debug, Clone)]
649pub struct Sketch {
650 pub source: String,
652 pub holes: Vec<Hole>,
654}
655impl Sketch {
656 pub fn new(source: impl Into<String>, holes: Vec<Hole>) -> Self {
658 Self {
659 source: source.into(),
660 holes,
661 }
662 }
663 pub fn num_holes(&self) -> usize {
665 self.holes.len()
666 }
667 pub fn fill_hole(&self, hole_id: usize, expr: &str) -> Sketch {
669 let new_source = self.source.replace(&format!("??{}", hole_id), expr);
670 let new_holes = self
671 .holes
672 .iter()
673 .filter(|h| h.id != hole_id)
674 .cloned()
675 .collect();
676 Sketch::new(new_source, new_holes)
677 }
678 pub fn is_complete(&self) -> bool {
680 self.holes.is_empty()
681 }
682}
683#[allow(dead_code)]
685pub struct FoilLearner {
686 pub max_depth: usize,
688 pub min_gain: f64,
690}
691#[allow(dead_code)]
692impl FoilLearner {
693 pub fn new(max_depth: usize, min_gain: f64) -> Self {
695 FoilLearner {
696 max_depth,
697 min_gain,
698 }
699 }
700 pub fn foil_gain(&self, t: f64, p0: f64, n0: f64, p1: f64, n1: f64) -> f64 {
704 if p0 + n0 == 0.0 || p1 + n1 == 0.0 {
705 return 0.0;
706 }
707 let before = (p0 / (p0 + n0)).log2();
708 let after = (p1 / (p1 + n1)).log2();
709 t * (after - before)
710 }
711 pub fn covering_clauses_needed(&self, num_positives: usize) -> usize {
714 let mut remaining = num_positives;
715 let mut clauses = 0;
716 while remaining > 0 {
717 remaining /= 2;
718 clauses += 1;
719 if clauses > self.max_depth {
720 break;
721 }
722 }
723 clauses
724 }
725}
726#[derive(Debug, Clone)]
728pub enum FuncProgram {
729 Lit(String),
731 Var(String),
733 Lam(String, Box<FuncProgram>),
735 App(Box<FuncProgram>, Box<FuncProgram>),
737 ListCase {
739 scrutinee: Box<FuncProgram>,
740 nil_branch: Box<FuncProgram>,
741 cons_head: String,
742 cons_tail: String,
743 cons_branch: Box<FuncProgram>,
744 },
745 Rec(Box<FuncProgram>),
747}
748impl FuncProgram {
749 pub fn pretty(&self) -> String {
751 match self {
752 FuncProgram::Lit(v) => v.clone(),
753 FuncProgram::Var(v) => v.clone(),
754 FuncProgram::Lam(x, body) => format!("fun {} -> {}", x, body.pretty()),
755 FuncProgram::App(f, a) => format!("({} {})", f.pretty(), a.pretty()),
756 FuncProgram::ListCase {
757 scrutinee,
758 nil_branch,
759 cons_head,
760 cons_tail,
761 cons_branch,
762 } => {
763 format!(
764 "match {} with | [] -> {} | {}::{} -> {}",
765 scrutinee.pretty(),
766 nil_branch.pretty(),
767 cons_head,
768 cons_tail,
769 cons_branch.pretty()
770 )
771 }
772 FuncProgram::Rec(p) => format!("rec({})", p.pretty()),
773 }
774 }
775 pub fn size(&self) -> usize {
777 match self {
778 FuncProgram::Lit(_) | FuncProgram::Var(_) => 1,
779 FuncProgram::Lam(_, b) => 1 + b.size(),
780 FuncProgram::App(f, a) => 1 + f.size() + a.size(),
781 FuncProgram::ListCase {
782 scrutinee,
783 nil_branch,
784 cons_branch,
785 ..
786 } => 1 + scrutinee.size() + nil_branch.size() + cons_branch.size(),
787 FuncProgram::Rec(p) => 1 + p.size(),
788 }
789 }
790}
791#[derive(Debug, Clone)]
793pub struct OracleSynthLoop {
794 pub queries: Vec<Vec<String>>,
796 pub answers: Vec<Option<String>>,
798}
799impl OracleSynthLoop {
800 pub fn new() -> Self {
802 Self {
803 queries: Vec::new(),
804 answers: Vec::new(),
805 }
806 }
807 pub fn query(&mut self, oracle: &dyn Oracle, input: Vec<String>) -> Option<String> {
809 let ans = oracle.query(&input);
810 self.queries.push(input);
811 self.answers.push(ans.clone());
812 ans
813 }
814 pub fn num_queries(&self) -> usize {
816 self.queries.len()
817 }
818}
819#[allow(dead_code)]
822#[derive(Debug, Clone)]
823pub struct ILPProblem {
824 pub positive_examples: Vec<String>,
826 pub negative_examples: Vec<String>,
828 pub background: Vec<String>,
830 pub target: String,
832}
833#[allow(dead_code)]
834impl ILPProblem {
835 pub fn new(target: &str) -> Self {
837 ILPProblem {
838 positive_examples: vec![],
839 negative_examples: vec![],
840 background: vec![],
841 target: target.to_string(),
842 }
843 }
844 pub fn add_positive(&mut self, example: &str) {
846 self.positive_examples.push(example.to_string());
847 }
848 pub fn add_negative(&mut self, example: &str) {
850 self.negative_examples.push(example.to_string());
851 }
852 pub fn add_background(&mut self, fact: &str) {
854 self.background.push(fact.to_string());
855 }
856 pub fn total_examples(&self) -> usize {
858 self.positive_examples.len() + self.negative_examples.len()
859 }
860 pub fn accuracy(&self, hypothesis: &[String]) -> f64 {
864 if self.total_examples() == 0 {
865 return 1.0;
866 }
867 let _hyp_set: std::collections::HashSet<&str> =
868 hypothesis.iter().map(|s| s.as_str()).collect();
869 let covered = self
870 .positive_examples
871 .iter()
872 .filter(|e| hypothesis.iter().any(|h| h.contains(e.as_str())))
873 .count();
874 covered as f64 / self.total_examples() as f64
875 }
876}
877#[allow(dead_code)]
879pub struct OGISSynthesizer {
880 pub max_queries: usize,
882 pub counter_examples: Vec<(Vec<i64>, i64)>,
884}
885#[allow(dead_code)]
886impl OGISSynthesizer {
887 pub fn new(max_queries: usize) -> Self {
889 OGISSynthesizer {
890 max_queries,
891 counter_examples: vec![],
892 }
893 }
894 pub fn add_counter_example(&mut self, input: Vec<i64>, output: i64) {
896 self.counter_examples.push((input, output));
897 }
898 pub fn is_consistent(&self, _hypothesis: &str) -> bool {
901 self.counter_examples.len() <= self.max_queries
902 }
903 pub fn cegis_iterations(&self) -> usize {
906 self.counter_examples.len()
907 }
908 pub fn has_converged(&self) -> bool {
910 self.counter_examples.is_empty() || self.counter_examples.len() >= self.max_queries
911 }
912}
913#[derive(Debug, Clone, PartialEq, Eq)]
915pub struct Production {
916 pub lhs: String,
918 pub rhs: Vec<String>,
920}
921impl Production {
922 pub fn new(lhs: impl Into<String>, rhs: Vec<String>) -> Self {
924 Self {
925 lhs: lhs.into(),
926 rhs,
927 }
928 }
929 pub fn is_epsilon(&self) -> bool {
931 self.rhs.is_empty()
932 }
933}
934#[derive(Debug, Clone, Default)]
936pub struct SynthContext {
937 pub components: HashMap<String, SynthType>,
939}
940impl SynthContext {
941 pub fn new() -> Self {
943 Self::default()
944 }
945 pub fn add(&mut self, name: impl Into<String>, ty: SynthType) {
947 self.components.insert(name.into(), ty);
948 }
949 pub fn matching(&self, goal: &SynthType) -> Vec<(&str, &SynthType)> {
951 self.components
952 .iter()
953 .filter(|(_, t)| *t == goal)
954 .map(|(n, t)| (n.as_str(), t))
955 .collect()
956 }
957}
958#[derive(Debug, Clone, PartialEq, Eq, Hash)]
960pub enum SynthType {
961 Base(String),
963 Arrow(Box<SynthType>, Box<SynthType>),
965 Product(Box<SynthType>, Box<SynthType>),
967 Sum(Box<SynthType>, Box<SynthType>),
969 Var(String),
971 Forall(String, Box<SynthType>),
973 Unit,
975}
976impl SynthType {
977 pub fn arrow(a: SynthType, b: SynthType) -> Self {
979 SynthType::Arrow(Box::new(a), Box::new(b))
980 }
981 pub fn product(a: SynthType, b: SynthType) -> Self {
983 SynthType::Product(Box::new(a), Box::new(b))
984 }
985 pub fn sum(a: SynthType, b: SynthType) -> Self {
987 SynthType::Sum(Box::new(a), Box::new(b))
988 }
989 pub fn free_vars(&self) -> Vec<String> {
991 match self {
992 SynthType::Base(_) | SynthType::Unit => vec![],
993 SynthType::Var(n) => vec![n.clone()],
994 SynthType::Arrow(a, b) | SynthType::Product(a, b) | SynthType::Sum(a, b) => {
995 let mut v = a.free_vars();
996 v.extend(b.free_vars());
997 v.sort();
998 v.dedup();
999 v
1000 }
1001 SynthType::Forall(bound, body) => body
1002 .free_vars()
1003 .into_iter()
1004 .filter(|n| n != bound)
1005 .collect(),
1006 }
1007 }
1008}
1009#[derive(Debug, Clone, PartialEq, Eq)]
1013pub struct DeductiveRule {
1014 pub name: String,
1016 pub arity: usize,
1018 pub description: String,
1020}
1021impl DeductiveRule {
1022 pub fn new(name: impl Into<String>, arity: usize, description: impl Into<String>) -> Self {
1024 Self {
1025 name: name.into(),
1026 arity,
1027 description: description.into(),
1028 }
1029 }
1030}
1031#[derive(Debug, Clone, PartialEq, Eq)]
1033pub struct Candidate {
1034 pub program: String,
1036 pub verified_inputs: Vec<Vec<String>>,
1038}
1039impl Candidate {
1040 pub fn new(program: impl Into<String>) -> Self {
1042 Self {
1043 program: program.into(),
1044 verified_inputs: Vec::new(),
1045 }
1046 }
1047 pub fn evaluate(&self, _input: &[String]) -> String {
1049 "?".into()
1050 }
1051}
1052#[derive(Debug, Clone, PartialEq, Eq)]
1054pub struct Component {
1055 pub name: String,
1057 pub input_sorts: Vec<String>,
1059 pub output_sort: String,
1061 pub precondition: String,
1063 pub postcondition: String,
1065}
1066impl Component {
1067 pub fn new(
1069 name: impl Into<String>,
1070 input_sorts: Vec<String>,
1071 output_sort: impl Into<String>,
1072 precondition: impl Into<String>,
1073 postcondition: impl Into<String>,
1074 ) -> Self {
1075 Self {
1076 name: name.into(),
1077 input_sorts,
1078 output_sort: output_sort.into(),
1079 precondition: precondition.into(),
1080 postcondition: postcondition.into(),
1081 }
1082 }
1083 pub fn output_matches(&self, target_sort: &str) -> bool {
1085 self.output_sort == target_sort
1086 }
1087}
1088#[derive(Debug, Clone, PartialEq, Eq)]
1090pub enum FuncTemplate {
1091 Map,
1093 Filter,
1095 Fold,
1097 Unfold,
1099 Custom(String),
1101}
1102impl FuncTemplate {
1103 pub fn arity(&self) -> usize {
1105 match self {
1106 FuncTemplate::Map | FuncTemplate::Filter | FuncTemplate::Unfold => 2,
1107 FuncTemplate::Fold => 3,
1108 FuncTemplate::Custom(_) => 0,
1109 }
1110 }
1111}
1112#[derive(Debug, Clone)]
1114pub struct BottomUpSynth {
1115 pub max_size: usize,
1117 pub variables: Vec<String>,
1119 pub literals: Vec<String>,
1121}
1122impl BottomUpSynth {
1123 pub fn new(max_size: usize, variables: Vec<String>, literals: Vec<String>) -> Self {
1125 Self {
1126 max_size,
1127 variables,
1128 literals,
1129 }
1130 }
1131 pub fn enumerate_size(&self, size: usize) -> Vec<FuncProgram> {
1133 if size == 1 {
1134 let mut progs: Vec<FuncProgram> = self
1135 .literals
1136 .iter()
1137 .map(|l| FuncProgram::Lit(l.clone()))
1138 .collect();
1139 progs.extend(self.variables.iter().map(|v| FuncProgram::Var(v.clone())));
1140 progs
1141 } else {
1142 let mut progs = Vec::new();
1143 for f_size in 1..size {
1144 let a_size = size - 1 - f_size;
1145 if a_size == 0 {
1146 continue;
1147 }
1148 let f_progs = self.enumerate_size(f_size);
1149 let a_progs = self.enumerate_size(a_size);
1150 for f in &f_progs {
1151 for a in &a_progs {
1152 progs.push(FuncProgram::App(Box::new(f.clone()), Box::new(a.clone())));
1153 }
1154 }
1155 }
1156 progs
1157 }
1158 }
1159}
1160#[derive(Debug, Clone, PartialEq, Eq)]
1162pub struct CFG {
1163 pub start: String,
1165 pub productions: Vec<Production>,
1167 pub non_terminals: Vec<String>,
1169 pub terminals: Vec<String>,
1171}
1172impl CFG {
1173 pub fn new(start: impl Into<String>) -> Self {
1175 let s = start.into();
1176 Self {
1177 start: s.clone(),
1178 productions: Vec::new(),
1179 non_terminals: vec![s],
1180 terminals: Vec::new(),
1181 }
1182 }
1183 pub fn add_production(&mut self, lhs: impl Into<String>, rhs: Vec<String>) {
1185 let lhs_s = lhs.into();
1186 if !self.non_terminals.contains(&lhs_s) {
1187 self.non_terminals.push(lhs_s.clone());
1188 }
1189 self.productions.push(Production::new(lhs_s, rhs));
1190 }
1191 pub fn add_terminal(&mut self, t: impl Into<String>) {
1193 let t_s = t.into();
1194 if !self.terminals.contains(&t_s) {
1195 self.terminals.push(t_s);
1196 }
1197 }
1198 pub fn productions_for(&self, nt: &str) -> Vec<&Production> {
1200 self.productions.iter().filter(|p| p.lhs == nt).collect()
1201 }
1202}
1203#[allow(dead_code)]
1205#[derive(Debug, Clone)]
1206pub struct Superoptimiser {
1207 pub instruction_set: Vec<String>,
1209 pub max_length: usize,
1211 pub tested: usize,
1213}
1214#[allow(dead_code)]
1215impl Superoptimiser {
1216 pub fn new(instruction_set: Vec<String>, max_length: usize) -> Self {
1224 Self {
1225 instruction_set,
1226 max_length,
1227 tested: 0,
1228 }
1229 }
1230 pub fn enumerate_length(&self, length: usize) -> Vec<Vec<String>> {
1232 if length == 0 {
1233 return vec![vec![]];
1234 }
1235 let mut result = Vec::new();
1236 for shorter in self.enumerate_length(length - 1) {
1237 for instr in &self.instruction_set {
1238 let mut prog = shorter.clone();
1239 prog.push(instr.clone());
1240 result.push(prog);
1241 }
1242 }
1243 result
1244 }
1245 pub fn test_equivalent(&mut self, _candidate: &[String], _reference: &[String]) -> bool {
1247 self.tested += 1;
1248 false
1249 }
1250 pub fn optimise(&mut self, reference: &[String]) -> Option<Vec<String>> {
1252 for length in 0..=self.max_length {
1253 for candidate in self.enumerate_length(length) {
1254 if self.test_equivalent(&candidate, reference) {
1255 return Some(candidate);
1256 }
1257 }
1258 }
1259 None
1260 }
1261}
1262#[derive(Debug, Clone)]
1264pub struct FlashFillSynth {
1265 pub operators: Vec<String>,
1267}
1268impl FlashFillSynth {
1269 pub fn new() -> Self {
1271 Self {
1272 operators: vec![
1273 "Concat".into(),
1274 "Substr".into(),
1275 "GetToken".into(),
1276 "Trim".into(),
1277 "ToUpper".into(),
1278 "ToLower".into(),
1279 ],
1280 }
1281 }
1282 pub fn synthesise(&self, examples: &[IOExample]) -> Option<String> {
1284 if examples.is_empty() {
1285 return None;
1286 }
1287 let identity = examples
1288 .iter()
1289 .all(|ex| ex.inputs.len() == 1 && ex.inputs[0] == ex.output);
1290 if identity {
1291 Some("fun x -> x".into())
1292 } else {
1293 None
1294 }
1295 }
1296}
1297#[derive(Debug, Clone, PartialEq, Eq)]
1299pub enum SyGuSResult {
1300 Solution(String),
1302 Infeasible,
1304 Timeout,
1306}
1307#[derive(Debug, Clone)]
1309pub struct SyGuSProblem {
1310 pub function_name: String,
1312 pub arguments: Vec<(String, String)>,
1314 pub return_sort: String,
1316 pub grammar: CFG,
1318 pub constraint: String,
1320}
1321impl SyGuSProblem {
1322 pub fn new(
1324 function_name: impl Into<String>,
1325 arguments: Vec<(String, String)>,
1326 return_sort: impl Into<String>,
1327 grammar: CFG,
1328 constraint: impl Into<String>,
1329 ) -> Self {
1330 Self {
1331 function_name: function_name.into(),
1332 arguments,
1333 return_sort: return_sort.into(),
1334 grammar,
1335 constraint: constraint.into(),
1336 }
1337 }
1338 pub fn arity(&self) -> usize {
1340 self.arguments.len()
1341 }
1342}
1343#[derive(Debug, Clone)]
1345pub struct ComponentSynthQuery {
1346 pub target_sort: String,
1348 pub spec: String,
1350 pub inputs: Vec<(String, String)>,
1352}
1353impl ComponentSynthQuery {
1354 pub fn new(
1356 target_sort: impl Into<String>,
1357 spec: impl Into<String>,
1358 inputs: Vec<(String, String)>,
1359 ) -> Self {
1360 Self {
1361 target_sort: target_sort.into(),
1362 spec: spec.into(),
1363 inputs,
1364 }
1365 }
1366}
1367#[derive(Debug, Clone)]
1369pub struct TypeDirectedSynth {
1370 pub depth_limit: usize,
1372 pub attempts: usize,
1374}
1375impl TypeDirectedSynth {
1376 pub fn new(depth_limit: usize) -> Self {
1378 Self {
1379 depth_limit,
1380 attempts: 0,
1381 }
1382 }
1383 pub fn synthesise(
1387 &mut self,
1388 ctx: &SynthContext,
1389 goal_type: &SynthType,
1390 depth: usize,
1391 ) -> Option<String> {
1392 self.attempts += 1;
1393 if depth > self.depth_limit {
1394 return None;
1395 }
1396 let matches = ctx.matching(goal_type);
1397 if let Some((name, _)) = matches.first() {
1398 return Some(name.to_string());
1399 }
1400 match goal_type {
1401 SynthType::Unit => Some("()".into()),
1402 SynthType::Arrow(dom, cod) => {
1403 let arg_name = format!("x{}", depth);
1404 let mut new_ctx = ctx.clone();
1405 new_ctx.add(arg_name.clone(), *dom.clone());
1406 let body = self.synthesise(&new_ctx, cod, depth + 1)?;
1407 Some(format!("fun {} -> {}", arg_name, body))
1408 }
1409 SynthType::Product(a, b) => {
1410 let pa = self.synthesise(ctx, a, depth + 1)?;
1411 let pb = self.synthesise(ctx, b, depth + 1)?;
1412 Some(format!("({}, {})", pa, pb))
1413 }
1414 _ => None,
1415 }
1416 }
1417}