use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
use std::collections::HashMap;
use super::functions::Oracle;
use super::functions::*;
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RefinementType {
pub base: String,
pub predicate: String,
}
#[allow(dead_code)]
impl RefinementType {
pub fn new(base: impl Into<String>, predicate: impl Into<String>) -> Self {
Self {
base: base.into(),
predicate: predicate.into(),
}
}
pub fn is_trivial(&self) -> bool {
self.predicate.trim().is_empty()
}
pub fn strengthen(&self, extra: impl Into<String>) -> Self {
let new_pred = format!("({}) && ({})", self.predicate, extra.into());
Self {
base: self.base.clone(),
predicate: new_pred,
}
}
pub fn weaken(&self, extra: impl Into<String>) -> Self {
let new_pred = format!("({}) || ({})", self.predicate, extra.into());
Self {
base: self.base.clone(),
predicate: new_pred,
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct ConstraintSynthEngine {
pub hard_constraints: Vec<String>,
pub soft_constraints: Vec<(String, u32)>,
pub template: Option<String>,
}
#[allow(dead_code)]
impl ConstraintSynthEngine {
pub fn new() -> Self {
Self::default()
}
pub fn add_hard(&mut self, constraint: impl Into<String>) {
self.hard_constraints.push(constraint.into());
}
pub fn add_soft(&mut self, constraint: impl Into<String>, weight: u32) {
self.soft_constraints.push((constraint.into(), weight));
}
pub fn set_template(&mut self, template: impl Into<String>) {
self.template = Some(template.into());
}
pub fn num_constraints(&self) -> usize {
self.hard_constraints.len() + self.soft_constraints.len()
}
pub fn encode_pbo(&self) -> String {
let hard: Vec<String> = self
.hard_constraints
.iter()
.map(|c| format!("HARD: {}", c))
.collect();
let soft: Vec<String> = self
.soft_constraints
.iter()
.map(|(c, w)| format!("SOFT[{}]: {}", w, c))
.collect();
[hard, soft].concat().join("\n")
}
pub fn solve(&self) -> Option<String> {
if self.hard_constraints.is_empty() {
self.template.clone()
} else {
None
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Spec {
Logic(String),
Examples(Vec<(Vec<String>, String)>),
Grammar(CFG),
Conjunction(Box<Spec>, Box<Spec>),
Disjunction(Box<Spec>, Box<Spec>),
}
impl Spec {
pub fn logic(pred: impl Into<String>) -> Self {
Spec::Logic(pred.into())
}
pub fn from_examples(ex: Vec<(Vec<String>, String)>) -> Self {
Spec::Examples(ex)
}
pub fn constraint_count(&self) -> usize {
match self {
Spec::Logic(_) => 1,
Spec::Examples(ex) => ex.len(),
Spec::Grammar(_) => 1,
Spec::Conjunction(a, b) => a.constraint_count() + b.constraint_count(),
Spec::Disjunction(a, b) => a.constraint_count().max(b.constraint_count()),
}
}
}
#[derive(Debug, Clone)]
pub struct CegisState {
pub spec: Spec,
pub counterexamples: Vec<Vec<String>>,
pub candidate: Option<Candidate>,
pub iterations: usize,
pub max_iterations: usize,
}
impl CegisState {
pub fn new(spec: Spec, max_iterations: usize) -> Self {
Self {
spec,
counterexamples: Vec::new(),
candidate: None,
iterations: 0,
max_iterations,
}
}
pub fn propose(&mut self, candidate: Candidate) {
self.candidate = Some(candidate);
self.iterations += 1;
}
pub fn add_counterexample(&mut self, ce: Vec<String>) {
self.counterexamples.push(ce);
self.candidate = None;
}
pub fn is_solved(&self) -> bool {
self.candidate.is_some()
}
pub fn is_exhausted(&self) -> bool {
self.iterations >= self.max_iterations
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MWSynthGoal {
pub pre: String,
pub post: String,
pub output_var: String,
pub subgoals: Vec<MWSynthGoal>,
}
#[allow(dead_code)]
impl MWSynthGoal {
pub fn leaf(
pre: impl Into<String>,
post: impl Into<String>,
output_var: impl Into<String>,
) -> Self {
Self {
pre: pre.into(),
post: post.into(),
output_var: output_var.into(),
subgoals: Vec::new(),
}
}
pub fn with_subgoals(
pre: impl Into<String>,
post: impl Into<String>,
output_var: impl Into<String>,
subgoals: Vec<MWSynthGoal>,
) -> Self {
Self {
pre: pre.into(),
post: post.into(),
output_var: output_var.into(),
subgoals,
}
}
pub fn depth(&self) -> usize {
if self.subgoals.is_empty() {
0
} else {
1 + self.subgoals.iter().map(|sg| sg.depth()).max().unwrap_or(0)
}
}
pub fn is_leaf(&self) -> bool {
self.subgoals.is_empty()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IOExample {
pub inputs: Vec<String>,
pub output: String,
}
impl IOExample {
pub fn new(inputs: Vec<String>, output: impl Into<String>) -> Self {
Self {
inputs,
output: output.into(),
}
}
}
#[derive(Debug, Clone)]
pub struct EnumerativeSolver {
pub depth_limit: usize,
pub enumerated: usize,
}
impl EnumerativeSolver {
pub fn new(depth_limit: usize) -> Self {
Self {
depth_limit,
enumerated: 0,
}
}
pub fn enumerate(&mut self, grammar: &CFG, depth: usize) -> Vec<String> {
if depth == 0 {
let terms: Vec<String> = grammar.terminals.clone();
self.enumerated += terms.len();
terms
} else {
let mut result = Vec::new();
for prod in &grammar.productions.clone() {
if prod.rhs.iter().all(|s| grammar.terminals.contains(s)) {
let term = prod.rhs.join(" ");
result.push(term);
self.enumerated += 1;
}
}
result
}
}
pub fn solve(&mut self, _problem: &SyGuSProblem) -> SyGuSResult {
SyGuSResult::Timeout
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Hole {
pub id: usize,
pub expected_type: String,
pub constraint: Option<String>,
}
impl Hole {
pub fn new(id: usize, expected_type: impl Into<String>) -> Self {
Self {
id,
expected_type: expected_type.into(),
constraint: None,
}
}
pub fn constrained(
id: usize,
expected_type: impl Into<String>,
constraint: impl Into<String>,
) -> Self {
Self {
id,
expected_type: expected_type.into(),
constraint: Some(constraint.into()),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct TableOracle {
pub table: HashMap<Vec<String>, String>,
}
impl TableOracle {
pub fn new() -> Self {
Self::default()
}
pub fn insert(&mut self, input: Vec<String>, output: impl Into<String>) {
self.table.insert(input, output.into());
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ProgramSketch {
pub template: String,
pub num_holes: usize,
pub hole_types: Vec<String>,
}
#[allow(dead_code)]
impl ProgramSketch {
pub fn new(template: &str, num_holes: usize) -> Self {
ProgramSketch {
template: template.to_string(),
num_holes,
hole_types: vec!["Any".to_string(); num_holes],
}
}
pub fn set_hole_type(&mut self, i: usize, ty: &str) {
if i < self.hole_types.len() {
self.hole_types[i] = ty.to_string();
}
}
pub fn fill(&self, completions: &[&str]) -> String {
let mut result = self.template.clone();
for (i, completion) in completions.iter().enumerate() {
result = result.replace(&format!("{{hole_{}}}", i), completion);
}
result
}
pub fn is_complete(&self, program: &str) -> bool {
!(0..self.num_holes).any(|i| program.contains(&format!("{{hole_{}}}", i)))
}
pub fn sketch_space_size(&self, num_operations: u64) -> u64 {
num_operations.saturating_pow(self.num_holes as u32)
}
}
#[derive(Debug, Clone)]
pub struct VersionSpace {
pub candidates: Vec<String>,
pub examples: Vec<IOExample>,
}
impl VersionSpace {
pub fn new(candidates: Vec<String>) -> Self {
Self {
candidates,
examples: Vec::new(),
}
}
pub fn refine(&mut self, example: IOExample) {
self.examples.push(example);
}
pub fn unique_solution(&self) -> Option<&str> {
if self.candidates.len() == 1 {
Some(&self.candidates[0])
} else {
None
}
}
pub fn size(&self) -> usize {
self.candidates.len()
}
}
#[derive(Debug, Clone)]
pub struct SketchSolver {
pub candidates_per_hole: usize,
}
impl SketchSolver {
pub fn new(candidates_per_hole: usize) -> Self {
Self {
candidates_per_hole,
}
}
pub fn complete(&self, sketch: &Sketch, _spec: &Spec) -> Option<String> {
if sketch.is_complete() {
return Some(sketch.source.clone());
}
None
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct HoudiniInvariantSynth {
pub candidates: Vec<String>,
pub iterations: usize,
}
#[allow(dead_code)]
impl HoudiniInvariantSynth {
pub fn new(candidates: Vec<String>) -> Self {
Self {
candidates,
iterations: 0,
}
}
pub fn add_candidate(&mut self, pred: impl Into<String>) {
self.candidates.push(pred.into());
}
pub fn step(&mut self) -> usize {
self.iterations += 1;
self.candidates.len()
}
pub fn current_invariants(&self) -> &[String] {
&self.candidates
}
pub fn is_fixpoint(&self) -> bool {
self.iterations > 0
}
}
#[derive(Debug, Clone, Default)]
pub struct ComponentLibrary {
pub components: Vec<Component>,
}
impl ComponentLibrary {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, comp: Component) {
self.components.push(comp);
}
pub fn candidates_for(&self, target: &str) -> Vec<&Component> {
self.components
.iter()
.filter(|c| c.output_matches(target))
.collect()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VerifierResult {
Correct,
Counterexample(Vec<String>),
Unknown,
}
#[derive(Debug, Clone)]
pub struct DerivationNode {
pub goal: (String, String),
pub rule: Option<DeductiveRule>,
pub children: Vec<DerivationNode>,
pub program: Option<String>,
}
impl DerivationNode {
pub fn leaf(
pre: impl Into<String>,
post: impl Into<String>,
program: impl Into<String>,
) -> Self {
Self {
goal: (pre.into(), post.into()),
rule: None,
children: Vec::new(),
program: Some(program.into()),
}
}
pub fn internal(
pre: impl Into<String>,
post: impl Into<String>,
rule: DeductiveRule,
children: Vec<DerivationNode>,
) -> Self {
Self {
goal: (pre.into(), post.into()),
rule: Some(rule),
children,
program: None,
}
}
pub fn extract_program(&self) -> String {
if let Some(ref p) = self.program {
return p.clone();
}
if let Some(ref rule) = self.rule {
match rule.name.as_str() {
"seq" => {
let parts: Vec<String> =
self.children.iter().map(|c| c.extract_program()).collect();
parts.join("; ")
}
"if-then-else" => {
let cond = self
.children
.first()
.map(|c| c.extract_program())
.unwrap_or_default();
let then_b = self
.children
.get(1)
.map(|c| c.extract_program())
.unwrap_or_default();
let else_b = self
.children
.get(2)
.map(|c| c.extract_program())
.unwrap_or_default();
format!("if {} then {} else {}", cond, then_b, else_b)
}
_ => self
.children
.iter()
.map(|c| c.extract_program())
.collect::<Vec<_>>()
.join(" "),
}
} else {
String::new()
}
}
pub fn depth(&self) -> usize {
if self.children.is_empty() {
0
} else {
1 + self.children.iter().map(|c| c.depth()).max().unwrap_or(0)
}
}
}
#[derive(Debug, Clone)]
pub struct Sketch {
pub source: String,
pub holes: Vec<Hole>,
}
impl Sketch {
pub fn new(source: impl Into<String>, holes: Vec<Hole>) -> Self {
Self {
source: source.into(),
holes,
}
}
pub fn num_holes(&self) -> usize {
self.holes.len()
}
pub fn fill_hole(&self, hole_id: usize, expr: &str) -> Sketch {
let new_source = self.source.replace(&format!("??{}", hole_id), expr);
let new_holes = self
.holes
.iter()
.filter(|h| h.id != hole_id)
.cloned()
.collect();
Sketch::new(new_source, new_holes)
}
pub fn is_complete(&self) -> bool {
self.holes.is_empty()
}
}
#[allow(dead_code)]
pub struct FoilLearner {
pub max_depth: usize,
pub min_gain: f64,
}
#[allow(dead_code)]
impl FoilLearner {
pub fn new(max_depth: usize, min_gain: f64) -> Self {
FoilLearner {
max_depth,
min_gain,
}
}
pub fn foil_gain(&self, t: f64, p0: f64, n0: f64, p1: f64, n1: f64) -> f64 {
if p0 + n0 == 0.0 || p1 + n1 == 0.0 {
return 0.0;
}
let before = (p0 / (p0 + n0)).log2();
let after = (p1 / (p1 + n1)).log2();
t * (after - before)
}
pub fn covering_clauses_needed(&self, num_positives: usize) -> usize {
let mut remaining = num_positives;
let mut clauses = 0;
while remaining > 0 {
remaining /= 2;
clauses += 1;
if clauses > self.max_depth {
break;
}
}
clauses
}
}
#[derive(Debug, Clone)]
pub enum FuncProgram {
Lit(String),
Var(String),
Lam(String, Box<FuncProgram>),
App(Box<FuncProgram>, Box<FuncProgram>),
ListCase {
scrutinee: Box<FuncProgram>,
nil_branch: Box<FuncProgram>,
cons_head: String,
cons_tail: String,
cons_branch: Box<FuncProgram>,
},
Rec(Box<FuncProgram>),
}
impl FuncProgram {
pub fn pretty(&self) -> String {
match self {
FuncProgram::Lit(v) => v.clone(),
FuncProgram::Var(v) => v.clone(),
FuncProgram::Lam(x, body) => format!("fun {} -> {}", x, body.pretty()),
FuncProgram::App(f, a) => format!("({} {})", f.pretty(), a.pretty()),
FuncProgram::ListCase {
scrutinee,
nil_branch,
cons_head,
cons_tail,
cons_branch,
} => {
format!(
"match {} with | [] -> {} | {}::{} -> {}",
scrutinee.pretty(),
nil_branch.pretty(),
cons_head,
cons_tail,
cons_branch.pretty()
)
}
FuncProgram::Rec(p) => format!("rec({})", p.pretty()),
}
}
pub fn size(&self) -> usize {
match self {
FuncProgram::Lit(_) | FuncProgram::Var(_) => 1,
FuncProgram::Lam(_, b) => 1 + b.size(),
FuncProgram::App(f, a) => 1 + f.size() + a.size(),
FuncProgram::ListCase {
scrutinee,
nil_branch,
cons_branch,
..
} => 1 + scrutinee.size() + nil_branch.size() + cons_branch.size(),
FuncProgram::Rec(p) => 1 + p.size(),
}
}
}
#[derive(Debug, Clone)]
pub struct OracleSynthLoop {
pub queries: Vec<Vec<String>>,
pub answers: Vec<Option<String>>,
}
impl OracleSynthLoop {
pub fn new() -> Self {
Self {
queries: Vec::new(),
answers: Vec::new(),
}
}
pub fn query(&mut self, oracle: &dyn Oracle, input: Vec<String>) -> Option<String> {
let ans = oracle.query(&input);
self.queries.push(input);
self.answers.push(ans.clone());
ans
}
pub fn num_queries(&self) -> usize {
self.queries.len()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ILPProblem {
pub positive_examples: Vec<String>,
pub negative_examples: Vec<String>,
pub background: Vec<String>,
pub target: String,
}
#[allow(dead_code)]
impl ILPProblem {
pub fn new(target: &str) -> Self {
ILPProblem {
positive_examples: vec![],
negative_examples: vec![],
background: vec![],
target: target.to_string(),
}
}
pub fn add_positive(&mut self, example: &str) {
self.positive_examples.push(example.to_string());
}
pub fn add_negative(&mut self, example: &str) {
self.negative_examples.push(example.to_string());
}
pub fn add_background(&mut self, fact: &str) {
self.background.push(fact.to_string());
}
pub fn total_examples(&self) -> usize {
self.positive_examples.len() + self.negative_examples.len()
}
pub fn accuracy(&self, hypothesis: &[String]) -> f64 {
if self.total_examples() == 0 {
return 1.0;
}
let _hyp_set: std::collections::HashSet<&str> =
hypothesis.iter().map(|s| s.as_str()).collect();
let covered = self
.positive_examples
.iter()
.filter(|e| hypothesis.iter().any(|h| h.contains(e.as_str())))
.count();
covered as f64 / self.total_examples() as f64
}
}
#[allow(dead_code)]
pub struct OGISSynthesizer {
pub max_queries: usize,
pub counter_examples: Vec<(Vec<i64>, i64)>,
}
#[allow(dead_code)]
impl OGISSynthesizer {
pub fn new(max_queries: usize) -> Self {
OGISSynthesizer {
max_queries,
counter_examples: vec![],
}
}
pub fn add_counter_example(&mut self, input: Vec<i64>, output: i64) {
self.counter_examples.push((input, output));
}
pub fn is_consistent(&self, _hypothesis: &str) -> bool {
self.counter_examples.len() <= self.max_queries
}
pub fn cegis_iterations(&self) -> usize {
self.counter_examples.len()
}
pub fn has_converged(&self) -> bool {
self.counter_examples.is_empty() || self.counter_examples.len() >= self.max_queries
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Production {
pub lhs: String,
pub rhs: Vec<String>,
}
impl Production {
pub fn new(lhs: impl Into<String>, rhs: Vec<String>) -> Self {
Self {
lhs: lhs.into(),
rhs,
}
}
pub fn is_epsilon(&self) -> bool {
self.rhs.is_empty()
}
}
#[derive(Debug, Clone, Default)]
pub struct SynthContext {
pub components: HashMap<String, SynthType>,
}
impl SynthContext {
pub fn new() -> Self {
Self::default()
}
pub fn add(&mut self, name: impl Into<String>, ty: SynthType) {
self.components.insert(name.into(), ty);
}
pub fn matching(&self, goal: &SynthType) -> Vec<(&str, &SynthType)> {
self.components
.iter()
.filter(|(_, t)| *t == goal)
.map(|(n, t)| (n.as_str(), t))
.collect()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SynthType {
Base(String),
Arrow(Box<SynthType>, Box<SynthType>),
Product(Box<SynthType>, Box<SynthType>),
Sum(Box<SynthType>, Box<SynthType>),
Var(String),
Forall(String, Box<SynthType>),
Unit,
}
impl SynthType {
pub fn arrow(a: SynthType, b: SynthType) -> Self {
SynthType::Arrow(Box::new(a), Box::new(b))
}
pub fn product(a: SynthType, b: SynthType) -> Self {
SynthType::Product(Box::new(a), Box::new(b))
}
pub fn sum(a: SynthType, b: SynthType) -> Self {
SynthType::Sum(Box::new(a), Box::new(b))
}
pub fn free_vars(&self) -> Vec<String> {
match self {
SynthType::Base(_) | SynthType::Unit => vec![],
SynthType::Var(n) => vec![n.clone()],
SynthType::Arrow(a, b) | SynthType::Product(a, b) | SynthType::Sum(a, b) => {
let mut v = a.free_vars();
v.extend(b.free_vars());
v.sort();
v.dedup();
v
}
SynthType::Forall(bound, body) => body
.free_vars()
.into_iter()
.filter(|n| n != bound)
.collect(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeductiveRule {
pub name: String,
pub arity: usize,
pub description: String,
}
impl DeductiveRule {
pub fn new(name: impl Into<String>, arity: usize, description: impl Into<String>) -> Self {
Self {
name: name.into(),
arity,
description: description.into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Candidate {
pub program: String,
pub verified_inputs: Vec<Vec<String>>,
}
impl Candidate {
pub fn new(program: impl Into<String>) -> Self {
Self {
program: program.into(),
verified_inputs: Vec::new(),
}
}
pub fn evaluate(&self, _input: &[String]) -> String {
"?".into()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Component {
pub name: String,
pub input_sorts: Vec<String>,
pub output_sort: String,
pub precondition: String,
pub postcondition: String,
}
impl Component {
pub fn new(
name: impl Into<String>,
input_sorts: Vec<String>,
output_sort: impl Into<String>,
precondition: impl Into<String>,
postcondition: impl Into<String>,
) -> Self {
Self {
name: name.into(),
input_sorts,
output_sort: output_sort.into(),
precondition: precondition.into(),
postcondition: postcondition.into(),
}
}
pub fn output_matches(&self, target_sort: &str) -> bool {
self.output_sort == target_sort
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FuncTemplate {
Map,
Filter,
Fold,
Unfold,
Custom(String),
}
impl FuncTemplate {
pub fn arity(&self) -> usize {
match self {
FuncTemplate::Map | FuncTemplate::Filter | FuncTemplate::Unfold => 2,
FuncTemplate::Fold => 3,
FuncTemplate::Custom(_) => 0,
}
}
}
#[derive(Debug, Clone)]
pub struct BottomUpSynth {
pub max_size: usize,
pub variables: Vec<String>,
pub literals: Vec<String>,
}
impl BottomUpSynth {
pub fn new(max_size: usize, variables: Vec<String>, literals: Vec<String>) -> Self {
Self {
max_size,
variables,
literals,
}
}
pub fn enumerate_size(&self, size: usize) -> Vec<FuncProgram> {
if size == 1 {
let mut progs: Vec<FuncProgram> = self
.literals
.iter()
.map(|l| FuncProgram::Lit(l.clone()))
.collect();
progs.extend(self.variables.iter().map(|v| FuncProgram::Var(v.clone())));
progs
} else {
let mut progs = Vec::new();
for f_size in 1..size {
let a_size = size - 1 - f_size;
if a_size == 0 {
continue;
}
let f_progs = self.enumerate_size(f_size);
let a_progs = self.enumerate_size(a_size);
for f in &f_progs {
for a in &a_progs {
progs.push(FuncProgram::App(Box::new(f.clone()), Box::new(a.clone())));
}
}
}
progs
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CFG {
pub start: String,
pub productions: Vec<Production>,
pub non_terminals: Vec<String>,
pub terminals: Vec<String>,
}
impl CFG {
pub fn new(start: impl Into<String>) -> Self {
let s = start.into();
Self {
start: s.clone(),
productions: Vec::new(),
non_terminals: vec![s],
terminals: Vec::new(),
}
}
pub fn add_production(&mut self, lhs: impl Into<String>, rhs: Vec<String>) {
let lhs_s = lhs.into();
if !self.non_terminals.contains(&lhs_s) {
self.non_terminals.push(lhs_s.clone());
}
self.productions.push(Production::new(lhs_s, rhs));
}
pub fn add_terminal(&mut self, t: impl Into<String>) {
let t_s = t.into();
if !self.terminals.contains(&t_s) {
self.terminals.push(t_s);
}
}
pub fn productions_for(&self, nt: &str) -> Vec<&Production> {
self.productions.iter().filter(|p| p.lhs == nt).collect()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct Superoptimiser {
pub instruction_set: Vec<String>,
pub max_length: usize,
pub tested: usize,
}
#[allow(dead_code)]
impl Superoptimiser {
pub fn new(instruction_set: Vec<String>, max_length: usize) -> Self {
Self {
instruction_set,
max_length,
tested: 0,
}
}
pub fn enumerate_length(&self, length: usize) -> Vec<Vec<String>> {
if length == 0 {
return vec![vec![]];
}
let mut result = Vec::new();
for shorter in self.enumerate_length(length - 1) {
for instr in &self.instruction_set {
let mut prog = shorter.clone();
prog.push(instr.clone());
result.push(prog);
}
}
result
}
pub fn test_equivalent(&mut self, _candidate: &[String], _reference: &[String]) -> bool {
self.tested += 1;
false
}
pub fn optimise(&mut self, reference: &[String]) -> Option<Vec<String>> {
for length in 0..=self.max_length {
for candidate in self.enumerate_length(length) {
if self.test_equivalent(&candidate, reference) {
return Some(candidate);
}
}
}
None
}
}
#[derive(Debug, Clone)]
pub struct FlashFillSynth {
pub operators: Vec<String>,
}
impl FlashFillSynth {
pub fn new() -> Self {
Self {
operators: vec![
"Concat".into(),
"Substr".into(),
"GetToken".into(),
"Trim".into(),
"ToUpper".into(),
"ToLower".into(),
],
}
}
pub fn synthesise(&self, examples: &[IOExample]) -> Option<String> {
if examples.is_empty() {
return None;
}
let identity = examples
.iter()
.all(|ex| ex.inputs.len() == 1 && ex.inputs[0] == ex.output);
if identity {
Some("fun x -> x".into())
} else {
None
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyGuSResult {
Solution(String),
Infeasible,
Timeout,
}
#[derive(Debug, Clone)]
pub struct SyGuSProblem {
pub function_name: String,
pub arguments: Vec<(String, String)>,
pub return_sort: String,
pub grammar: CFG,
pub constraint: String,
}
impl SyGuSProblem {
pub fn new(
function_name: impl Into<String>,
arguments: Vec<(String, String)>,
return_sort: impl Into<String>,
grammar: CFG,
constraint: impl Into<String>,
) -> Self {
Self {
function_name: function_name.into(),
arguments,
return_sort: return_sort.into(),
grammar,
constraint: constraint.into(),
}
}
pub fn arity(&self) -> usize {
self.arguments.len()
}
}
#[derive(Debug, Clone)]
pub struct ComponentSynthQuery {
pub target_sort: String,
pub spec: String,
pub inputs: Vec<(String, String)>,
}
impl ComponentSynthQuery {
pub fn new(
target_sort: impl Into<String>,
spec: impl Into<String>,
inputs: Vec<(String, String)>,
) -> Self {
Self {
target_sort: target_sort.into(),
spec: spec.into(),
inputs,
}
}
}
#[derive(Debug, Clone)]
pub struct TypeDirectedSynth {
pub depth_limit: usize,
pub attempts: usize,
}
impl TypeDirectedSynth {
pub fn new(depth_limit: usize) -> Self {
Self {
depth_limit,
attempts: 0,
}
}
pub fn synthesise(
&mut self,
ctx: &SynthContext,
goal_type: &SynthType,
depth: usize,
) -> Option<String> {
self.attempts += 1;
if depth > self.depth_limit {
return None;
}
let matches = ctx.matching(goal_type);
if let Some((name, _)) = matches.first() {
return Some(name.to_string());
}
match goal_type {
SynthType::Unit => Some("()".into()),
SynthType::Arrow(dom, cod) => {
let arg_name = format!("x{}", depth);
let mut new_ctx = ctx.clone();
new_ctx.add(arg_name.clone(), *dom.clone());
let body = self.synthesise(&new_ctx, cod, depth + 1)?;
Some(format!("fun {} -> {}", arg_name, body))
}
SynthType::Product(a, b) => {
let pa = self.synthesise(ctx, a, depth + 1)?;
let pb = self.synthesise(ctx, b, depth + 1)?;
Some(format!("({}, {})", pa, pb))
}
_ => None,
}
}
}