Skip to main content

ftml_solver/impls/
proving.rs

1use std::borrow::Cow;
2
3use ftml_ontology::terms::{
4    ApplicationTerm, Argument, ComponentVar, Term, Variable, eq::Alpha, helpers::IntoTerm,
5};
6use ftml_solver_trace::CheckingTask;
7use ftml_uris::{Id, SymbolUri};
8
9const MAX_DEPTH: usize = 8;
10
11use crate::{
12    CheckRef,
13    facts::{Fact, GlobalOrLocal, GoalPremise, LocalFacts},
14    hoas::HOASSymbols,
15    rules::ProofRule,
16    split::SplitStrategy,
17};
18
19#[derive(Default)]
20pub(crate) struct ProverState(
21    parking_lot::RwLock<ProverStateI>,
22    std::sync::atomic::AtomicUsize,
23);
24impl std::fmt::Debug for ProverState {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        self.0.read().fmt(f)
27    }
28}
29impl ProverState {
30    fn add_goal<Split: SplitStrategy>(
31        &self,
32        t: &Term,
33        checker: &CheckRef<'_, '_, Split>,
34    ) -> Result<Option<Term>, GoalId> {
35        let mut lock = self.0.write();
36        if let Some((i, (_, _))) = lock
37            .goals
38            .iter()
39            .enumerate()
40            .find(|(_, (e, _))| e.alpha_equal(t))
41        {
42            tracing::debug!("Already exists");
43            if let Some(d) = lock.get_solution(GoalId(i), checker) {
44                Ok(Some(d))
45            } else {
46                drop(lock);
47                // avoid recursion
48                Ok(None) //Err(GoalId(i))
49            }
50        } else {
51            tracing::debug!("New goal");
52            let len = lock.goals.len();
53            let mut strategies = Vec::new();
54            for (strat, premises) in checker.facts_for(t) {
55                let mut done = true;
56                let mut npremises = Vec::new();
57                for p in premises {
58                    if let Some((i, old)) = lock
59                        .premises
60                        .iter()
61                        .enumerate()
62                        .find(|(_, e)| e.matches_goal(&p, &mut Alpha::new()))
63                    {
64                        match old.proven() {
65                            Some(true) => {
66                                npremises.push(PremiseId(i));
67                            }
68                            Some(false) => return Ok(None),
69                            None => {
70                                done = false;
71                                npremises.push(PremiseId(i));
72                            }
73                        }
74                    } else {
75                        done = false;
76                        npremises.push(PremiseId(lock.premises.len()));
77                        lock.premises.push(p.into());
78                    }
79                }
80                if done {
81                    return Ok(
82                        lock.close_done(strat.into_term(checker.context.as_ref()), &npremises)
83                    );
84                }
85                let id = lock.strategies.len();
86                strategies.push(StrategyId(id));
87                lock.strategies.push((GoalId(len), strat, npremises));
88            }
89            if strategies.is_empty() {
90                Ok(None)
91            } else {
92                lock.goals.push((t.clone(), GoalState::Running(strategies)));
93                drop(lock);
94                Err(GoalId(len))
95            }
96        }
97    }
98}
99#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
100pub struct GoalId(usize);
101
102#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
103struct StrategyId(usize);
104
105#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
106struct PremiseId(usize);
107
108enum GoalState {
109    Running(Vec<StrategyId>),
110    //Done(Term),
111}
112
113#[derive(Default)]
114struct ProverStateI {
115    goals: Vec<(Term, GoalState)>,
116    strategies: Vec<(GoalId, GlobalOrLocal, Vec<PremiseId>)>,
117    premises: Vec<Premise>,
118}
119
120enum Premise {
121    Typing {
122        elem: Term,
123        tp: Term,
124        is_sequence: bool,
125        done: Option<bool>,
126    },
127    Proof {
128        goal: Term,
129        proof: Option<Option<Term>>,
130    },
131    NeedSuchThat {
132        name: Variable,
133        tp: Term,
134        is_sequence: bool,
135        premises: Vec<Self>,
136        found: Option<Term>,
137    },
138}
139impl From<GoalPremise> for Premise {
140    fn from(value: GoalPremise) -> Self {
141        match value {
142            GoalPremise::Typing {
143                elem,
144                tp,
145                is_sequence,
146            } => Self::Typing {
147                elem,
148                tp,
149                is_sequence,
150                done: None,
151            },
152            GoalPremise::Proof(t) => Self::Proof {
153                goal: t,
154                proof: None,
155            },
156            GoalPremise::NeedSuchThat {
157                name,
158                tp,
159                is_sequence,
160                premises,
161            } => Self::NeedSuchThat {
162                name: Variable::Name {
163                    name,
164                    notated: None,
165                },
166                tp,
167                is_sequence,
168                premises: premises.into_iter().map(Into::into).collect(),
169                found: None,
170            },
171        }
172    }
173}
174impl Premise {
175    fn proven(&self) -> Option<bool> {
176        match self {
177            Self::Typing { done, .. } => *done,
178            Self::Proof { proof, .. } => proof.as_ref().map(|_| true),
179            Self::NeedSuchThat { found, .. } => found.as_ref().map(|_| true),
180        }
181    }
182    fn matches_goal<'s>(&'s self, other: &'s GoalPremise, alpha: &mut Alpha<'s>) -> bool {
183        match (self, other) {
184            (
185                Self::Typing {
186                    elem,
187                    tp,
188                    is_sequence,
189                    ..
190                },
191                GoalPremise::Typing {
192                    elem: elem2,
193                    tp: tp2,
194                    is_sequence: is_sequence2,
195                },
196            ) => {
197                *is_sequence == *is_sequence2
198                    && elem2.alpha_equal_under(elem, alpha)
199                    && tp2.alpha_equal_under(tp, alpha)
200            }
201            (Self::Proof { goal, .. }, GoalPremise::Proof(goal2)) => {
202                goal2.alpha_equal_under(goal, alpha)
203            }
204            (
205                Self::NeedSuchThat {
206                    name,
207                    tp,
208                    is_sequence,
209                    premises,
210                    ..
211                },
212                GoalPremise::NeedSuchThat {
213                    name: name2,
214                    tp: tp2,
215                    is_sequence: is_sequence2,
216                    premises: premises2,
217                },
218            ) => {
219                *is_sequence == *is_sequence2 && tp2.alpha_equal_under(tp, alpha) && {
220                    alpha.push((name2.as_ref(), name));
221                    if premises2
222                        .iter()
223                        .all(|p2| premises.iter().any(|p| p.matches_goal(p2, alpha)))
224                    {
225                        alpha.pop();
226                        true
227                    } else {
228                        false
229                    }
230                }
231            }
232            _ => false,
233        }
234    }
235}
236
237impl ProverState {
238    /*pub fn new(hoas: &HOASSymbols) -> Self {
239        ProverState {
240            goals: Vec::new(), //subs: Vec::new(),
241                               //main: Vec::new(),
242                               //attempts: Vec::new(),
243                               //used: ctx.len(),
244        }
245    }*/
246    /*pub fn clean(&mut self, context: &[Cow<'_, ComponentVar>]) {
247        TODO
248    }*/
249}
250
251impl ProverStateI {
252    fn close_done(&self, head: Term, premises: &[PremiseId]) -> Option<Term> {
253        let Ok(args) = premises
254            .iter()
255            .filter_map(|i| {
256                let Some(p) = self.premises.get(i.0) else {
257                    return Some(Err(()));
258                };
259                match p {
260                    Premise::Typing {
261                        done: Some(true), ..
262                    } => None,
263                    Premise::Proof {
264                        proof: Some(Some(proof)),
265                        ..
266                    }
267                    | Premise::NeedSuchThat {
268                        found: Some(proof), ..
269                    } => Some(Ok(Argument::Simple(proof.clone()))),
270                    _ => Some(Err(())),
271                }
272            })
273            .collect::<Result<Vec<_>, ()>>()
274        else {
275            return None;
276        };
277        Some(if args.is_empty() {
278            head
279        } else {
280            Term::Application(ApplicationTerm::new(head, args.into_boxed_slice(), None))
281        })
282    }
283    fn get_solution<Split: SplitStrategy>(
284        &self,
285        id: GoalId,
286        checker: &CheckRef<Split>,
287    ) -> Option<Term> {
288        match &self.goals.get(id.0)?.1 {
289            //GoalState::Done(t) => Some(t.clone()),
290            GoalState::Running(v) => {
291                for strat in v {
292                    if let Some(t) = self.strategy_done(*strat, checker) {
293                        return Some(t);
294                    }
295                }
296                None
297            }
298        }
299    }
300    fn strategy_done<Split: SplitStrategy>(
301        &self,
302        id: StrategyId,
303        checker: &CheckRef<Split>,
304    ) -> Option<Term> {
305        let strat = self.strategies.get(id.0)?;
306        if strat.2.iter().all(|e| {
307            self.premises
308                .get(e.0)
309                .is_some_and(|e| e.proven() == Some(true))
310        }) {
311            self.close_done(
312                strat.1.clone().into_term(checker.context.as_ref()),
313                &strat.2,
314            )
315        } else {
316            None
317        }
318    }
319}
320
321impl<'t, Split: SplitStrategy> CheckRef<'t, '_, Split> {
322    pub fn prove(&mut self, goal: &'t Term) -> Option<Term> {
323        //self.untraced(CheckingTask::Proving(goal), |slf| slf.prove_i(goal))
324        self.wrap_check(CheckingTask::Proving(goal), |slf| slf.prove_i(goal))
325    }
326    pub(crate) fn prove_i(&mut self, goal: &'t Term) -> Option<Term> {
327        let judgment = self.top.hoas.as_ref()?.judgment.as_ref()?;
328        //tracing::debug!("Facts: {:#?}", self.facts);
329        //
330        let r = if let Some([Argument::Simple(goal)]) = goal.unapply(judgment) {
331            self.backchain(goal)
332        } else {
333            tracing::debug!("Does not match {judgment}: {:?}", goal.debug_short());
334            self.simplify_rules(
335                self.top.rules.proof(),
336                goal,
337                ProofRule::applicable,
338                |slf, rl, t| rl.prove(slf, t),
339            )
340        };
341        r.map(|t| self.subst(t))
342    }
343
344    fn backchain(&mut self, goal: &'t Term) -> Option<Term> {
345        tracing::debug!(
346            "Proving {:?}", // in context:\n{:#?}",
347            goal.debug_short(),
348            //self.context
349        );
350
351        //println!("{}", std::backtrace::Backtrace::force_capture());
352
353        let sgoal = self.simplify_full(true, goal);
354        let goal = sgoal.as_ref().unwrap_or(goal);
355
356        /*tracing::debug!(
357            "Facts: \n Global: {:#?}\n Local: {:#?}",
358            self.top.facts,
359            self.context.facts()
360        );*/
361
362        let proof_state = self.proof_state;
363        let goal_id = match proof_state.add_goal(goal, self) {
364            Ok(r) => return r,
365            Err(i) => i,
366        };
367        //tracing::warn!("Proof State: {proof_state:#?}");
368
369        let dp = proof_state
370            .1
371            .fetch_add(1, std::sync::atomic::Ordering::AcqRel);
372        if dp > MAX_DEPTH {
373            self.failure("exceeded maximum depth");
374            proof_state
375                .1
376                .fetch_sub(1, std::sync::atomic::Ordering::AcqRel);
377            return None;
378        }
379
380        let mut lock = proof_state.0.read();
381        loop {
382            let Some((i, next)) =
383                lock.premises.iter().enumerate().find(|(_, e)| {
384                    !matches!(*e, Premise::NeedSuchThat { .. }) && e.proven().is_none()
385                })
386            else {
387                drop(lock);
388                self.failure("No more premises");
389                //self.comment(format!("{proof_state:#?}"));
390                proof_state
391                    .1
392                    .fetch_sub(1, std::sync::atomic::Ordering::AcqRel);
393                return None;
394            };
395            let idx = PremiseId(i);
396            match next {
397                Premise::Typing {
398                    elem,
399                    tp,
400                    is_sequence,
401                    ..
402                } => {
403                    let tm = elem.clone();
404                    let tp = tp.clone();
405                    drop(lock);
406                    let r = self.scoped(|slf| slf.check_type(&tm, &tp)) == Some(true);
407                    let mut lock = proof_state.0.write();
408                    if let Some(Premise::Typing { done, .. }) = lock.premises.get_mut(idx.0) {
409                        *done = Some(r);
410                    }
411                    drop(lock);
412                }
413                Premise::Proof { goal, .. } => {
414                    let goal = goal.clone();
415                    drop(lock);
416                    let goal = self.top.hoas.as_ref()?.judgment.clone()?.apply_tms([goal]);
417                    let r = self.scoped(|slf| slf.prove(&goal));
418                    let mut lock = proof_state.0.write();
419                    if let Some(Premise::Proof { proof, .. }) = lock.premises.get_mut(idx.0) {
420                        *proof = Some(r);
421                    }
422                }
423                _ => todo!(),
424            }
425            lock = proof_state.0.read();
426            // lock is dropped!
427            if let Some(proof) = lock.get_solution(goal_id, self) {
428                proof_state
429                    .1
430                    .fetch_sub(1, std::sync::atomic::Ordering::AcqRel);
431                return Some(proof);
432            }
433
434            // TODO check if done
435        }
436
437        /*
438        let mut local_facts = LocalFacts::default();
439        for (i, c) in self.context.0.iter().enumerate() {
440            if let Some(tp) = c.tp.as_ref()
441                && let Some(fact) = Fact::from_tp(hoas, tp)
442            {
443                local_facts.facts.push((i, fact));
444            }
445        }
446
447        let mut state = ProofState {
448            local: local_facts,
449            subs: Vec::new(),
450            main: Vec::new(),
451            attempts: Vec::new(),
452        };
453        if !self.backchain_next(&mut state, goal) {
454            self.failure("No applicable strategies");
455            return None;
456        }
457        std::mem::swap(&mut state.main, &mut state.attempts);
458        tracing::warn!("Here: {state:#?}");
459
460        /*tracing::debug!(
461            "Facts: \n Global: {:#?}\n Local: {:#?}",
462            self.top.facts,
463            local_facts
464        );*/
465
466        None
467         */
468    }
469
470    /*
471    fn backchain_next(&mut self, state: &mut ProverState, next: &'t Term) -> bool {
472        let mut facts = Vec::new();
473        let Some(t) = self.simplify_until(next, |slf, t| {
474            facts = slf.facts_for(t).collect();
475            !facts.is_empty()
476        }) else {
477            self.failure("No applicable proof rules");
478            return false;
479        };
480        self.counter("Facts found", facts.len());
481        let mut added = false;
482        for (f, gls) in facts {
483            let mut changed = false;
484            let r = gls
485                .into_iter()
486                .map(|g| {
487                    if let Some(sg) = state.subs.iter().position(|e| *e == g) {
488                        sg
489                    } else {
490                        changed = true;
491                        state.subs.push(g.into());
492                        state.subs.len() - 1
493                    }
494                })
495                .collect();
496            if changed {
497                state.attempts.push(ProofAttempt { rf: f, subgoals: r });
498                added = true;
499            }
500            //tracing::warn!("{f}: {gls:?}");
501        }
502        added
503    }
504     */
505}
506
507impl std::fmt::Debug for ProverStateI {
508    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
509        let mut out = f.debug_struct("ProverState");
510        out.field("goals", &GoalStateDebug(&self.goals))
511            .field("strategies", &StrategiesDebug(&self.strategies))
512            .field("premises", &self.premises)
513            .finish()
514    }
515}
516impl std::fmt::Debug for Premise {
517    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
518        match self {
519            Self::Typing {
520                elem,
521                tp,
522                is_sequence,
523                done,
524            } => write!(
525                f,
526                "{:?}  :  {:?}{}  ({:?})",
527                elem.debug_short(),
528                tp.debug_short(),
529                if *is_sequence { "*" } else { "" },
530                done
531            ),
532            Self::Proof { goal, proof } => {
533                write!(
534                    f,
535                    "⊢ {:?}  :=  {:?}",
536                    goal.debug_short(),
537                    proof
538                        .as_ref()
539                        .and_then(|t| t.as_ref().map(Term::debug_short))
540                )
541            }
542            Self::NeedSuchThat {
543                name,
544                tp,
545                is_sequence,
546                premises,
547                found,
548            } => write!(
549                f,
550                "SOME {name} : {:?}{}  := {:?} {:?}",
551                tp.debug_short(),
552                if *is_sequence { "*" } else { "" },
553                found.as_ref().map(Term::debug_short),
554                premises
555            ),
556        }
557    }
558}
559
560struct GoalStateDebug<'s>(&'s [(Term, GoalState)]);
561impl std::fmt::Debug for GoalStateDebug<'_> {
562    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
563        f.debug_list().entries(self.0.iter().map(GSPair)).finish()
564    }
565}
566struct GSPair<'s>(&'s (Term, GoalState));
567impl std::fmt::Debug for GSPair<'_> {
568    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
569        write!(f, "{:?} {:?}", self.0.0.debug_short(), &self.0.1)
570    }
571}
572struct StrategiesDebug<'s>(&'s [(GoalId, GlobalOrLocal, Vec<PremiseId>)]);
573impl std::fmt::Debug for StrategiesDebug<'_> {
574    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
575        f.debug_list()
576            .entries(self.0.iter().map(StratLine))
577            .finish()
578    }
579}
580struct StratLine<'s>(&'s (GoalId, GlobalOrLocal, Vec<PremiseId>));
581
582impl std::fmt::Debug for StratLine<'_> {
583    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
584        write!(f, "{:?}: {} {:?}", self.0.0, self.0.1, self.0.2)
585    }
586}
587impl std::fmt::Debug for GoalState {
588    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
589        match self {
590            Self::Running(v) => v.fmt(f), //f.debug_tuple("Running").field(v).finish(),
591                                          //Self::Done(t) => t.debug_short().fmt(f),
592        }
593    }
594}
595impl std::fmt::Debug for GoalId {
596    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
597        self.0.fmt(f)
598    }
599}
600impl std::fmt::Debug for StrategyId {
601    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
602        self.0.fmt(f)
603    }
604}
605impl std::fmt::Debug for PremiseId {
606    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
607        self.0.fmt(f)
608    }
609}
610
611/*
612struct ProofAttempt {
613    rf: GlobalOrLocal,
614    subgoals: Vec<usize>,
615}
616impl std::fmt::Debug for ProofAttempt {
617    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
618        write!(f, "{} ({} subgoals)", self.rf, self.subgoals.len())
619    }
620}
621
622#[derive(Hash)]
623enum ProofProgress {
624    None,
625    InProgress(Vec<usize>),
626    Done(Term),
627}
628impl std::fmt::Debug for ProofProgress {
629    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
630        match self {
631            Self::None => f.write_str("not yet started"),
632            Self::InProgress(_) => f.write_str("attempted"),
633            Self::Done(t) => write!(f, "SUCCESS: {:?}", t.debug_short()),
634        }
635    }
636}
637
638enum SubgoalI {
639    Typing {
640        elem: Term,
641        tp: Term,
642        checked: Option<bool>,
643        is_sequence: bool,
644    },
645    Premise {
646        goal: Term,
647        found: ProofProgress,
648    },
649    Need {
650        name: Id,
651        tp: Term,
652        is_sequence: bool,
653        df: Option<Term>,
654    },
655}
656impl PartialEq<Subgoal> for SubgoalI {
657    fn eq(&self, other: &Subgoal) -> bool {
658        match (self, other) {
659            (
660                Self::Typing {
661                    elem,
662                    tp,
663                    is_sequence,
664                    ..
665                },
666                Subgoal::Typing {
667                    elem: e2,
668                    tp: t2,
669                    is_sequence: i2,
670                },
671            ) => alpha_equal(elem, e2) && alpha_equal(tp, t2) && is_sequence == i2,
672            (Self::Premise { goal, .. }, Subgoal::Premise(g2)) => alpha_equal(goal, g2),
673            (
674                Self::Need {
675                    name,
676                    tp,
677                    is_sequence,
678                    ..
679                },
680                Subgoal::Need {
681                    name: n2,
682                    tp: t2,
683                    is_sequence: i2,
684                },
685            ) => name == n2 && tp == t2 && is_sequence == i2,
686            _ => false,
687        }
688    }
689}
690impl From<Subgoal> for SubgoalI {
691    fn from(value: Subgoal) -> Self {
692        match value {
693            Subgoal::Typing {
694                elem,
695                tp,
696                is_sequence,
697            } => Self::Typing {
698                elem,
699                tp,
700                is_sequence,
701                checked: None,
702            },
703            Subgoal::Premise(tm) => Self::Premise {
704                goal: tm,
705                found: ProofProgress::None,
706            },
707            Subgoal::Need {
708                name,
709                tp,
710                is_sequence,
711            } => Self::Need {
712                name,
713                tp,
714                is_sequence,
715                df: None,
716            },
717        }
718    }
719}
720impl PartialEq for SubgoalI {
721    fn eq(&self, other: &Self) -> bool {
722        match (self, other) {
723            (
724                Self::Typing {
725                    elem,
726                    tp,
727                    is_sequence,
728                    ..
729                },
730                Self::Typing {
731                    elem: elem2,
732                    tp: tp2,
733                    is_sequence: is_sequence2,
734                    ..
735                },
736            ) => elem == elem2 && tp == tp2 && is_sequence == is_sequence2,
737            (Self::Premise { goal, .. }, Self::Premise { goal: goal2, .. }) => goal == goal2,
738            _ => false,
739        }
740    }
741}
742
743 */