synthphonia/backward/
liststr.rs

1
2
3use std::{collections::{HashMap, HashSet}, pin::pin};
4
5use simple_rc_async::task::{self, JoinHandle};
6
7use crate::{backward::str::HandleRcVec, closure, debg, expr::{cfg::Cfg, context::Context, ops::{self, Op1Enum}, Expr}, forward::executor::Executor, galloc::{self, AllocForAny, AllocForExactSizeIter}, never, solutions::new_thread_with_limit, utils::{select_ret, select_ret3, select_ret4}, value::Value};
8
9use super::{Deducer, Problem};
10
11#[derive(Debug)]
12/// Represents a deduction strategy for list transformation synthesis using an optional mapping configuration.
13/// 
14/// Encapsulates an index for the non-terminal symbol and an optional grammar configuration that may be used for map-based operations during list deduction.
15pub struct ListDeducer {
16    pub nt: usize,
17    pub map: Option<Cfg>,
18    pub filter: Option<Cfg>,
19}
20
21impl Deducer for ListDeducer {
22    /// Deduce a synthesis subproblem asynchronously by concurrently monitoring a task and mapping-related events to yield the resulting expression. 
23    /// 
24    /// 
25    /// This function triggers debugging output, acquires a task associated with the current subproblem, and sets up a listener that, if a mapping configuration is present, extends a collection of futures via a mapping operation. 
26    /// It then concurrently awaits multiple asynchronous futures and returns the selected synthesized expression based on the first completed event.
27    async fn deduce(&'static self, exec: &'static crate::forward::executor::Executor, prob: Problem) -> &'static crate::expr::Expr {
28        debg!("Deducing subproblem: {} {:?}", exec.cfg[self.nt].name, prob.value);
29        let task = exec.data[self.nt].all_eq.acquire(prob.value);
30
31        let futures = HandleRcVec::new();
32        let map_event = exec.data[self.nt].len().unwrap().listen_for_each(prob.value.length_inside().unwrap(), closure! { clone futures, clone prob; move |delimiter: Value| {
33                if self.map.is_some() {
34                    futures.extend_iter(self.map(exec, prob, delimiter).into_iter());
35                }
36                None::<&'static Expr>
37        }});
38        let filter_event = exec.data[self.nt].contains.as_ref().unwrap().listen_for_each(prob.value, closure! { clone futures, clone prob; move |list: Value| {
39            if self.filter.is_some() && subseq_test(prob.value, list) {
40                    futures.extend_iter(self.filter(exec, prob, list).into_iter());
41                }
42                None::<&'static Expr>
43        }});
44
45        select_ret4(pin!(map_event), pin!(filter_event), pin!(task), pin!(futures)).await
46    }
47}
48
49impl ListDeducer {
50    #[inline]
51    /// Deduce a map operation
52    pub fn map(&'static self, exec: &'static Executor, mut prob: Problem, list: Value) -> Option<JoinHandle<&'static Expr>> {
53        if prob.used_cost >= 6 { return None; }
54        let p = prob.value.to_liststr();
55        if p.iter().all(|x| x.len() <= 2) {  return None; }
56        let l = list.to_liststr();
57        assert!(p.iter().zip(l.iter()).all(|(a, b)| a.len() == b.len()));
58
59
60        let p = prob.value.flatten_leak();
61        let l = list.flatten_leak();
62        Some(task::spawn(async move {
63
64            let mut cfg = self.map.as_ref().unwrap().clone();
65            let ctx = Context::new(p.len(), vec![l.into()], vec![], p.into());
66            cfg.config.size_limit = 10;
67            cfg.config.time_limit = 1000;
68            let handle = new_thread_with_limit(cfg, ctx);
69            debg!("ListDeducer::map {:?} {:?} new thread {}", prob.value, list, handle.id());
70            let inner = exec.bridge.wait(handle).await;
71            let mut result = exec.data[prob.nt].all_eq.get(list);
72            Expr::Op1(Op1Enum::Map(ops::Map(Some(inner.alloc_local()))).galloc(), result).galloc()
73        }))
74    }
75    #[inline]
76    /// Deduce a filter operation
77    pub fn filter(&'static self, exec: &'static Executor, mut prob: Problem, list: Value) -> Option<JoinHandle<&'static Expr>> {
78        if prob.used_cost >= 6 { return None; }
79        let p = prob.value.to_liststr();
80        if p.iter().all(|x| x.len() <= 2) {  return None; }
81        let l = list.to_liststr();
82        let mut io = HashMap::<&'static str, bool>::new();
83        if exec.size() > 7 {  return None; }
84        
85        for (p, l) in p.iter().zip(l.iter()) {
86            for elem in l.iter() {
87                match io.entry(elem) {
88                    std::collections::hash_map::Entry::Occupied(o) => { if *o.get() != p.contains(elem) { return None } }
89                    std::collections::hash_map::Entry::Vacant(v) => {v.insert(p.contains(elem)); }
90                }
91            }
92        }
93        let outputs = io.iter().map(|(_, o)| *o).galloc_scollect();
94        let inputs = io.into_iter().map(|(i, _)| i).galloc_scollect();
95
96        Some(task::spawn(async move {
97            let mut cfg = self.filter.as_ref().unwrap().clone();
98            let ctx = Context::new(outputs.len(), vec![inputs.into()], vec![], outputs.into());
99            cfg.config.size_limit = 10;
100            cfg.config.time_limit = 1000;
101            let handle = new_thread_with_limit(cfg, ctx);
102            debg!("ListDeducer::filter {:?} {:?} {:?} {:?} new thread {}", p, l, inputs, outputs, handle.id());
103            let inner = exec.bridge.wait(handle).await;
104            let mut result = exec.data[prob.nt].all_eq.get(list);
105            Expr::Op1(Op1Enum::Filter(ops::Filter(Some(inner.alloc_local()))).galloc(), result).galloc()
106        }))
107    }
108}
109
110fn subseq_test(sublist: Value, list: Value) -> bool {
111    let Value::ListStr(sublist) = sublist else { return false; };
112    let Value::ListStr(list) = list else { return false; };
113    let mut set = HashSet::new();
114    for (s, l) in sublist.iter().zip(list.iter()) {
115        let mut sublist_id = 0;
116        for p in l.iter() {
117            if sublist_id < s.len() && *p == s[sublist_id] {
118                set.insert(s[sublist_id]);
119                sublist_id += 1;
120            } else if set.contains(*p) {
121                return false;
122            }
123        }
124        if sublist_id != s.len() {
125            return false;
126        }
127    }
128    
129    true
130}