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 */