1use std::collections::{HashMap, HashSet};
17
18use logicaffeine_kernel::{
19 double_check, infer_type, is_subtype, prelude::StandardLibrary, Context, DoubleCheck, Term,
20 Universe,
21};
22
23use crate::certifier::{certify, proof_expr_to_type, proof_expr_to_type_ctx, CertificationContext};
24use crate::{BackwardChainer, DerivationTree, ProofExpr, ProofTerm};
25
26pub struct VerifiedProof {
33 pub derivation: Option<DerivationTree>,
35 pub proof_term: Option<Term>,
37 pub kernel_ctx: Context,
40 pub verified: bool,
42 pub verification_error: Option<String>,
44}
45
46#[derive(Debug, Clone)]
54pub struct Definition {
55 pub name: String,
57 pub params: Vec<String>,
59 pub definiens: ProofExpr,
61}
62
63pub fn prove_certify_check(premises: &[ProofExpr], goal: &ProofExpr) -> VerifiedProof {
71 prove_certify_check_bounded(premises, goal, 100)
72}
73
74#[derive(Debug, Clone, PartialEq)]
78pub struct LibraryTheorem {
79 pub name: String,
80 pub premises: Vec<ProofExpr>,
81 pub goal: ProofExpr,
82 pub cites: Vec<String>,
84}
85
86pub struct LibraryResult {
88 pub name: String,
89 pub verified: bool,
90 pub verification_error: Option<String>,
91}
92
93pub(crate) fn citation_order(theorems: &[LibraryTheorem]) -> Vec<usize> {
99 use std::collections::HashSet;
100 let known: HashSet<&str> = theorems.iter().map(|t| t.name.as_str()).collect();
101 let mut placed: HashSet<usize> = HashSet::new();
102 let mut placed_names: HashSet<&str> = HashSet::new();
103 let mut order = Vec::with_capacity(theorems.len());
104 loop {
105 let mut progressed = false;
106 for (i, t) in theorems.iter().enumerate() {
107 if placed.contains(&i) {
108 continue;
109 }
110 let ready = t
111 .cites
112 .iter()
113 .all(|c| !known.contains(c.as_str()) || placed_names.contains(c.as_str()));
114 if ready {
115 order.push(i);
116 placed.insert(i);
117 placed_names.insert(t.name.as_str());
118 progressed = true;
119 }
120 }
121 if !progressed {
122 break;
123 }
124 }
125 for i in 0..theorems.len() {
126 if !placed.contains(&i) {
127 order.push(i);
128 }
129 }
130 order
131}
132
133pub fn prove_library(theorems: &[LibraryTheorem]) -> Vec<LibraryResult> {
139 prove_library_with_axioms(&[], theorems)
140}
141
142pub fn prove_library_with_axioms(
146 axioms: &[ProofExpr],
147 theorems: &[LibraryTheorem],
148) -> Vec<LibraryResult> {
149 use std::collections::HashMap;
150 let mut proved: HashMap<&str, &ProofExpr> = HashMap::new();
151 let mut by_name: HashMap<&str, LibraryResult> = HashMap::new();
152
153 for &i in &citation_order(theorems) {
154 let t = &theorems[i];
155 let mut premises = axioms.to_vec();
156 premises.extend(t.premises.iter().cloned());
157 for cite in &t.cites {
158 if let Some(goal) = proved.get(cite.as_str()) {
159 premises.push((*goal).clone());
160 }
161 }
162 let vp = prove_certify_check(&premises, &t.goal);
163 if vp.verified {
164 proved.insert(t.name.as_str(), &t.goal);
165 }
166 by_name.insert(
167 t.name.as_str(),
168 LibraryResult {
169 name: t.name.clone(),
170 verified: vp.verified,
171 verification_error: vp.verification_error,
172 },
173 );
174 }
175
176 theorems
177 .iter()
178 .map(|t| by_name.remove(t.name.as_str()).expect("every theorem produces a result"))
179 .collect()
180}
181
182pub fn prove_certify_check_with_defs(
184 premises: &[ProofExpr],
185 goal: &ProofExpr,
186 definitions: &[Definition],
187) -> VerifiedProof {
188 prove_certify_check_bounded_with_defs(premises, goal, definitions, 100)
189}
190
191pub fn prove_certify_check_bounded(
196 premises: &[ProofExpr],
197 goal: &ProofExpr,
198 max_depth: usize,
199) -> VerifiedProof {
200 prove_certify_check_bounded_with_defs(premises, goal, &[], max_depth)
201}
202
203pub fn prove_certify_check_bounded_with_defs(
210 premises: &[ProofExpr],
211 goal: &ProofExpr,
212 definitions: &[Definition],
213 max_depth: usize,
214) -> VerifiedProof {
215 on_big_stack(|| {
219 prove_certify_check_bounded_with_defs_inner(premises, goal, definitions, max_depth)
220 })
221}
222
223fn prove_certify_check_bounded_with_defs_inner(
224 premises: &[ProofExpr],
225 goal: &ProofExpr,
226 definitions: &[Definition],
227 max_depth: usize,
228) -> VerifiedProof {
229 let abstracted = abstract_definitions(definitions);
230 let definitions = &abstracted[..];
231 if let Err(e) = validate_definitions(definitions) {
232 return definition_error(e);
233 }
234
235 let expanded: Option<(Vec<ProofExpr>, ProofExpr)> = if definitions.is_empty() {
244 None
245 } else {
246 let defmap: HashMap<&str, &Definition> =
247 definitions.iter().map(|d| (d.name.as_str(), d)).collect();
248 let ep = premises
249 .iter()
250 .map(|p| expand_defs_in_expr(p, &defmap, 0))
251 .collect();
252 let eg = expand_defs_in_expr(goal, &defmap, 0);
253 Some((ep, eg))
254 };
255 let (search_premises, search_goal): (&[ProofExpr], &ProofExpr) = match &expanded {
256 Some((ep, eg)) => (ep, eg),
257 None => (premises, goal),
258 };
259
260 let (kernel_ctx, flat_premises, prepared_goal) =
263 prepare_ctx_with_defs(search_premises, search_goal, definitions);
264 let abstracted_goal = if expanded.is_none() {
268 prepared_goal
269 } else {
270 BackwardChainer::new().abstract_all_events(goal)
271 };
272
273 let mut engine = BackwardChainer::new();
275 engine.set_max_depth(max_depth);
276 for premise in &flat_premises {
277 engine.add_axiom(premise.clone());
278 }
279 let search_goal = engine.abstract_all_events(search_goal);
282 let derivation = match engine.prove(search_goal) {
283 Ok(d) => d,
284 Err(e) => {
285 return VerifiedProof {
286 derivation: None,
287 proof_term: None,
288 kernel_ctx,
289 verified: false,
290 verification_error: Some(format!("Proof search failed: {}", e)),
291 };
292 }
293 };
294
295 finish_check(kernel_ctx, &abstracted_goal, derivation)
296}
297
298pub fn check_derivation(
306 premises: &[ProofExpr],
307 goal: &ProofExpr,
308 derivation: DerivationTree,
309) -> VerifiedProof {
310 check_derivation_with_defs(premises, goal, &[], derivation)
311}
312
313pub fn check_derivation_with_defs(
317 premises: &[ProofExpr],
318 goal: &ProofExpr,
319 definitions: &[Definition],
320 derivation: DerivationTree,
321) -> VerifiedProof {
322 on_big_stack(|| check_derivation_with_defs_inner(premises, goal, definitions, derivation))
323}
324
325fn check_derivation_with_defs_inner(
326 premises: &[ProofExpr],
327 goal: &ProofExpr,
328 definitions: &[Definition],
329 derivation: DerivationTree,
330) -> VerifiedProof {
331 let abstracted = abstract_definitions(definitions);
332 let definitions = &abstracted[..];
333 if let Err(e) = validate_definitions(definitions) {
334 return definition_error(e);
335 }
336 let (kernel_ctx, _flat_premises, abstracted_goal) =
337 prepare_ctx_with_defs(premises, goal, definitions);
338 finish_check(kernel_ctx, &abstracted_goal, derivation)
339}
340
341fn prepare_ctx_with_defs(
347 premises: &[ProofExpr],
348 goal: &ProofExpr,
349 definitions: &[Definition],
350) -> (Context, Vec<ProofExpr>, ProofExpr) {
351 let mut kernel_ctx = Context::new();
352 StandardLibrary::register(&mut kernel_ctx);
353
354 for def in definitions {
360 register_definition(&mut kernel_ctx, def);
361 }
362
363 fn split_conjuncts(expr: &ProofExpr, out: &mut Vec<ProofExpr>) {
371 if let ProofExpr::And(l, r) = expr {
372 split_conjuncts(l, out);
373 split_conjuncts(r, out);
374 } else {
375 out.push(expr.clone());
376 }
377 }
378 let engine_for_abstraction = BackwardChainer::new();
379 let mut flat_premises = Vec::new();
380 for premise in premises {
381 let abstracted = engine_for_abstraction.abstract_all_events(premise);
382 split_conjuncts(&expand_iff(&abstracted), &mut flat_premises);
386 }
387 let abstracted_goal = engine_for_abstraction.abstract_all_events(goal);
388
389 let mut collector = SymbolCollector::new();
391 for premise in &flat_premises {
392 collector.collect(premise);
393 }
394 collector.collect(&abstracted_goal);
395 for def in definitions {
400 collector.collect(&def.definiens);
401 }
402 for (name, ind) in collector.inductive_predicates() {
406 if kernel_ctx.get_global(name).is_none() {
407 kernel_ctx.add_declaration(
408 name,
409 Term::Pi {
410 param: "_".to_string(),
411 param_type: Box::new(Term::Global(ind.to_string())),
412 body_type: Box::new(Term::Sort(Universe::Prop)),
413 },
414 );
415 }
416 }
417 for (name, arity) in collector.predicates() {
418 register_predicate(&mut kernel_ctx, name, arity);
419 }
420 for (name, arity) in collector.functions() {
421 register_function(&mut kernel_ctx, name, arity);
422 }
423 for name in collector.modalities() {
426 register_modality(&mut kernel_ctx, name);
427 }
428 for name in collector.int_atoms() {
431 if kernel_ctx.get_global(name).is_none() {
432 kernel_ctx.add_declaration(name, Term::Global("Int".to_string()));
433 }
434 }
435 let param_names: HashSet<&str> = definitions
438 .iter()
439 .flat_map(|d| d.params.iter().map(String::as_str))
440 .collect();
441 for name in collector.constants() {
442 if param_names.contains(name.as_str()) {
443 continue;
444 }
445 register_constant(&mut kernel_ctx, name);
446 }
447
448 for (i, premise) in flat_premises.iter().enumerate() {
449 if let Ok(hyp_type) = proof_expr_to_type_ctx(premise, &kernel_ctx) {
450 let hyp_name = format!("h{}", i + 1);
451 kernel_ctx.add_declaration(&hyp_name, hyp_type);
452 }
453 }
454
455 for (i, premise) in premises.iter().enumerate() {
460 let whole = expand_iff(&engine_for_abstraction.abstract_all_events(premise));
461 if matches!(whole, ProofExpr::And(_, _)) {
462 if let Ok(hyp_type) = proof_expr_to_type(&whole) {
463 kernel_ctx.add_declaration(&format!("hw{}", i + 1), hyp_type);
464 }
465 }
466 }
467
468 (kernel_ctx, flat_premises, abstracted_goal)
469}
470
471pub(crate) fn on_big_stack<T: Send>(f: impl FnOnce() -> T + Send) -> T {
480 #[cfg(target_arch = "wasm32")]
485 {
486 f()
487 }
488 #[cfg(not(target_arch = "wasm32"))]
489 {
490 std::thread::scope(|s| {
491 std::thread::Builder::new()
492 .stack_size(32 * 1024 * 1024)
493 .spawn_scoped(s, f)
494 .expect("spawn proof thread")
495 .join()
496 .expect("proof thread panicked")
497 })
498 }
499}
500
501fn finish_check(
502 kernel_ctx: Context,
503 abstracted_goal: &ProofExpr,
504 derivation: DerivationTree,
505) -> VerifiedProof {
506 let trace = std::env::var("LOGOS_TRACE").is_ok();
507 let t_cert = trace.then(std::time::Instant::now);
509 let proof_term = {
510 let cert_ctx = CertificationContext::new(&kernel_ctx);
511 match certify(&derivation, &cert_ctx) {
512 Ok(t) => t,
513 Err(e) => {
514 return VerifiedProof {
515 derivation: Some(derivation),
516 proof_term: None,
517 kernel_ctx,
518 verified: false,
519 verification_error: Some(format!("Certification failed: {}", e)),
520 };
521 }
522 }
523 };
524 if let Some(t_cert) = t_cert {
525 eprintln!(
526 "[cert] certify(build) {:.2?} → {} kernel-term nodes",
527 t_cert.elapsed(),
528 count_term_nodes(&proof_term)
529 );
530 }
531
532 let t_infer = trace.then(std::time::Instant::now);
538 let inferred = match infer_type(&kernel_ctx, &proof_term) {
539 Ok(t) => t,
540 Err(e) => {
541 return VerifiedProof {
542 derivation: Some(derivation),
543 proof_term: None,
544 kernel_ctx,
545 verified: false,
546 verification_error: Some(format!("Type check failed: {}", e)),
547 };
548 }
549 };
550 if let Some(t_infer) = t_infer {
551 eprintln!("[cert] infer_type(check) {:.2?}", t_infer.elapsed());
552 }
553
554 let goal_type = match proof_expr_to_type_ctx(abstracted_goal, &kernel_ctx) {
555 Ok(t) => t,
556 Err(e) => {
557 return VerifiedProof {
558 derivation: Some(derivation),
559 proof_term: None,
560 kernel_ctx,
561 verified: false,
562 verification_error: Some(format!(
563 "Cannot express the goal as a kernel type: {}",
564 e
565 )),
566 };
567 }
568 };
569
570 if !is_subtype(&kernel_ctx, &inferred, &goal_type) {
571 return VerifiedProof {
572 derivation: Some(derivation),
573 proof_term: None,
574 kernel_ctx,
575 verified: false,
576 verification_error: Some(format!(
577 "Proof term proves a different proposition: inferred {:?}, goal {:?}",
578 inferred, goal_type
579 )),
580 };
581 }
582
583 if let DoubleCheck::Disagree(why) = double_check(&kernel_ctx, &proof_term) {
591 return VerifiedProof {
592 derivation: Some(derivation),
593 proof_term: None,
594 kernel_ctx,
595 verified: false,
596 verification_error: Some(format!("Independent re-checker disagreement: {}", why)),
597 };
598 }
599
600 VerifiedProof {
601 derivation: Some(derivation),
602 proof_term: Some(proof_term),
603 kernel_ctx,
604 verified: true,
605 verification_error: None,
606 }
607}
608
609pub struct ConflictReport {
616 pub inconsistent: bool,
619 pub proof_term: Option<Term>,
621 pub kernel_ctx: Context,
623 pub conflicting_premises: Vec<usize>,
625 pub error: Option<String>,
628}
629
630pub fn detect_conflict(premises: &[ProofExpr]) -> ConflictReport {
638 let falsum = ProofExpr::Atom("⊥".to_string());
639 let outcome = prove_certify_check(premises, &falsum);
640
641 if !outcome.verified {
642 return ConflictReport {
643 inconsistent: false,
644 proof_term: None,
645 kernel_ctx: outcome.kernel_ctx,
646 conflicting_premises: Vec::new(),
647 error: outcome.verification_error,
648 };
649 }
650
651 let mut used: Vec<usize> = Vec::new();
655 if let Some(derivation) = &outcome.derivation {
656 let mut leaves: Vec<&ProofExpr> = Vec::new();
657 collect_premise_leaves(derivation, &mut leaves);
658 for (i, premise) in premises.iter().enumerate() {
659 if leaves.iter().any(|leaf| *leaf == premise) && !used.contains(&i) {
660 used.push(i);
661 }
662 }
663 }
664
665 ConflictReport {
666 inconsistent: true,
667 proof_term: outcome.proof_term,
668 kernel_ctx: outcome.kernel_ctx,
669 conflicting_premises: used,
670 error: None,
671 }
672}
673
674fn collect_premise_leaves<'a>(tree: &'a DerivationTree, out: &mut Vec<&'a ProofExpr>) {
677 if matches!(tree.rule, crate::InferenceRule::PremiseMatch) {
678 out.push(&tree.conclusion);
679 }
680 for premise in &tree.premises {
681 collect_premise_leaves(premise, out);
682 }
683}
684
685struct SymbolCollector {
694 predicates: HashMap<String, usize>,
695 functions: HashMap<String, usize>,
696 constants: HashSet<String>,
697 weak_constants: HashSet<String>,
704 variables: HashSet<String>,
708 int_atoms: HashSet<String>,
711 inductive_predicates: HashMap<String, &'static str>,
716 modalities: HashSet<String>,
719}
720
721impl SymbolCollector {
722 fn new() -> Self {
723 SymbolCollector {
724 predicates: HashMap::new(),
725 functions: HashMap::new(),
726 constants: HashSet::new(),
727 weak_constants: HashSet::new(),
728 variables: HashSet::new(),
729 int_atoms: HashSet::new(),
730 inductive_predicates: HashMap::new(),
731 modalities: HashSet::new(),
732 }
733 }
734
735 fn mark_int(&mut self, term: &ProofTerm) {
738 match term {
739 ProofTerm::Constant(s) if s.parse::<i64>().is_ok() => {}
741 ProofTerm::Constant(s) | ProofTerm::Variable(s) | ProofTerm::BoundVarRef(s) => {
742 self.int_atoms.insert(s.clone());
743 }
744 ProofTerm::Function(name, args)
745 if matches!(name.as_str(), "add" | "sub" | "mul" | "div" | "mod") =>
746 {
747 for a in args {
748 self.mark_int(a);
749 }
750 }
751 _ => {}
752 }
753 }
754
755 fn note_predicate(&mut self, name: &str, arity: usize) {
756 self.predicates
757 .entry(name.to_string())
758 .and_modify(|a| *a = (*a).max(arity))
759 .or_insert(arity);
760 }
761
762 fn note_function(&mut self, name: &str, arity: usize) {
767 self.functions
768 .entry(name.to_string())
769 .and_modify(|a| *a = (*a).max(arity))
770 .or_insert(arity);
771 }
772
773 fn collect(&mut self, expr: &ProofExpr) {
774 match expr {
775 ProofExpr::Predicate { name, args, .. } => {
776 self.note_predicate(name, args.len());
777 if args.len() == 1 {
780 if let Some(ind) = constructor_domain(&args[0]) {
781 self.inductive_predicates.insert(name.clone(), ind);
782 }
783 }
784 for arg in args {
785 self.collect_term(arg);
786 }
787 }
788 ProofExpr::And(l, r)
789 | ProofExpr::Or(l, r)
790 | ProofExpr::Implies(l, r)
791 | ProofExpr::Iff(l, r) => {
792 self.collect(l);
793 self.collect(r);
794 }
795 ProofExpr::Not(inner) => self.collect(inner),
796 ProofExpr::ForAll { variable, body } | ProofExpr::Exists { variable, body } => {
797 self.variables.insert(variable.clone());
798 self.collect(body);
799 }
800 ProofExpr::Temporal { operator, body } => {
803 self.modalities.insert(operator.clone());
804 self.collect(body);
805 }
806 ProofExpr::Identity(l, r) => {
807 self.collect_term(l);
808 self.collect_term(r);
809 }
810 _ => {}
812 }
813 }
814
815 fn collect_term(&mut self, term: &ProofTerm) {
816 match term {
817 ProofTerm::Constant(name) => {
818 if name
825 .chars()
826 .next()
827 .map(|c| c.is_uppercase() || c.is_ascii_digit())
828 .unwrap_or(false)
829 {
830 self.constants.insert(name.clone());
831 } else {
832 self.weak_constants.insert(name.clone());
833 }
834 }
835 ProofTerm::Variable(name) | ProofTerm::BoundVarRef(name) => {
836 self.variables.insert(name.clone());
837 }
838 ProofTerm::Function(name, args) => {
839 if matches!(
843 name.as_str(),
844 "le" | "lt" | "ge" | "gt" | "add" | "sub" | "mul" | "div" | "mod"
845 ) {
846 for arg in args {
847 self.mark_int(arg);
848 }
849 } else {
850 self.note_function(name, args.len());
851 }
852 for arg in args {
853 self.collect_term(arg);
854 }
855 }
856 _ => {}
857 }
858 }
859
860 fn predicates(&self) -> impl Iterator<Item = (&String, usize)> {
861 self.predicates.iter().map(|(n, a)| (n, *a))
862 }
863
864 fn functions(&self) -> impl Iterator<Item = (&String, usize)> {
865 self.functions.iter().map(|(n, a)| (n, *a))
866 }
867
868 fn int_atoms(&self) -> impl Iterator<Item = &String> {
869 self.int_atoms.iter()
870 }
871
872 fn inductive_predicates(&self) -> impl Iterator<Item = (&String, &'static str)> {
873 self.inductive_predicates.iter().map(|(n, d)| (n, *d))
874 }
875
876 fn constants(&self) -> Vec<&String> {
877 self.constants
880 .iter()
881 .chain(
882 self.weak_constants
883 .iter()
884 .filter(|n| !self.variables.contains(*n)),
885 )
886 .collect()
887 }
888
889 fn modalities(&self) -> impl Iterator<Item = &String> {
890 self.modalities.iter()
891 }
892}
893
894fn constructor_domain(t: &ProofTerm) -> Option<&'static str> {
901 let name = match t {
902 ProofTerm::Constant(s) => s.as_str(),
903 ProofTerm::Function(n, _) => n.as_str(),
904 _ => return None,
905 };
906 match name {
907 "Zero" | "Succ" => Some("Nat"),
908 "ENil" | "ECons" => Some("EList"),
909 _ => None,
910 }
911}
912
913#[doc(hidden)]
916fn count_term_nodes(t: &Term) -> usize {
917 match t {
918 Term::App(f, a) => 1 + count_term_nodes(f) + count_term_nodes(a),
919 Term::Pi { param_type, body_type, .. } => 1 + count_term_nodes(param_type) + count_term_nodes(body_type),
920 Term::Lambda { param_type, body, .. } => 1 + count_term_nodes(param_type) + count_term_nodes(body),
921 Term::Match { discriminant, motive, cases } => {
922 1 + count_term_nodes(discriminant) + count_term_nodes(motive)
923 + cases.iter().map(count_term_nodes).sum::<usize>()
924 }
925 Term::Fix { body, .. } => 1 + count_term_nodes(body),
926 _ => 1,
927 }
928}
929
930fn register_predicate(ctx: &mut Context, name: &str, arity: usize) {
931 if ctx.get_global(name).is_some() {
932 return;
933 }
934 let mut ty = Term::Sort(Universe::Prop);
935 for _ in 0..arity {
936 ty = Term::Pi {
937 param: "_".to_string(),
938 param_type: Box::new(Term::Global("Entity".to_string())),
939 body_type: Box::new(ty),
940 };
941 }
942 ctx.add_declaration(name, ty);
943}
944
945fn register_function(ctx: &mut Context, name: &str, arity: usize) {
949 if ctx.get_global(name).is_some() {
950 return;
951 }
952 let mut ty = Term::Global("Entity".to_string());
953 for _ in 0..arity {
954 ty = Term::Pi {
955 param: "_".to_string(),
956 param_type: Box::new(Term::Global("Entity".to_string())),
957 body_type: Box::new(ty),
958 };
959 }
960 ctx.add_declaration(name, ty);
961}
962
963fn register_constant(ctx: &mut Context, name: &str) {
965 if ctx.get_global(name).is_some() {
966 return;
967 }
968 ctx.add_declaration(name, Term::Global("Entity".to_string()));
969}
970
971fn register_modality(ctx: &mut Context, name: &str) {
975 if ctx.get_global(name).is_some() {
976 return;
977 }
978 ctx.add_declaration(
979 name,
980 Term::Pi {
981 param: "_".to_string(),
982 param_type: Box::new(Term::Sort(Universe::Prop)),
983 body_type: Box::new(Term::Sort(Universe::Prop)),
984 },
985 );
986}
987
988fn definition_error(message: String) -> VerifiedProof {
990 VerifiedProof {
991 derivation: None,
992 proof_term: None,
993 kernel_ctx: Context::new(),
994 verified: false,
995 verification_error: Some(message),
996 }
997}
998
999fn abstract_definitions(definitions: &[Definition]) -> Vec<Definition> {
1005 let abstractor = BackwardChainer::new();
1006 definitions
1007 .iter()
1008 .map(|d| Definition {
1009 name: d.name.clone(),
1010 params: d.params.clone(),
1011 definiens: abstractor.abstract_all_events(&d.definiens),
1012 })
1013 .collect()
1014}
1015
1016fn validate_definitions(definitions: &[Definition]) -> Result<(), String> {
1025 if definitions.is_empty() {
1026 return Ok(());
1027 }
1028 for def in definitions {
1029 if let Err(e) = proof_expr_to_type(&def.definiens) {
1030 return Err(format!("cannot lower definition `{}`: {:?}", def.name, e));
1031 }
1032 }
1033 if let Some(cycle) = find_definition_cycle(definitions) {
1036 return Err(format!(
1037 "circular definition among {{{}}}: a definition may not recursively refer \
1038 to itself, directly or transitively",
1039 cycle.join(", ")
1040 ));
1041 }
1042 Ok(())
1043}
1044
1045fn register_definition(ctx: &mut Context, def: &Definition) {
1052 if ctx.get_global(&def.name).is_some() {
1053 return;
1054 }
1055 let mut body = match proof_expr_to_type(&def.definiens) {
1056 Ok(b) => b,
1057 Err(_) => {
1058 register_predicate(ctx, &def.name, def.params.len());
1061 return;
1062 }
1063 };
1064 for p in &def.params {
1065 body = subst_global_to_var(body, p);
1066 }
1067 for p in def.params.iter().rev() {
1069 body = Term::Lambda {
1070 param: p.clone(),
1071 param_type: Box::new(Term::Global("Entity".to_string())),
1072 body: Box::new(body),
1073 };
1074 }
1075 let mut ty = Term::Sort(Universe::Prop);
1076 for _ in &def.params {
1077 ty = Term::Pi {
1078 param: "_".to_string(),
1079 param_type: Box::new(Term::Global("Entity".to_string())),
1080 body_type: Box::new(ty),
1081 };
1082 }
1083 ctx.add_definition(def.name.clone(), ty, body);
1084}
1085
1086fn subst_global_to_var(term: Term, name: &str) -> Term {
1090 match term {
1091 Term::Global(n) => {
1092 if n == name {
1093 Term::Var(n)
1094 } else {
1095 Term::Global(n)
1096 }
1097 }
1098 Term::App(f, a) => Term::App(
1099 Box::new(subst_global_to_var(*f, name)),
1100 Box::new(subst_global_to_var(*a, name)),
1101 ),
1102 Term::Pi { param, param_type, body_type } => Term::Pi {
1103 param,
1104 param_type: Box::new(subst_global_to_var(*param_type, name)),
1105 body_type: Box::new(subst_global_to_var(*body_type, name)),
1106 },
1107 Term::Lambda { param, param_type, body } => Term::Lambda {
1108 param,
1109 param_type: Box::new(subst_global_to_var(*param_type, name)),
1110 body: Box::new(subst_global_to_var(*body, name)),
1111 },
1112 Term::Match { discriminant, motive, cases } => Term::Match {
1113 discriminant: Box::new(subst_global_to_var(*discriminant, name)),
1114 motive: Box::new(subst_global_to_var(*motive, name)),
1115 cases: cases.into_iter().map(|c| subst_global_to_var(c, name)).collect(),
1116 },
1117 Term::Fix { name: fix_name, body } => Term::Fix {
1118 name: fix_name,
1119 body: Box::new(subst_global_to_var(*body, name)),
1120 },
1121 other => other,
1122 }
1123}
1124
1125fn collect_defined_predicates(expr: &ProofExpr, defined: &HashSet<&str>, out: &mut Vec<String>) {
1130 match expr {
1131 ProofExpr::Predicate { name, .. } => {
1132 if defined.contains(name.as_str()) && !out.iter().any(|n| n == name) {
1133 out.push(name.clone());
1134 }
1135 }
1136 ProofExpr::And(l, r)
1137 | ProofExpr::Or(l, r)
1138 | ProofExpr::Implies(l, r)
1139 | ProofExpr::Iff(l, r) => {
1140 collect_defined_predicates(l, defined, out);
1141 collect_defined_predicates(r, defined, out);
1142 }
1143 ProofExpr::Not(p) => collect_defined_predicates(p, defined, out),
1144 ProofExpr::ForAll { body, .. } | ProofExpr::Exists { body, .. } => {
1145 collect_defined_predicates(body, defined, out)
1146 }
1147 _ => {}
1148 }
1149}
1150
1151fn def_edges(definitions: &[Definition]) -> Vec<(String, Vec<String>)> {
1154 let defined: HashSet<&str> = definitions.iter().map(|d| d.name.as_str()).collect();
1155 definitions
1156 .iter()
1157 .map(|d| {
1158 let mut uses = Vec::new();
1159 collect_defined_predicates(&d.definiens, &defined, &mut uses);
1160 (d.name.clone(), uses)
1161 })
1162 .collect()
1163}
1164
1165fn find_definition_cycle(definitions: &[Definition]) -> Option<Vec<String>> {
1170 let edges = def_edges(definitions);
1171 let adj: HashMap<&str, &[String]> =
1172 edges.iter().map(|(n, u)| (n.as_str(), u.as_slice())).collect();
1173
1174 let mut indeg: HashMap<&str, usize> = adj.keys().map(|n| (*n, 0usize)).collect();
1175 for deps in adj.values() {
1176 for d in deps.iter() {
1177 if let Some(c) = indeg.get_mut(d.as_str()) {
1178 *c += 1;
1179 }
1180 }
1181 }
1182
1183 let mut queue: Vec<&str> = indeg
1184 .iter()
1185 .filter(|(_, &c)| c == 0)
1186 .map(|(n, _)| *n)
1187 .collect();
1188 let mut removed = 0usize;
1189 while let Some(n) = queue.pop() {
1190 removed += 1;
1191 if let Some(deps) = adj.get(n) {
1192 for d in deps.iter() {
1193 if let Some(c) = indeg.get_mut(d.as_str()) {
1194 *c -= 1;
1195 if *c == 0 {
1196 queue.push(d.as_str());
1197 }
1198 }
1199 }
1200 }
1201 }
1202
1203 if removed == adj.len() {
1204 None
1205 } else {
1206 let mut cyclic: Vec<String> = indeg
1207 .iter()
1208 .filter(|(_, &c)| c > 0)
1209 .map(|(n, _)| n.to_string())
1210 .collect();
1211 cyclic.sort();
1212 Some(cyclic)
1213 }
1214}
1215
1216#[derive(Debug, Clone, Default)]
1221pub struct DependencyGraph {
1222 pub def_uses: Vec<(String, Vec<String>)>,
1224 pub theorem_uses: Vec<String>,
1226}
1227
1228pub fn dependency_graph(
1232 definitions: &[Definition],
1233 premises: &[ProofExpr],
1234 goal: &ProofExpr,
1235) -> DependencyGraph {
1236 let defined: HashSet<&str> = definitions.iter().map(|d| d.name.as_str()).collect();
1237 let def_uses = def_edges(definitions);
1238 let mut theorem_uses = Vec::new();
1239 for p in premises {
1240 collect_defined_predicates(p, &defined, &mut theorem_uses);
1241 }
1242 collect_defined_predicates(goal, &defined, &mut theorem_uses);
1243 DependencyGraph {
1244 def_uses,
1245 theorem_uses,
1246 }
1247}
1248
1249const MAX_EXPANSION_DEPTH: usize = 64;
1253
1254fn expand_defs_in_expr(
1260 expr: &ProofExpr,
1261 defs: &HashMap<&str, &Definition>,
1262 depth: usize,
1263) -> ProofExpr {
1264 if depth >= MAX_EXPANSION_DEPTH {
1265 return expr.clone();
1266 }
1267 match expr {
1268 ProofExpr::Predicate { name, args, .. } => {
1269 if let Some(def) = defs.get(name.as_str()) {
1270 if def.params.len() == args.len() {
1271 let substituted = substitute_params(&def.definiens, &def.params, args);
1272 return expand_defs_in_expr(&substituted, defs, depth + 1);
1273 }
1274 }
1275 expr.clone()
1276 }
1277 ProofExpr::And(l, r) => ProofExpr::And(
1278 Box::new(expand_defs_in_expr(l, defs, depth)),
1279 Box::new(expand_defs_in_expr(r, defs, depth)),
1280 ),
1281 ProofExpr::Or(l, r) => ProofExpr::Or(
1282 Box::new(expand_defs_in_expr(l, defs, depth)),
1283 Box::new(expand_defs_in_expr(r, defs, depth)),
1284 ),
1285 ProofExpr::Implies(l, r) => ProofExpr::Implies(
1286 Box::new(expand_defs_in_expr(l, defs, depth)),
1287 Box::new(expand_defs_in_expr(r, defs, depth)),
1288 ),
1289 ProofExpr::Iff(l, r) => ProofExpr::Iff(
1290 Box::new(expand_defs_in_expr(l, defs, depth)),
1291 Box::new(expand_defs_in_expr(r, defs, depth)),
1292 ),
1293 ProofExpr::Not(p) => ProofExpr::Not(Box::new(expand_defs_in_expr(p, defs, depth))),
1294 ProofExpr::ForAll { variable, body } => ProofExpr::ForAll {
1295 variable: variable.clone(),
1296 body: Box::new(expand_defs_in_expr(body, defs, depth)),
1297 },
1298 ProofExpr::Exists { variable, body } => ProofExpr::Exists {
1299 variable: variable.clone(),
1300 body: Box::new(expand_defs_in_expr(body, defs, depth)),
1301 },
1302 other => other.clone(),
1303 }
1304}
1305
1306fn substitute_params(expr: &ProofExpr, params: &[String], args: &[ProofTerm]) -> ProofExpr {
1309 match expr {
1310 ProofExpr::Predicate { name, args: pargs, world } => ProofExpr::Predicate {
1311 name: name.clone(),
1312 args: pargs.iter().map(|t| subst_term(t, params, args)).collect(),
1313 world: world.clone(),
1314 },
1315 ProofExpr::And(l, r) => ProofExpr::And(
1316 Box::new(substitute_params(l, params, args)),
1317 Box::new(substitute_params(r, params, args)),
1318 ),
1319 ProofExpr::Or(l, r) => ProofExpr::Or(
1320 Box::new(substitute_params(l, params, args)),
1321 Box::new(substitute_params(r, params, args)),
1322 ),
1323 ProofExpr::Implies(l, r) => ProofExpr::Implies(
1324 Box::new(substitute_params(l, params, args)),
1325 Box::new(substitute_params(r, params, args)),
1326 ),
1327 ProofExpr::Iff(l, r) => ProofExpr::Iff(
1328 Box::new(substitute_params(l, params, args)),
1329 Box::new(substitute_params(r, params, args)),
1330 ),
1331 ProofExpr::Not(p) => ProofExpr::Not(Box::new(substitute_params(p, params, args))),
1332 ProofExpr::ForAll { variable, body } => ProofExpr::ForAll {
1333 variable: variable.clone(),
1334 body: Box::new(substitute_params(body, params, args)),
1335 },
1336 ProofExpr::Exists { variable, body } => ProofExpr::Exists {
1337 variable: variable.clone(),
1338 body: Box::new(substitute_params(body, params, args)),
1339 },
1340 ProofExpr::Identity(l, r) => {
1341 ProofExpr::Identity(subst_term(l, params, args), subst_term(r, params, args))
1342 }
1343 other => other.clone(),
1344 }
1345}
1346
1347fn expand_iff(expr: &ProofExpr) -> ProofExpr {
1352 match expr {
1353 ProofExpr::Iff(p, q) => {
1354 let p = expand_iff(p);
1355 let q = expand_iff(q);
1356 ProofExpr::And(
1357 Box::new(ProofExpr::Implies(Box::new(p.clone()), Box::new(q.clone()))),
1358 Box::new(ProofExpr::Implies(Box::new(q), Box::new(p))),
1359 )
1360 }
1361 ProofExpr::And(l, r) => {
1362 ProofExpr::And(Box::new(expand_iff(l)), Box::new(expand_iff(r)))
1363 }
1364 ProofExpr::Or(l, r) => ProofExpr::Or(Box::new(expand_iff(l)), Box::new(expand_iff(r))),
1365 ProofExpr::Implies(l, r) => {
1366 ProofExpr::Implies(Box::new(expand_iff(l)), Box::new(expand_iff(r)))
1367 }
1368 ProofExpr::Not(p) => ProofExpr::Not(Box::new(expand_iff(p))),
1369 ProofExpr::ForAll { variable, body } => ProofExpr::ForAll {
1370 variable: variable.clone(),
1371 body: Box::new(expand_iff(body)),
1372 },
1373 ProofExpr::Exists { variable, body } => ProofExpr::Exists {
1374 variable: variable.clone(),
1375 body: Box::new(expand_iff(body)),
1376 },
1377 other => other.clone(),
1378 }
1379}
1380
1381fn subst_term(term: &ProofTerm, params: &[String], args: &[ProofTerm]) -> ProofTerm {
1386 match term {
1387 ProofTerm::Constant(n) => match params.iter().position(|p| p == n) {
1388 Some(i) => args[i].clone(),
1389 None => term.clone(),
1390 },
1391 ProofTerm::Function(name, fargs) => ProofTerm::Function(
1392 name.clone(),
1393 fargs.iter().map(|t| subst_term(t, params, args)).collect(),
1394 ),
1395 ProofTerm::Group(ts) => {
1396 ProofTerm::Group(ts.iter().map(|t| subst_term(t, params, args)).collect())
1397 }
1398 ProofTerm::Variable(_) | ProofTerm::BoundVarRef(_) => term.clone(),
1400 }
1401}