1use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
6
7use super::functions::*;
8use std::collections::HashMap;
9
10#[derive(Debug, Clone)]
12pub struct LinearSequent {
13 pub context: Vec<String>,
15 pub conclusion: String,
17}
18impl LinearSequent {
19 pub fn new(context: Vec<String>, conclusion: impl Into<String>) -> Self {
21 Self {
22 context,
23 conclusion: conclusion.into(),
24 }
25 }
26 pub fn cut_elimination(&self) -> String {
30 format!(
31 "cut-elim(β’ {} β’ {})",
32 self.context.join(", "),
33 self.conclusion
34 )
35 }
36 pub fn is_provable(&self) -> bool {
38 self.context.contains(&self.conclusion)
39 }
40 pub fn one_sided_form(&self) -> String {
42 let mut gamma = self.context.clone();
43 gamma.push(self.conclusion.clone());
44 format!("β’ {}", gamma.join(", "))
45 }
46}
47#[allow(dead_code)]
49#[derive(Debug, Clone)]
50pub struct SepLogicTriple {
51 pub precondition: String,
52 pub command: String,
53 pub postcondition: String,
54}
55impl SepLogicTriple {
56 #[allow(dead_code)]
57 pub fn new(pre: &str, cmd: &str, post: &str) -> Self {
58 Self {
59 precondition: pre.to_string(),
60 command: cmd.to_string(),
61 postcondition: post.to_string(),
62 }
63 }
64 #[allow(dead_code)]
65 pub fn frame_rule(&self, frame: &str) -> SepLogicTriple {
66 SepLogicTriple::new(
67 &format!("{} * {}", self.precondition, frame),
68 &self.command,
69 &format!("{} * {}", self.postcondition, frame),
70 )
71 }
72 #[allow(dead_code)]
73 pub fn display(&self) -> String {
74 format!(
75 "{{{}}}\n {}\n{{{}}}",
76 self.precondition, self.command, self.postcondition
77 )
78 }
79}
80#[allow(dead_code)]
82#[derive(Debug, Clone)]
83pub enum LlRule {
84 Ax,
85 Cut,
86 TensorR,
87 ParR,
88 WithR,
89 PlusR1,
90 PlusR2,
91 OfCourseR,
92 WhyNotR,
93 Dereliction,
94 Contraction,
95 Weakening,
96}
97impl LlRule {
98 #[allow(dead_code)]
99 pub fn is_structural(&self) -> bool {
100 matches!(
101 self,
102 LlRule::Contraction | LlRule::Weakening | LlRule::Dereliction
103 )
104 }
105 #[allow(dead_code)]
106 pub fn applies_to_exponentials(&self) -> bool {
107 matches!(
108 self,
109 LlRule::OfCourseR
110 | LlRule::WhyNotR
111 | LlRule::Dereliction
112 | LlRule::Contraction
113 | LlRule::Weakening
114 )
115 }
116 #[allow(dead_code)]
117 pub fn name(&self) -> &'static str {
118 match self {
119 LlRule::Ax => "axiom",
120 LlRule::Cut => "cut",
121 LlRule::TensorR => "tensor-R",
122 LlRule::ParR => "par-R",
123 LlRule::WithR => "with-R",
124 LlRule::PlusR1 => "plus-R1",
125 LlRule::PlusR2 => "plus-R2",
126 LlRule::OfCourseR => "!-R",
127 LlRule::WhyNotR => "?-R",
128 LlRule::Dereliction => "dereliction",
129 LlRule::Contraction => "contraction",
130 LlRule::Weakening => "weakening",
131 }
132 }
133}
134#[derive(Debug, Clone, PartialEq, Eq)]
136pub struct Heap {
137 pub map: std::collections::HashMap<u64, u64>,
139}
140impl Heap {
141 pub fn empty() -> Self {
143 Heap {
144 map: std::collections::HashMap::new(),
145 }
146 }
147 pub fn singleton(addr: u64, val: u64) -> Self {
149 let mut h = Heap::empty();
150 h.map.insert(addr, val);
151 h
152 }
153 pub fn disjoint(&self, other: &Heap) -> bool {
155 self.map.keys().all(|k| !other.map.contains_key(k))
156 }
157 pub fn union(&self, other: &Heap) -> Option<Heap> {
159 if !self.disjoint(other) {
160 return None;
161 }
162 let mut map = self.map.clone();
163 map.extend(other.map.iter().map(|(&k, &v)| (k, v)));
164 Some(Heap { map })
165 }
166 pub fn size(&self) -> usize {
168 self.map.len()
169 }
170 pub fn read(&self, addr: u64) -> Option<u64> {
172 self.map.get(&addr).copied()
173 }
174 pub fn write(&self, addr: u64, val: u64) -> Heap {
176 let mut h = self.clone();
177 h.map.insert(addr, val);
178 h
179 }
180 pub fn sep_split<P, Q>(&self, p: P, q: Q) -> Option<(Heap, Heap)>
183 where
184 P: Fn(&Heap) -> bool,
185 Q: Fn(&Heap) -> bool,
186 {
187 let keys: Vec<u64> = self.map.keys().copied().collect();
188 let n = keys.len();
189 for mask in 0u64..(1u64 << n) {
190 let mut h1 = Heap::empty();
191 let mut h2 = Heap::empty();
192 for (i, &k) in keys.iter().enumerate() {
193 let v = self.map[&k];
194 if (mask >> i) & 1 == 1 {
195 h1.map.insert(k, v);
196 } else {
197 h2.map.insert(k, v);
198 }
199 }
200 if p(&h1) && q(&h2) {
201 return Some((h1, h2));
202 }
203 }
204 None
205 }
206}
207#[derive(Debug, Clone)]
209pub struct LinSequent {
210 pub formulas: Vec<LinFormula>,
212}
213impl LinSequent {
214 pub fn new(formulas: Vec<LinFormula>) -> Self {
216 LinSequent { formulas }
217 }
218 pub fn unit() -> Self {
220 LinSequent::new(vec![LinFormula::One])
221 }
222 pub fn is_axiom(&self) -> bool {
224 if self.formulas.len() != 2 {
225 return false;
226 }
227 let a = &self.formulas[0];
228 let b = &self.formulas[1];
229 a.dual() == *b || b.dual() == *a
230 }
231 pub fn tensor_components(&self) -> Option<(LinSequent, LinFormula, LinFormula)> {
234 for (i, f) in self.formulas.iter().enumerate() {
235 if let LinFormula::Tensor(a, b) = f {
236 let mut rest = self.formulas.clone();
237 rest.remove(i);
238 return Some((LinSequent::new(rest), *a.clone(), *b.clone()));
239 }
240 }
241 None
242 }
243 pub fn display(&self) -> String {
245 let parts: Vec<String> = self.formulas.iter().map(|f| f.to_string()).collect();
246 format!("β’ {}", parts.join(", "))
247 }
248 pub fn total_complexity(&self) -> usize {
250 self.formulas.iter().map(|f| f.complexity()).sum()
251 }
252}
253#[derive(Debug, Clone)]
256pub struct PhaseSemantics {
257 pub monoid: String,
259 pub facts: Vec<String>,
261}
262impl PhaseSemantics {
263 pub fn new(monoid: impl Into<String>, facts: Vec<String>) -> Self {
265 Self {
266 monoid: monoid.into(),
267 facts,
268 }
269 }
270 pub fn interpret_formula(&self, formula: &str) -> String {
274 format!(
275 "β¦{}β§_{{{}}} β {{{}}}",
276 formula,
277 self.monoid,
278 self.facts.join(", ")
279 )
280 }
281 pub fn soundness(&self, formula: &str) -> bool {
283 !formula.is_empty()
284 }
285 pub fn completeness_statement(&self) -> String {
287 format!(
288 "LL β’ A iff A is valid in all phase spaces over {}",
289 self.monoid
290 )
291 }
292 pub fn is_fact(&self, element: &str) -> bool {
294 self.facts.contains(&element.to_string())
295 }
296}
297#[derive(Debug, Clone, PartialEq, Eq)]
299pub enum LinkKind {
300 Axiom,
302 Cut,
304 TensorLink,
306 ParLink,
308}
309#[allow(dead_code)]
311#[derive(Debug, Clone)]
312pub struct PhaseSpaceExt {
313 pub monoid_name: String,
314 pub facts_description: String,
315}
316impl PhaseSpaceExt {
317 #[allow(dead_code)]
318 pub fn new(monoid: &str) -> Self {
319 Self {
320 monoid_name: monoid.to_string(),
321 facts_description: "Facts: subsets F of M such that F^perp^perp = F".to_string(),
322 }
323 }
324 #[allow(dead_code)]
325 pub fn completeness_description(&self) -> String {
326 format!(
327 "Phase semantics (Girard) complete for MALL: provable <-> valid in all phase models over {}",
328 self.monoid_name
329 )
330 }
331}
332#[allow(dead_code)]
334#[derive(Debug, Clone)]
335pub struct LinearTypeSystem {
336 pub language: String,
337 pub resource_tracking: bool,
338 pub affine_types: bool,
339}
340impl LinearTypeSystem {
341 #[allow(dead_code)]
342 pub fn linear_haskell() -> Self {
343 Self {
344 language: "Linear Haskell".to_string(),
345 resource_tracking: true,
346 affine_types: false,
347 }
348 }
349 #[allow(dead_code)]
350 pub fn rust_ownership() -> Self {
351 Self {
352 language: "Rust (ownership system)".to_string(),
353 resource_tracking: true,
354 affine_types: true,
355 }
356 }
357 #[allow(dead_code)]
358 pub fn uniqueness_types(lang: &str) -> Self {
359 Self {
360 language: lang.to_string(),
361 resource_tracking: true,
362 affine_types: false,
363 }
364 }
365 #[allow(dead_code)]
366 pub fn prevents_use_after_free(&self) -> bool {
367 self.resource_tracking
368 }
369}
370#[derive(Debug, Clone, PartialEq, Eq)]
376pub enum LinearFormula {
377 Atom(String),
379 Tensor(Box<LinearFormula>, Box<LinearFormula>),
381 Par(Box<LinearFormula>, Box<LinearFormula>),
383 With(Box<LinearFormula>, Box<LinearFormula>),
385 Plus(Box<LinearFormula>, Box<LinearFormula>),
387 OfCourse(Box<LinearFormula>),
389 WhyNot(Box<LinearFormula>),
391 One,
393 Bottom,
395 Top,
397 Zero,
399 Dual(Box<LinearFormula>),
401}
402impl LinearFormula {
403 pub fn dual(self) -> Self {
414 match self {
415 LinearFormula::Atom(s) => LinearFormula::Dual(Box::new(LinearFormula::Atom(s))),
416 LinearFormula::Tensor(a, b) => {
417 LinearFormula::Par(Box::new(a.dual()), Box::new(b.dual()))
418 }
419 LinearFormula::Par(a, b) => {
420 LinearFormula::Tensor(Box::new(a.dual()), Box::new(b.dual()))
421 }
422 LinearFormula::With(a, b) => {
423 LinearFormula::Plus(Box::new(a.dual()), Box::new(b.dual()))
424 }
425 LinearFormula::Plus(a, b) => {
426 LinearFormula::With(Box::new(a.dual()), Box::new(b.dual()))
427 }
428 LinearFormula::OfCourse(a) => LinearFormula::WhyNot(Box::new(a.dual())),
429 LinearFormula::WhyNot(a) => LinearFormula::OfCourse(Box::new(a.dual())),
430 LinearFormula::One => LinearFormula::Bottom,
431 LinearFormula::Bottom => LinearFormula::One,
432 LinearFormula::Top => LinearFormula::Zero,
433 LinearFormula::Zero => LinearFormula::Top,
434 LinearFormula::Dual(a) => *a,
435 }
436 }
437 pub fn is_multiplicative(&self) -> bool {
439 match self {
440 LinearFormula::Atom(_) | LinearFormula::Dual(_) => true,
441 LinearFormula::Tensor(a, b) | LinearFormula::Par(a, b) => {
442 a.is_multiplicative() && b.is_multiplicative()
443 }
444 LinearFormula::One | LinearFormula::Bottom => true,
445 _ => false,
446 }
447 }
448 pub fn is_additive(&self) -> bool {
450 match self {
451 LinearFormula::Atom(_) | LinearFormula::Dual(_) => true,
452 LinearFormula::With(a, b) | LinearFormula::Plus(a, b) => {
453 a.is_additive() && b.is_additive()
454 }
455 LinearFormula::Top | LinearFormula::Zero => true,
456 _ => false,
457 }
458 }
459 pub fn is_exponential(&self) -> bool {
461 match self {
462 LinearFormula::OfCourse(_) | LinearFormula::WhyNot(_) => true,
463 LinearFormula::Tensor(a, b)
464 | LinearFormula::Par(a, b)
465 | LinearFormula::With(a, b)
466 | LinearFormula::Plus(a, b) => a.is_exponential() || b.is_exponential(),
467 LinearFormula::Dual(a) => a.is_exponential(),
468 _ => false,
469 }
470 }
471 pub fn lollipop(a: LinearFormula, b: LinearFormula) -> LinearFormula {
473 LinearFormula::Par(Box::new(a.dual()), Box::new(b))
474 }
475 pub fn complexity(&self) -> usize {
477 match self {
478 LinearFormula::Atom(_)
479 | LinearFormula::One
480 | LinearFormula::Bottom
481 | LinearFormula::Top
482 | LinearFormula::Zero => 0,
483 LinearFormula::Dual(a) => a.complexity(),
484 LinearFormula::OfCourse(a) | LinearFormula::WhyNot(a) => 1 + a.complexity(),
485 LinearFormula::Tensor(a, b)
486 | LinearFormula::Par(a, b)
487 | LinearFormula::With(a, b)
488 | LinearFormula::Plus(a, b) => 1 + a.complexity() + b.complexity(),
489 }
490 }
491}
492#[allow(dead_code)]
494#[derive(Debug, Clone)]
495pub struct LlGame {
496 pub formula: String,
497 pub player: String,
498 pub opponent: String,
499 pub winning_condition: String,
500}
501impl LlGame {
502 #[allow(dead_code)]
503 pub fn new(formula: &str) -> Self {
504 Self {
505 formula: formula.to_string(),
506 player: "Proponent (P)".to_string(),
507 opponent: "Opponent (O)".to_string(),
508 winning_condition: "P wins if O cannot move".to_string(),
509 }
510 }
511 #[allow(dead_code)]
512 pub fn tensor_game(g1: &str, g2: &str) -> Self {
513 Self::new(&format!("{g1} tensor {g2}"))
514 }
515 #[allow(dead_code)]
516 pub fn abramsky_jagadeesan_description(&self) -> String {
517 format!(
518 "Game semantics (Abramsky-Jagadeesan): formula {} has a game where P-strategy = proof",
519 self.formula
520 )
521 }
522}
523#[allow(dead_code)]
525#[derive(Debug, Clone)]
526pub struct BoundedLinearLogic {
527 pub polynomial_bound: String,
528}
529impl BoundedLinearLogic {
530 #[allow(dead_code)]
531 pub fn new(bound: &str) -> Self {
532 Self {
533 polynomial_bound: bound.to_string(),
534 }
535 }
536 #[allow(dead_code)]
537 pub fn captures_ptime(&self) -> bool {
538 true
539 }
540 #[allow(dead_code)]
541 pub fn description(&self) -> String {
542 format!(
543 "BLL with bound {}: proofs correspond to polynomial-time algorithms",
544 self.polynomial_bound
545 )
546 }
547}
548#[derive(Debug, Clone)]
550pub struct ResourceLogic {
551 pub resources: Vec<String>,
553}
554impl ResourceLogic {
555 pub fn new(resources: Vec<String>) -> Self {
557 Self { resources }
558 }
559 pub fn resource_consumption_model(&self) -> String {
562 format!("consume({})", self.resources.join(", "))
563 }
564 pub fn separation_logic_connection(&self) -> String {
567 "BI separation conjunction * β
linear tensor β".to_string()
568 }
569 pub fn has_resource(&self, name: &str) -> bool {
571 self.resources.contains(&name.to_string())
572 }
573 pub fn consume(&self, name: &str) -> Option<Self> {
575 if self.has_resource(name) {
576 let resources = self
577 .resources
578 .iter()
579 .filter(|&r| r != name)
580 .cloned()
581 .collect();
582 Some(Self { resources })
583 } else {
584 None
585 }
586 }
587 pub fn count(&self) -> usize {
589 self.resources.len()
590 }
591}
592#[derive(Debug, Clone, PartialEq, Eq)]
594pub enum RelFormula {
595 Atom(String),
597 Neg(Box<RelFormula>),
599 And(Box<RelFormula>, Box<RelFormula>),
601 Or(Box<RelFormula>, Box<RelFormula>),
603 Implies(Box<RelFormula>, Box<RelFormula>),
605 Fusion(Box<RelFormula>, Box<RelFormula>),
607}
608impl RelFormula {
609 pub fn implies(a: RelFormula, b: RelFormula) -> Self {
611 RelFormula::Implies(Box::new(a), Box::new(b))
612 }
613 pub fn and(a: RelFormula, b: RelFormula) -> Self {
615 RelFormula::And(Box::new(a), Box::new(b))
616 }
617 pub fn or(a: RelFormula, b: RelFormula) -> Self {
619 RelFormula::Or(Box::new(a), Box::new(b))
620 }
621 pub fn neg(a: RelFormula) -> Self {
623 RelFormula::Neg(Box::new(a))
624 }
625 pub fn fusion(a: RelFormula, b: RelFormula) -> Self {
627 RelFormula::Fusion(Box::new(a), Box::new(b))
628 }
629 pub fn is_reflexivity_axiom(&self) -> bool {
631 if let RelFormula::Implies(a, b) = self {
632 a == b
633 } else {
634 false
635 }
636 }
637}
638#[allow(dead_code)]
640#[derive(Debug, Clone)]
641pub enum BiFormula {
642 Atom(String),
643 Emp,
644 True,
645 SepConj(Box<BiFormula>, Box<BiFormula>),
646 SepImpl(Box<BiFormula>, Box<BiFormula>),
647 Conj(Box<BiFormula>, Box<BiFormula>),
648 Impl(Box<BiFormula>, Box<BiFormula>),
649 Disj(Box<BiFormula>, Box<BiFormula>),
650 Neg(Box<BiFormula>),
651}
652impl BiFormula {
653 #[allow(dead_code)]
654 pub fn atom(s: &str) -> Self {
655 Self::Atom(s.to_string())
656 }
657 #[allow(dead_code)]
658 pub fn sep_conj(a: BiFormula, b: BiFormula) -> Self {
659 Self::SepConj(Box::new(a), Box::new(b))
660 }
661 #[allow(dead_code)]
662 pub fn sep_impl(a: BiFormula, b: BiFormula) -> Self {
663 Self::SepImpl(Box::new(a), Box::new(b))
664 }
665 #[allow(dead_code)]
666 pub fn is_separation_connective(&self) -> bool {
667 matches!(
668 self,
669 BiFormula::SepConj(..) | BiFormula::SepImpl(..) | BiFormula::Emp
670 )
671 }
672}
673#[allow(dead_code)]
675#[derive(Debug, Clone)]
676pub struct DialecticaTransform {
677 pub formula: String,
678 pub dialectica_type: String,
679}
680impl DialecticaTransform {
681 #[allow(dead_code)]
682 pub fn new(formula: &str, dtype: &str) -> Self {
683 Self {
684 formula: formula.to_string(),
685 dialectica_type: dtype.to_string(),
686 }
687 }
688 #[allow(dead_code)]
689 pub fn de_paiva_description(&self) -> String {
690 format!("de Paiva's Dialectica categories model linear logic (with !A = forall x. A^x)",)
691 }
692}
693#[derive(Debug, Clone)]
695pub struct GameSemantics {
696 pub arenas: Vec<String>,
698}
699impl GameSemantics {
700 pub fn new(arenas: Vec<String>) -> Self {
702 Self { arenas }
703 }
704 pub fn innocent_strategies(&self) -> Vec<String> {
707 self.arenas
708 .iter()
709 .map(|a| format!("InnocentStrat({})", a))
710 .collect()
711 }
712 pub fn composition(&self) -> String {
714 if self.arenas.len() < 2 {
715 "id".to_string()
716 } else {
717 self.arenas
718 .windows(2)
719 .map(|w| format!("{};{}", w[0], w[1]))
720 .collect::<Vec<_>>()
721 .join(" β ")
722 }
723 }
724 pub fn tensor(&self) -> String {
726 self.arenas.join(" β ")
727 }
728 pub fn full_completeness_statement(&self) -> String {
730 "Every innocent strategy is the denotation of a linear logic proof".to_string()
731 }
732}
733#[derive(Debug, Clone)]
735pub struct CoherenceSpace {
736 pub n_tokens: usize,
738 pub coh: Vec<Vec<bool>>,
740}
741impl CoherenceSpace {
742 pub fn flat(n: usize) -> Self {
744 let mut coh = vec![vec![false; n]; n];
745 for i in 0..n {
746 coh[i][i] = true;
747 }
748 CoherenceSpace { n_tokens: n, coh }
749 }
750 pub fn complete(n: usize) -> Self {
752 CoherenceSpace {
753 n_tokens: n,
754 coh: vec![vec![true; n]; n],
755 }
756 }
757 pub fn is_clique(&self, tokens: &[usize]) -> bool {
759 for &i in tokens {
760 for &j in tokens {
761 if i < self.n_tokens && j < self.n_tokens && !self.coh[i][j] {
762 return false;
763 }
764 }
765 }
766 true
767 }
768 pub fn is_antichain(&self, tokens: &[usize]) -> bool {
770 for (idx, &i) in tokens.iter().enumerate() {
771 for &j in &tokens[idx + 1..] {
772 if i < self.n_tokens && j < self.n_tokens && self.coh[i][j] {
773 return false;
774 }
775 }
776 }
777 true
778 }
779 pub fn tensor(&self, other: &CoherenceSpace) -> CoherenceSpace {
781 let n = self.n_tokens * other.n_tokens;
782 let mut coh = vec![vec![false; n]; n];
783 for i1 in 0..self.n_tokens {
784 for j1 in 0..other.n_tokens {
785 for i2 in 0..self.n_tokens {
786 for j2 in 0..other.n_tokens {
787 let row = i1 * other.n_tokens + j1;
788 let col = i2 * other.n_tokens + j2;
789 coh[row][col] = self.coh[i1][i2] && other.coh[j1][j2];
790 }
791 }
792 }
793 }
794 CoherenceSpace { n_tokens: n, coh }
795 }
796 pub fn with(&self, other: &CoherenceSpace) -> CoherenceSpace {
798 let n = self.n_tokens + other.n_tokens;
799 let mut coh = vec![vec![false; n]; n];
800 for i in 0..self.n_tokens {
801 for j in 0..self.n_tokens {
802 coh[i][j] = self.coh[i][j];
803 }
804 }
805 let offset = self.n_tokens;
806 for i in 0..other.n_tokens {
807 for j in 0..other.n_tokens {
808 coh[offset + i][offset + j] = other.coh[i][j];
809 }
810 }
811 CoherenceSpace { n_tokens: n, coh }
812 }
813}
814#[derive(Debug, Clone, Default)]
816pub struct ExponentialRules;
817impl ExponentialRules {
818 pub fn new() -> Self {
820 Self
821 }
822 pub fn dereliction(&self, a: &str) -> String {
824 format!("dereliction(!{a} β’ {a})")
825 }
826 pub fn contraction(&self, a: &str) -> String {
828 format!("contraction(!{a} β’ !{a} β !{a})")
829 }
830 pub fn weakening(&self, a: &str) -> String {
832 format!("weakening(!{a} β’ 1)")
833 }
834 pub fn promotion(&self, a: &str, gamma: &[&str]) -> String {
836 let ctx = gamma.join(", ");
837 format!("promotion(![{ctx}] β’ {a} β ![{ctx}] β’ !{a})")
838 }
839 pub fn storage(&self, a: &str) -> String {
841 format!("storage(!{a} β‘ !{a} β !{a})")
842 }
843}
844#[derive(Debug, Clone)]
847pub struct PhaseSpace {
848 pub n: usize,
850 pub mul: Vec<Vec<usize>>,
852 pub identity: usize,
854 pub bot: Vec<bool>,
856}
857impl PhaseSpace {
858 pub fn new(_name: &str) -> Self {
861 Self::trivial()
862 }
863 pub fn completeness_description(&self) -> String {
865 format!(
866 "Phase semantics complete for linear logic: monoid size={}, bot={} elements",
867 self.n,
868 self.bot.iter().filter(|&&b| b).count()
869 )
870 }
871 pub fn trivial() -> Self {
873 PhaseSpace {
874 n: 1,
875 mul: vec![vec![0]],
876 identity: 0,
877 bot: vec![true],
878 }
879 }
880 pub fn multiply(&self, a: usize, b: usize) -> usize {
882 self.mul[a % self.n][b % self.n]
883 }
884 pub fn closure(&self, subset: &[bool]) -> Vec<bool> {
887 let perp = self.orthogonal(subset);
888 self.orthogonal(&perp)
889 }
890 pub fn orthogonal(&self, subset: &[bool]) -> Vec<bool> {
892 let mut result = vec![true; self.n];
893 for m in 0..self.n {
894 for a in 0..self.n {
895 if subset[a] {
896 let prod = self.multiply(m, a);
897 if !self.bot[prod] {
898 result[m] = false;
899 break;
900 }
901 }
902 }
903 }
904 result
905 }
906 pub fn is_fact(&self, subset: &[bool]) -> bool {
908 let closed = self.closure(subset);
909 subset.iter().zip(closed.iter()).all(|(a, b)| a == b)
910 }
911 pub fn subset_size(subset: &[bool]) -> usize {
913 subset.iter().filter(|&&b| b).count()
914 }
915}
916#[derive(Debug, Clone)]
918pub struct Link {
919 pub kind: LinkKind,
921 pub conclusions: Vec<usize>,
923 pub premises: Vec<usize>,
925}
926impl Link {
927 pub fn axiom(i: usize, j: usize) -> Self {
929 Link {
930 kind: LinkKind::Axiom,
931 conclusions: vec![i, j],
932 premises: vec![],
933 }
934 }
935 pub fn tensor(premise_a: usize, premise_b: usize, conclusion: usize) -> Self {
937 Link {
938 kind: LinkKind::TensorLink,
939 conclusions: vec![conclusion],
940 premises: vec![premise_a, premise_b],
941 }
942 }
943 pub fn par(premise_a: usize, premise_b: usize, conclusion: usize) -> Self {
945 Link {
946 kind: LinkKind::ParLink,
947 conclusions: vec![conclusion],
948 premises: vec![premise_a, premise_b],
949 }
950 }
951}
952#[derive(Debug, Clone, PartialEq, Eq, Hash)]
954pub enum LinFormula {
955 Atom(String),
957 One,
959 Bot,
961 Top,
963 Zero,
965 Tensor(Box<LinFormula>, Box<LinFormula>),
967 Par(Box<LinFormula>, Box<LinFormula>),
969 With(Box<LinFormula>, Box<LinFormula>),
971 Plus(Box<LinFormula>, Box<LinFormula>),
973 Bang(Box<LinFormula>),
975 WhyNot(Box<LinFormula>),
977 Neg(Box<LinFormula>),
979}
980impl LinFormula {
981 pub fn dual(&self) -> LinFormula {
984 match self {
985 LinFormula::Atom(s) => LinFormula::Neg(Box::new(LinFormula::Atom(s.clone()))),
986 LinFormula::Neg(inner) => *inner.clone(),
987 LinFormula::One => LinFormula::Bot,
988 LinFormula::Bot => LinFormula::One,
989 LinFormula::Top => LinFormula::Zero,
990 LinFormula::Zero => LinFormula::Top,
991 LinFormula::Tensor(a, b) => LinFormula::Par(Box::new(a.dual()), Box::new(b.dual())),
992 LinFormula::Par(a, b) => LinFormula::Tensor(Box::new(a.dual()), Box::new(b.dual())),
993 LinFormula::With(a, b) => LinFormula::Plus(Box::new(a.dual()), Box::new(b.dual())),
994 LinFormula::Plus(a, b) => LinFormula::With(Box::new(a.dual()), Box::new(b.dual())),
995 LinFormula::Bang(a) => LinFormula::WhyNot(Box::new(a.dual())),
996 LinFormula::WhyNot(a) => LinFormula::Bang(Box::new(a.dual())),
997 }
998 }
999 pub fn lollipop(a: LinFormula, b: LinFormula) -> LinFormula {
1001 LinFormula::Par(Box::new(a.dual()), Box::new(b))
1002 }
1003 pub fn is_literal(&self) -> bool {
1005 match self {
1006 LinFormula::Atom(_) => true,
1007 LinFormula::Neg(inner) => matches!(inner.as_ref(), LinFormula::Atom(_)),
1008 _ => false,
1009 }
1010 }
1011 pub fn is_multiplicative(&self) -> bool {
1013 match self {
1014 LinFormula::Atom(_) | LinFormula::Neg(_) | LinFormula::One | LinFormula::Bot => true,
1015 LinFormula::Tensor(a, b) | LinFormula::Par(a, b) => {
1016 a.is_multiplicative() && b.is_multiplicative()
1017 }
1018 LinFormula::Bang(a) | LinFormula::WhyNot(a) => a.is_multiplicative(),
1019 LinFormula::With(_, _) | LinFormula::Plus(_, _) => false,
1020 LinFormula::Top | LinFormula::Zero => false,
1021 }
1022 }
1023 pub fn depth(&self) -> usize {
1025 match self {
1026 LinFormula::Atom(_)
1027 | LinFormula::One
1028 | LinFormula::Bot
1029 | LinFormula::Top
1030 | LinFormula::Zero => 0,
1031 LinFormula::Neg(a) | LinFormula::Bang(a) | LinFormula::WhyNot(a) => 1 + a.depth(),
1032 LinFormula::Tensor(a, b)
1033 | LinFormula::Par(a, b)
1034 | LinFormula::With(a, b)
1035 | LinFormula::Plus(a, b) => 1 + a.depth().max(b.depth()),
1036 }
1037 }
1038 pub fn complexity(&self) -> usize {
1040 match self {
1041 LinFormula::Atom(_)
1042 | LinFormula::One
1043 | LinFormula::Bot
1044 | LinFormula::Top
1045 | LinFormula::Zero => 0,
1046 LinFormula::Neg(a) | LinFormula::Bang(a) | LinFormula::WhyNot(a) => 1 + a.complexity(),
1047 LinFormula::Tensor(a, b)
1048 | LinFormula::Par(a, b)
1049 | LinFormula::With(a, b)
1050 | LinFormula::Plus(a, b) => 1 + a.complexity() + b.complexity(),
1051 }
1052 }
1053 pub fn atoms(&self) -> Vec<String> {
1055 let mut result = Vec::new();
1056 self.collect_atoms(&mut result);
1057 result.sort();
1058 result.dedup();
1059 result
1060 }
1061 fn collect_atoms(&self, out: &mut Vec<String>) {
1062 match self {
1063 LinFormula::Atom(s) => out.push(s.clone()),
1064 LinFormula::Neg(a) | LinFormula::Bang(a) | LinFormula::WhyNot(a) => {
1065 a.collect_atoms(out);
1066 }
1067 LinFormula::Tensor(a, b)
1068 | LinFormula::Par(a, b)
1069 | LinFormula::With(a, b)
1070 | LinFormula::Plus(a, b) => {
1071 a.collect_atoms(out);
1072 b.collect_atoms(out);
1073 }
1074 _ => {}
1075 }
1076 }
1077}
1078#[allow(dead_code)]
1080#[derive(Debug, Clone, PartialEq)]
1081pub enum LlFormula {
1082 Atom(String),
1083 Neg(Box<LlFormula>),
1084 Tensor(Box<LlFormula>, Box<LlFormula>),
1085 Par(Box<LlFormula>, Box<LlFormula>),
1086 Plus(Box<LlFormula>, Box<LlFormula>),
1087 With(Box<LlFormula>, Box<LlFormula>),
1088 One,
1089 Bottom,
1090 Top,
1091 Zero,
1092 OfCourse(Box<LlFormula>),
1093 WhyNot(Box<LlFormula>),
1094 Forall(String, Box<LlFormula>),
1095 Exists(String, Box<LlFormula>),
1096}
1097impl LlFormula {
1098 #[allow(dead_code)]
1099 pub fn atom(s: &str) -> Self {
1100 Self::Atom(s.to_string())
1101 }
1102 #[allow(dead_code)]
1103 pub fn tensor(a: LlFormula, b: LlFormula) -> Self {
1104 Self::Tensor(Box::new(a), Box::new(b))
1105 }
1106 #[allow(dead_code)]
1107 pub fn par(a: LlFormula, b: LlFormula) -> Self {
1108 Self::Par(Box::new(a), Box::new(b))
1109 }
1110 #[allow(dead_code)]
1111 pub fn with_op(a: LlFormula, b: LlFormula) -> Self {
1112 Self::With(Box::new(a), Box::new(b))
1113 }
1114 #[allow(dead_code)]
1115 pub fn plus(a: LlFormula, b: LlFormula) -> Self {
1116 Self::Plus(Box::new(a), Box::new(b))
1117 }
1118 #[allow(dead_code)]
1119 pub fn of_course(a: LlFormula) -> Self {
1120 Self::OfCourse(Box::new(a))
1121 }
1122 #[allow(dead_code)]
1123 pub fn why_not(a: LlFormula) -> Self {
1124 Self::WhyNot(Box::new(a))
1125 }
1126 #[allow(dead_code)]
1127 pub fn neg(a: LlFormula) -> Self {
1128 Self::Neg(Box::new(a))
1129 }
1130 #[allow(dead_code)]
1131 pub fn is_multiplicative(&self) -> bool {
1132 matches!(
1133 self,
1134 LlFormula::Tensor(..) | LlFormula::Par(..) | LlFormula::One | LlFormula::Bottom
1135 )
1136 }
1137 #[allow(dead_code)]
1138 pub fn is_additive(&self) -> bool {
1139 matches!(
1140 self,
1141 LlFormula::Plus(..) | LlFormula::With(..) | LlFormula::Top | LlFormula::Zero
1142 )
1143 }
1144 #[allow(dead_code)]
1145 pub fn is_exponential(&self) -> bool {
1146 matches!(self, LlFormula::OfCourse(..) | LlFormula::WhyNot(..))
1147 }
1148 #[allow(dead_code)]
1149 pub fn linear_negation(&self) -> LlFormula {
1150 match self {
1151 LlFormula::Atom(s) => LlFormula::Neg(Box::new(LlFormula::Atom(s.clone()))),
1152 LlFormula::Neg(a) => *a.clone(),
1153 LlFormula::Tensor(a, b) => LlFormula::par(a.linear_negation(), b.linear_negation()),
1154 LlFormula::Par(a, b) => LlFormula::tensor(a.linear_negation(), b.linear_negation()),
1155 LlFormula::Plus(a, b) => LlFormula::with_op(a.linear_negation(), b.linear_negation()),
1156 LlFormula::With(a, b) => LlFormula::plus(a.linear_negation(), b.linear_negation()),
1157 LlFormula::One => LlFormula::Bottom,
1158 LlFormula::Bottom => LlFormula::One,
1159 LlFormula::Top => LlFormula::Zero,
1160 LlFormula::Zero => LlFormula::Top,
1161 LlFormula::OfCourse(a) => LlFormula::why_not(a.linear_negation()),
1162 LlFormula::WhyNot(a) => LlFormula::of_course(a.linear_negation()),
1163 LlFormula::Forall(x, a) => LlFormula::Exists(x.clone(), Box::new(a.linear_negation())),
1164 LlFormula::Exists(x, a) => LlFormula::Forall(x.clone(), Box::new(a.linear_negation())),
1165 }
1166 }
1167}
1168#[derive(Debug, Clone)]
1170pub struct ProofStructure {
1171 pub n_formulas: usize,
1173 pub links: Vec<Link>,
1175}
1176impl ProofStructure {
1177 pub fn new(n_formulas: usize) -> Self {
1179 ProofStructure {
1180 n_formulas,
1181 links: Vec::new(),
1182 }
1183 }
1184 pub fn add_link(&mut self, link: Link) {
1186 self.links.push(link);
1187 }
1188 pub fn is_correct(&self) -> bool {
1194 if self.n_formulas == 0 {
1195 return true;
1196 }
1197 let mut parent: Vec<usize> = (0..self.n_formulas).collect();
1198 fn find(parent: &mut Vec<usize>, x: usize) -> usize {
1199 if parent[x] != x {
1200 parent[x] = find(parent, parent[x]);
1201 }
1202 parent[x]
1203 }
1204 let mut edge_count = 0usize;
1205 for link in &self.links {
1206 if link.kind == LinkKind::Axiom && link.conclusions.len() == 2 {
1207 let a = link.conclusions[0];
1208 let b = link.conclusions[1];
1209 if a >= self.n_formulas || b >= self.n_formulas {
1210 return false;
1211 }
1212 let ra = find(&mut parent, a);
1213 let rb = find(&mut parent, b);
1214 if ra == rb {
1215 return false;
1216 }
1217 parent[ra] = rb;
1218 edge_count += 1;
1219 }
1220 }
1221 edge_count * 2 == self.n_formulas
1222 }
1223 pub fn axiom_count(&self) -> usize {
1225 self.links
1226 .iter()
1227 .filter(|l| l.kind == LinkKind::Axiom)
1228 .count()
1229 }
1230 pub fn cut_count(&self) -> usize {
1232 self.links
1233 .iter()
1234 .filter(|l| l.kind == LinkKind::Cut)
1235 .count()
1236 }
1237}
1238#[allow(dead_code)]
1240#[derive(Debug, Clone)]
1241pub struct ProofStructureExt {
1242 pub sequent: String,
1243 pub num_axiom_links: usize,
1244 pub is_correct: bool,
1245}
1246impl ProofStructureExt {
1247 #[allow(dead_code)]
1248 pub fn new(sequent: &str, axiom_links: usize, correct: bool) -> Self {
1249 Self {
1250 sequent: sequent.to_string(),
1251 num_axiom_links: axiom_links,
1252 is_correct: correct,
1253 }
1254 }
1255 #[allow(dead_code)]
1256 pub fn correctness_criterion(&self) -> String {
1257 "Girard's correctness criterion: every switching of par is acyclic and connected"
1258 .to_string()
1259 }
1260 #[allow(dead_code)]
1261 pub fn cut_elimination_description(&self) -> String {
1262 "MLL cut elimination: geometrically reduces proof net size (RetorΓ©'s criterion)".to_string()
1263 }
1264}
1265#[derive(Debug, Clone)]
1267pub struct ProofNet {
1268 pub formulas: Vec<LinearFormula>,
1270 pub links: Vec<(usize, usize)>,
1272}
1273impl ProofNet {
1274 pub fn new(formulas: Vec<LinearFormula>) -> Self {
1276 Self {
1277 formulas,
1278 links: Vec::new(),
1279 }
1280 }
1281 pub fn add_link(&mut self, i: usize, j: usize) {
1283 self.links.push((i, j));
1284 }
1285 pub fn is_well_typed(&self) -> bool {
1288 let n = self.formulas.len();
1289 if n == 0 {
1290 return true;
1291 }
1292 let mut count = vec![0usize; n];
1293 for &(i, j) in &self.links {
1294 if i >= n || j >= n {
1295 return false;
1296 }
1297 count[i] += 1;
1298 count[j] += 1;
1299 }
1300 count.iter().all(|&c| c == 1)
1301 }
1302 pub fn correctness_criterion_danos_regnier(&self) -> bool {
1308 self.is_well_typed() && !self.formulas.is_empty()
1309 }
1310 pub fn size(&self) -> usize {
1312 self.formulas.len()
1313 }
1314}
1315#[allow(dead_code)]
1317#[derive(Debug, Clone)]
1318pub struct GeometryOfInteraction {
1319 pub description: String,
1320}
1321impl GeometryOfInteraction {
1322 #[allow(dead_code)]
1323 pub fn girard_goi() -> Self {
1324 Self {
1325 description:
1326 "Girard's GoI: cut elimination as execution formula in a traced monoidal category"
1327 .to_string(),
1328 }
1329 }
1330 #[allow(dead_code)]
1331 pub fn dynamic_description(&self) -> String {
1332 "Paths in proof nets compose via execution formula: feedback loop".to_string()
1333 }
1334}
1335#[derive(Debug, Clone)]
1338pub struct Quantales {
1339 pub quantale: String,
1341}
1342impl Quantales {
1343 pub fn new(quantale: impl Into<String>) -> Self {
1345 Self {
1346 quantale: quantale.into(),
1347 }
1348 }
1349 pub fn is_linear_category(&self) -> bool {
1351 !self.quantale.is_empty()
1352 }
1353 pub fn star_autonomous(&self) -> String {
1358 format!("*-Autonomous({})", self.quantale)
1359 }
1360 pub fn free_star_autonomous(atoms: &[&str]) -> Self {
1362 Self::new(format!("Free*Aut({{{}}})", atoms.join(", ")))
1363 }
1364 pub fn phase_space_quantale(monoid: &str) -> Self {
1366 Self::new(format!("PhaseSpace({})", monoid))
1367 }
1368 pub fn chu_construction(&self, k: &str) -> String {
1370 format!("Chu({}, {})", self.quantale, k)
1371 }
1372 pub fn dualizing_object(&self) -> String {
1374 format!("β₯_{}", self.quantale)
1375 }
1376}