use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
use super::functions::*;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct LinearSequent {
pub context: Vec<String>,
pub conclusion: String,
}
impl LinearSequent {
pub fn new(context: Vec<String>, conclusion: impl Into<String>) -> Self {
Self {
context,
conclusion: conclusion.into(),
}
}
pub fn cut_elimination(&self) -> String {
format!(
"cut-elim(⊢ {} ⊢ {})",
self.context.join(", "),
self.conclusion
)
}
pub fn is_provable(&self) -> bool {
self.context.contains(&self.conclusion)
}
pub fn one_sided_form(&self) -> String {
let mut gamma = self.context.clone();
gamma.push(self.conclusion.clone());
format!("⊢ {}", gamma.join(", "))
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct SepLogicTriple {
pub precondition: String,
pub command: String,
pub postcondition: String,
}
impl SepLogicTriple {
#[allow(dead_code)]
pub fn new(pre: &str, cmd: &str, post: &str) -> Self {
Self {
precondition: pre.to_string(),
command: cmd.to_string(),
postcondition: post.to_string(),
}
}
#[allow(dead_code)]
pub fn frame_rule(&self, frame: &str) -> SepLogicTriple {
SepLogicTriple::new(
&format!("{} * {}", self.precondition, frame),
&self.command,
&format!("{} * {}", self.postcondition, frame),
)
}
#[allow(dead_code)]
pub fn display(&self) -> String {
format!(
"{{{}}}\n {}\n{{{}}}",
self.precondition, self.command, self.postcondition
)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub enum LlRule {
Ax,
Cut,
TensorR,
ParR,
WithR,
PlusR1,
PlusR2,
OfCourseR,
WhyNotR,
Dereliction,
Contraction,
Weakening,
}
impl LlRule {
#[allow(dead_code)]
pub fn is_structural(&self) -> bool {
matches!(
self,
LlRule::Contraction | LlRule::Weakening | LlRule::Dereliction
)
}
#[allow(dead_code)]
pub fn applies_to_exponentials(&self) -> bool {
matches!(
self,
LlRule::OfCourseR
| LlRule::WhyNotR
| LlRule::Dereliction
| LlRule::Contraction
| LlRule::Weakening
)
}
#[allow(dead_code)]
pub fn name(&self) -> &'static str {
match self {
LlRule::Ax => "axiom",
LlRule::Cut => "cut",
LlRule::TensorR => "tensor-R",
LlRule::ParR => "par-R",
LlRule::WithR => "with-R",
LlRule::PlusR1 => "plus-R1",
LlRule::PlusR2 => "plus-R2",
LlRule::OfCourseR => "!-R",
LlRule::WhyNotR => "?-R",
LlRule::Dereliction => "dereliction",
LlRule::Contraction => "contraction",
LlRule::Weakening => "weakening",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Heap {
pub map: std::collections::HashMap<u64, u64>,
}
impl Heap {
pub fn empty() -> Self {
Heap {
map: std::collections::HashMap::new(),
}
}
pub fn singleton(addr: u64, val: u64) -> Self {
let mut h = Heap::empty();
h.map.insert(addr, val);
h
}
pub fn disjoint(&self, other: &Heap) -> bool {
self.map.keys().all(|k| !other.map.contains_key(k))
}
pub fn union(&self, other: &Heap) -> Option<Heap> {
if !self.disjoint(other) {
return None;
}
let mut map = self.map.clone();
map.extend(other.map.iter().map(|(&k, &v)| (k, v)));
Some(Heap { map })
}
pub fn size(&self) -> usize {
self.map.len()
}
pub fn read(&self, addr: u64) -> Option<u64> {
self.map.get(&addr).copied()
}
pub fn write(&self, addr: u64, val: u64) -> Heap {
let mut h = self.clone();
h.map.insert(addr, val);
h
}
pub fn sep_split<P, Q>(&self, p: P, q: Q) -> Option<(Heap, Heap)>
where
P: Fn(&Heap) -> bool,
Q: Fn(&Heap) -> bool,
{
let keys: Vec<u64> = self.map.keys().copied().collect();
let n = keys.len();
for mask in 0u64..(1u64 << n) {
let mut h1 = Heap::empty();
let mut h2 = Heap::empty();
for (i, &k) in keys.iter().enumerate() {
let v = self.map[&k];
if (mask >> i) & 1 == 1 {
h1.map.insert(k, v);
} else {
h2.map.insert(k, v);
}
}
if p(&h1) && q(&h2) {
return Some((h1, h2));
}
}
None
}
}
#[derive(Debug, Clone)]
pub struct LinSequent {
pub formulas: Vec<LinFormula>,
}
impl LinSequent {
pub fn new(formulas: Vec<LinFormula>) -> Self {
LinSequent { formulas }
}
pub fn unit() -> Self {
LinSequent::new(vec![LinFormula::One])
}
pub fn is_axiom(&self) -> bool {
if self.formulas.len() != 2 {
return false;
}
let a = &self.formulas[0];
let b = &self.formulas[1];
a.dual() == *b || b.dual() == *a
}
pub fn tensor_components(&self) -> Option<(LinSequent, LinFormula, LinFormula)> {
for (i, f) in self.formulas.iter().enumerate() {
if let LinFormula::Tensor(a, b) = f {
let mut rest = self.formulas.clone();
rest.remove(i);
return Some((LinSequent::new(rest), *a.clone(), *b.clone()));
}
}
None
}
pub fn display(&self) -> String {
let parts: Vec<String> = self.formulas.iter().map(|f| f.to_string()).collect();
format!("⊢ {}", parts.join(", "))
}
pub fn total_complexity(&self) -> usize {
self.formulas.iter().map(|f| f.complexity()).sum()
}
}
#[derive(Debug, Clone)]
pub struct PhaseSemantics {
pub monoid: String,
pub facts: Vec<String>,
}
impl PhaseSemantics {
pub fn new(monoid: impl Into<String>, facts: Vec<String>) -> Self {
Self {
monoid: monoid.into(),
facts,
}
}
pub fn interpret_formula(&self, formula: &str) -> String {
format!(
"⟦{}⟧_{{{}}} ⊆ {{{}}}",
formula,
self.monoid,
self.facts.join(", ")
)
}
pub fn soundness(&self, formula: &str) -> bool {
!formula.is_empty()
}
pub fn completeness_statement(&self) -> String {
format!(
"LL ⊢ A iff A is valid in all phase spaces over {}",
self.monoid
)
}
pub fn is_fact(&self, element: &str) -> bool {
self.facts.contains(&element.to_string())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LinkKind {
Axiom,
Cut,
TensorLink,
ParLink,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PhaseSpaceExt {
pub monoid_name: String,
pub facts_description: String,
}
impl PhaseSpaceExt {
#[allow(dead_code)]
pub fn new(monoid: &str) -> Self {
Self {
monoid_name: monoid.to_string(),
facts_description: "Facts: subsets F of M such that F^perp^perp = F".to_string(),
}
}
#[allow(dead_code)]
pub fn completeness_description(&self) -> String {
format!(
"Phase semantics (Girard) complete for MALL: provable <-> valid in all phase models over {}",
self.monoid_name
)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct LinearTypeSystem {
pub language: String,
pub resource_tracking: bool,
pub affine_types: bool,
}
impl LinearTypeSystem {
#[allow(dead_code)]
pub fn linear_haskell() -> Self {
Self {
language: "Linear Haskell".to_string(),
resource_tracking: true,
affine_types: false,
}
}
#[allow(dead_code)]
pub fn rust_ownership() -> Self {
Self {
language: "Rust (ownership system)".to_string(),
resource_tracking: true,
affine_types: true,
}
}
#[allow(dead_code)]
pub fn uniqueness_types(lang: &str) -> Self {
Self {
language: lang.to_string(),
resource_tracking: true,
affine_types: false,
}
}
#[allow(dead_code)]
pub fn prevents_use_after_free(&self) -> bool {
self.resource_tracking
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LinearFormula {
Atom(String),
Tensor(Box<LinearFormula>, Box<LinearFormula>),
Par(Box<LinearFormula>, Box<LinearFormula>),
With(Box<LinearFormula>, Box<LinearFormula>),
Plus(Box<LinearFormula>, Box<LinearFormula>),
OfCourse(Box<LinearFormula>),
WhyNot(Box<LinearFormula>),
One,
Bottom,
Top,
Zero,
Dual(Box<LinearFormula>),
}
impl LinearFormula {
pub fn dual(self) -> Self {
match self {
LinearFormula::Atom(s) => LinearFormula::Dual(Box::new(LinearFormula::Atom(s))),
LinearFormula::Tensor(a, b) => {
LinearFormula::Par(Box::new(a.dual()), Box::new(b.dual()))
}
LinearFormula::Par(a, b) => {
LinearFormula::Tensor(Box::new(a.dual()), Box::new(b.dual()))
}
LinearFormula::With(a, b) => {
LinearFormula::Plus(Box::new(a.dual()), Box::new(b.dual()))
}
LinearFormula::Plus(a, b) => {
LinearFormula::With(Box::new(a.dual()), Box::new(b.dual()))
}
LinearFormula::OfCourse(a) => LinearFormula::WhyNot(Box::new(a.dual())),
LinearFormula::WhyNot(a) => LinearFormula::OfCourse(Box::new(a.dual())),
LinearFormula::One => LinearFormula::Bottom,
LinearFormula::Bottom => LinearFormula::One,
LinearFormula::Top => LinearFormula::Zero,
LinearFormula::Zero => LinearFormula::Top,
LinearFormula::Dual(a) => *a,
}
}
pub fn is_multiplicative(&self) -> bool {
match self {
LinearFormula::Atom(_) | LinearFormula::Dual(_) => true,
LinearFormula::Tensor(a, b) | LinearFormula::Par(a, b) => {
a.is_multiplicative() && b.is_multiplicative()
}
LinearFormula::One | LinearFormula::Bottom => true,
_ => false,
}
}
pub fn is_additive(&self) -> bool {
match self {
LinearFormula::Atom(_) | LinearFormula::Dual(_) => true,
LinearFormula::With(a, b) | LinearFormula::Plus(a, b) => {
a.is_additive() && b.is_additive()
}
LinearFormula::Top | LinearFormula::Zero => true,
_ => false,
}
}
pub fn is_exponential(&self) -> bool {
match self {
LinearFormula::OfCourse(_) | LinearFormula::WhyNot(_) => true,
LinearFormula::Tensor(a, b)
| LinearFormula::Par(a, b)
| LinearFormula::With(a, b)
| LinearFormula::Plus(a, b) => a.is_exponential() || b.is_exponential(),
LinearFormula::Dual(a) => a.is_exponential(),
_ => false,
}
}
pub fn lollipop(a: LinearFormula, b: LinearFormula) -> LinearFormula {
LinearFormula::Par(Box::new(a.dual()), Box::new(b))
}
pub fn complexity(&self) -> usize {
match self {
LinearFormula::Atom(_)
| LinearFormula::One
| LinearFormula::Bottom
| LinearFormula::Top
| LinearFormula::Zero => 0,
LinearFormula::Dual(a) => a.complexity(),
LinearFormula::OfCourse(a) | LinearFormula::WhyNot(a) => 1 + a.complexity(),
LinearFormula::Tensor(a, b)
| LinearFormula::Par(a, b)
| LinearFormula::With(a, b)
| LinearFormula::Plus(a, b) => 1 + a.complexity() + b.complexity(),
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct LlGame {
pub formula: String,
pub player: String,
pub opponent: String,
pub winning_condition: String,
}
impl LlGame {
#[allow(dead_code)]
pub fn new(formula: &str) -> Self {
Self {
formula: formula.to_string(),
player: "Proponent (P)".to_string(),
opponent: "Opponent (O)".to_string(),
winning_condition: "P wins if O cannot move".to_string(),
}
}
#[allow(dead_code)]
pub fn tensor_game(g1: &str, g2: &str) -> Self {
Self::new(&format!("{g1} tensor {g2}"))
}
#[allow(dead_code)]
pub fn abramsky_jagadeesan_description(&self) -> String {
format!(
"Game semantics (Abramsky-Jagadeesan): formula {} has a game where P-strategy = proof",
self.formula
)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct BoundedLinearLogic {
pub polynomial_bound: String,
}
impl BoundedLinearLogic {
#[allow(dead_code)]
pub fn new(bound: &str) -> Self {
Self {
polynomial_bound: bound.to_string(),
}
}
#[allow(dead_code)]
pub fn captures_ptime(&self) -> bool {
true
}
#[allow(dead_code)]
pub fn description(&self) -> String {
format!(
"BLL with bound {}: proofs correspond to polynomial-time algorithms",
self.polynomial_bound
)
}
}
#[derive(Debug, Clone)]
pub struct ResourceLogic {
pub resources: Vec<String>,
}
impl ResourceLogic {
pub fn new(resources: Vec<String>) -> Self {
Self { resources }
}
pub fn resource_consumption_model(&self) -> String {
format!("consume({})", self.resources.join(", "))
}
pub fn separation_logic_connection(&self) -> String {
"BI separation conjunction * ≅ linear tensor ⊗".to_string()
}
pub fn has_resource(&self, name: &str) -> bool {
self.resources.contains(&name.to_string())
}
pub fn consume(&self, name: &str) -> Option<Self> {
if self.has_resource(name) {
let resources = self
.resources
.iter()
.filter(|&r| r != name)
.cloned()
.collect();
Some(Self { resources })
} else {
None
}
}
pub fn count(&self) -> usize {
self.resources.len()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RelFormula {
Atom(String),
Neg(Box<RelFormula>),
And(Box<RelFormula>, Box<RelFormula>),
Or(Box<RelFormula>, Box<RelFormula>),
Implies(Box<RelFormula>, Box<RelFormula>),
Fusion(Box<RelFormula>, Box<RelFormula>),
}
impl RelFormula {
pub fn implies(a: RelFormula, b: RelFormula) -> Self {
RelFormula::Implies(Box::new(a), Box::new(b))
}
pub fn and(a: RelFormula, b: RelFormula) -> Self {
RelFormula::And(Box::new(a), Box::new(b))
}
pub fn or(a: RelFormula, b: RelFormula) -> Self {
RelFormula::Or(Box::new(a), Box::new(b))
}
pub fn neg(a: RelFormula) -> Self {
RelFormula::Neg(Box::new(a))
}
pub fn fusion(a: RelFormula, b: RelFormula) -> Self {
RelFormula::Fusion(Box::new(a), Box::new(b))
}
pub fn is_reflexivity_axiom(&self) -> bool {
if let RelFormula::Implies(a, b) = self {
a == b
} else {
false
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub enum BiFormula {
Atom(String),
Emp,
True,
SepConj(Box<BiFormula>, Box<BiFormula>),
SepImpl(Box<BiFormula>, Box<BiFormula>),
Conj(Box<BiFormula>, Box<BiFormula>),
Impl(Box<BiFormula>, Box<BiFormula>),
Disj(Box<BiFormula>, Box<BiFormula>),
Neg(Box<BiFormula>),
}
impl BiFormula {
#[allow(dead_code)]
pub fn atom(s: &str) -> Self {
Self::Atom(s.to_string())
}
#[allow(dead_code)]
pub fn sep_conj(a: BiFormula, b: BiFormula) -> Self {
Self::SepConj(Box::new(a), Box::new(b))
}
#[allow(dead_code)]
pub fn sep_impl(a: BiFormula, b: BiFormula) -> Self {
Self::SepImpl(Box::new(a), Box::new(b))
}
#[allow(dead_code)]
pub fn is_separation_connective(&self) -> bool {
matches!(
self,
BiFormula::SepConj(..) | BiFormula::SepImpl(..) | BiFormula::Emp
)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct DialecticaTransform {
pub formula: String,
pub dialectica_type: String,
}
impl DialecticaTransform {
#[allow(dead_code)]
pub fn new(formula: &str, dtype: &str) -> Self {
Self {
formula: formula.to_string(),
dialectica_type: dtype.to_string(),
}
}
#[allow(dead_code)]
pub fn de_paiva_description(&self) -> String {
format!("de Paiva's Dialectica categories model linear logic (with !A = forall x. A^x)",)
}
}
#[derive(Debug, Clone)]
pub struct GameSemantics {
pub arenas: Vec<String>,
}
impl GameSemantics {
pub fn new(arenas: Vec<String>) -> Self {
Self { arenas }
}
pub fn innocent_strategies(&self) -> Vec<String> {
self.arenas
.iter()
.map(|a| format!("InnocentStrat({})", a))
.collect()
}
pub fn composition(&self) -> String {
if self.arenas.len() < 2 {
"id".to_string()
} else {
self.arenas
.windows(2)
.map(|w| format!("{};{}", w[0], w[1]))
.collect::<Vec<_>>()
.join(" ∘ ")
}
}
pub fn tensor(&self) -> String {
self.arenas.join(" ⊗ ")
}
pub fn full_completeness_statement(&self) -> String {
"Every innocent strategy is the denotation of a linear logic proof".to_string()
}
}
#[derive(Debug, Clone)]
pub struct CoherenceSpace {
pub n_tokens: usize,
pub coh: Vec<Vec<bool>>,
}
impl CoherenceSpace {
pub fn flat(n: usize) -> Self {
let mut coh = vec![vec![false; n]; n];
for i in 0..n {
coh[i][i] = true;
}
CoherenceSpace { n_tokens: n, coh }
}
pub fn complete(n: usize) -> Self {
CoherenceSpace {
n_tokens: n,
coh: vec![vec![true; n]; n],
}
}
pub fn is_clique(&self, tokens: &[usize]) -> bool {
for &i in tokens {
for &j in tokens {
if i < self.n_tokens && j < self.n_tokens && !self.coh[i][j] {
return false;
}
}
}
true
}
pub fn is_antichain(&self, tokens: &[usize]) -> bool {
for (idx, &i) in tokens.iter().enumerate() {
for &j in &tokens[idx + 1..] {
if i < self.n_tokens && j < self.n_tokens && self.coh[i][j] {
return false;
}
}
}
true
}
pub fn tensor(&self, other: &CoherenceSpace) -> CoherenceSpace {
let n = self.n_tokens * other.n_tokens;
let mut coh = vec![vec![false; n]; n];
for i1 in 0..self.n_tokens {
for j1 in 0..other.n_tokens {
for i2 in 0..self.n_tokens {
for j2 in 0..other.n_tokens {
let row = i1 * other.n_tokens + j1;
let col = i2 * other.n_tokens + j2;
coh[row][col] = self.coh[i1][i2] && other.coh[j1][j2];
}
}
}
}
CoherenceSpace { n_tokens: n, coh }
}
pub fn with(&self, other: &CoherenceSpace) -> CoherenceSpace {
let n = self.n_tokens + other.n_tokens;
let mut coh = vec![vec![false; n]; n];
for i in 0..self.n_tokens {
for j in 0..self.n_tokens {
coh[i][j] = self.coh[i][j];
}
}
let offset = self.n_tokens;
for i in 0..other.n_tokens {
for j in 0..other.n_tokens {
coh[offset + i][offset + j] = other.coh[i][j];
}
}
CoherenceSpace { n_tokens: n, coh }
}
}
#[derive(Debug, Clone, Default)]
pub struct ExponentialRules;
impl ExponentialRules {
pub fn new() -> Self {
Self
}
pub fn dereliction(&self, a: &str) -> String {
format!("dereliction(!{a} ⊢ {a})")
}
pub fn contraction(&self, a: &str) -> String {
format!("contraction(!{a} ⊢ !{a} ⊗ !{a})")
}
pub fn weakening(&self, a: &str) -> String {
format!("weakening(!{a} ⊢ 1)")
}
pub fn promotion(&self, a: &str, gamma: &[&str]) -> String {
let ctx = gamma.join(", ");
format!("promotion(![{ctx}] ⊢ {a} → ![{ctx}] ⊢ !{a})")
}
pub fn storage(&self, a: &str) -> String {
format!("storage(!{a} ≡ !{a} ⊗ !{a})")
}
}
#[derive(Debug, Clone)]
pub struct PhaseSpace {
pub n: usize,
pub mul: Vec<Vec<usize>>,
pub identity: usize,
pub bot: Vec<bool>,
}
impl PhaseSpace {
pub fn new(_name: &str) -> Self {
Self::trivial()
}
pub fn completeness_description(&self) -> String {
format!(
"Phase semantics complete for linear logic: monoid size={}, bot={} elements",
self.n,
self.bot.iter().filter(|&&b| b).count()
)
}
pub fn trivial() -> Self {
PhaseSpace {
n: 1,
mul: vec![vec![0]],
identity: 0,
bot: vec![true],
}
}
pub fn multiply(&self, a: usize, b: usize) -> usize {
self.mul[a % self.n][b % self.n]
}
pub fn closure(&self, subset: &[bool]) -> Vec<bool> {
let perp = self.orthogonal(subset);
self.orthogonal(&perp)
}
pub fn orthogonal(&self, subset: &[bool]) -> Vec<bool> {
let mut result = vec![true; self.n];
for m in 0..self.n {
for a in 0..self.n {
if subset[a] {
let prod = self.multiply(m, a);
if !self.bot[prod] {
result[m] = false;
break;
}
}
}
}
result
}
pub fn is_fact(&self, subset: &[bool]) -> bool {
let closed = self.closure(subset);
subset.iter().zip(closed.iter()).all(|(a, b)| a == b)
}
pub fn subset_size(subset: &[bool]) -> usize {
subset.iter().filter(|&&b| b).count()
}
}
#[derive(Debug, Clone)]
pub struct Link {
pub kind: LinkKind,
pub conclusions: Vec<usize>,
pub premises: Vec<usize>,
}
impl Link {
pub fn axiom(i: usize, j: usize) -> Self {
Link {
kind: LinkKind::Axiom,
conclusions: vec![i, j],
premises: vec![],
}
}
pub fn tensor(premise_a: usize, premise_b: usize, conclusion: usize) -> Self {
Link {
kind: LinkKind::TensorLink,
conclusions: vec![conclusion],
premises: vec![premise_a, premise_b],
}
}
pub fn par(premise_a: usize, premise_b: usize, conclusion: usize) -> Self {
Link {
kind: LinkKind::ParLink,
conclusions: vec![conclusion],
premises: vec![premise_a, premise_b],
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum LinFormula {
Atom(String),
One,
Bot,
Top,
Zero,
Tensor(Box<LinFormula>, Box<LinFormula>),
Par(Box<LinFormula>, Box<LinFormula>),
With(Box<LinFormula>, Box<LinFormula>),
Plus(Box<LinFormula>, Box<LinFormula>),
Bang(Box<LinFormula>),
WhyNot(Box<LinFormula>),
Neg(Box<LinFormula>),
}
impl LinFormula {
pub fn dual(&self) -> LinFormula {
match self {
LinFormula::Atom(s) => LinFormula::Neg(Box::new(LinFormula::Atom(s.clone()))),
LinFormula::Neg(inner) => *inner.clone(),
LinFormula::One => LinFormula::Bot,
LinFormula::Bot => LinFormula::One,
LinFormula::Top => LinFormula::Zero,
LinFormula::Zero => LinFormula::Top,
LinFormula::Tensor(a, b) => LinFormula::Par(Box::new(a.dual()), Box::new(b.dual())),
LinFormula::Par(a, b) => LinFormula::Tensor(Box::new(a.dual()), Box::new(b.dual())),
LinFormula::With(a, b) => LinFormula::Plus(Box::new(a.dual()), Box::new(b.dual())),
LinFormula::Plus(a, b) => LinFormula::With(Box::new(a.dual()), Box::new(b.dual())),
LinFormula::Bang(a) => LinFormula::WhyNot(Box::new(a.dual())),
LinFormula::WhyNot(a) => LinFormula::Bang(Box::new(a.dual())),
}
}
pub fn lollipop(a: LinFormula, b: LinFormula) -> LinFormula {
LinFormula::Par(Box::new(a.dual()), Box::new(b))
}
pub fn is_literal(&self) -> bool {
match self {
LinFormula::Atom(_) => true,
LinFormula::Neg(inner) => matches!(inner.as_ref(), LinFormula::Atom(_)),
_ => false,
}
}
pub fn is_multiplicative(&self) -> bool {
match self {
LinFormula::Atom(_) | LinFormula::Neg(_) | LinFormula::One | LinFormula::Bot => true,
LinFormula::Tensor(a, b) | LinFormula::Par(a, b) => {
a.is_multiplicative() && b.is_multiplicative()
}
LinFormula::Bang(a) | LinFormula::WhyNot(a) => a.is_multiplicative(),
LinFormula::With(_, _) | LinFormula::Plus(_, _) => false,
LinFormula::Top | LinFormula::Zero => false,
}
}
pub fn depth(&self) -> usize {
match self {
LinFormula::Atom(_)
| LinFormula::One
| LinFormula::Bot
| LinFormula::Top
| LinFormula::Zero => 0,
LinFormula::Neg(a) | LinFormula::Bang(a) | LinFormula::WhyNot(a) => 1 + a.depth(),
LinFormula::Tensor(a, b)
| LinFormula::Par(a, b)
| LinFormula::With(a, b)
| LinFormula::Plus(a, b) => 1 + a.depth().max(b.depth()),
}
}
pub fn complexity(&self) -> usize {
match self {
LinFormula::Atom(_)
| LinFormula::One
| LinFormula::Bot
| LinFormula::Top
| LinFormula::Zero => 0,
LinFormula::Neg(a) | LinFormula::Bang(a) | LinFormula::WhyNot(a) => 1 + a.complexity(),
LinFormula::Tensor(a, b)
| LinFormula::Par(a, b)
| LinFormula::With(a, b)
| LinFormula::Plus(a, b) => 1 + a.complexity() + b.complexity(),
}
}
pub fn atoms(&self) -> Vec<String> {
let mut result = Vec::new();
self.collect_atoms(&mut result);
result.sort();
result.dedup();
result
}
fn collect_atoms(&self, out: &mut Vec<String>) {
match self {
LinFormula::Atom(s) => out.push(s.clone()),
LinFormula::Neg(a) | LinFormula::Bang(a) | LinFormula::WhyNot(a) => {
a.collect_atoms(out);
}
LinFormula::Tensor(a, b)
| LinFormula::Par(a, b)
| LinFormula::With(a, b)
| LinFormula::Plus(a, b) => {
a.collect_atoms(out);
b.collect_atoms(out);
}
_ => {}
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub enum LlFormula {
Atom(String),
Neg(Box<LlFormula>),
Tensor(Box<LlFormula>, Box<LlFormula>),
Par(Box<LlFormula>, Box<LlFormula>),
Plus(Box<LlFormula>, Box<LlFormula>),
With(Box<LlFormula>, Box<LlFormula>),
One,
Bottom,
Top,
Zero,
OfCourse(Box<LlFormula>),
WhyNot(Box<LlFormula>),
Forall(String, Box<LlFormula>),
Exists(String, Box<LlFormula>),
}
impl LlFormula {
#[allow(dead_code)]
pub fn atom(s: &str) -> Self {
Self::Atom(s.to_string())
}
#[allow(dead_code)]
pub fn tensor(a: LlFormula, b: LlFormula) -> Self {
Self::Tensor(Box::new(a), Box::new(b))
}
#[allow(dead_code)]
pub fn par(a: LlFormula, b: LlFormula) -> Self {
Self::Par(Box::new(a), Box::new(b))
}
#[allow(dead_code)]
pub fn with_op(a: LlFormula, b: LlFormula) -> Self {
Self::With(Box::new(a), Box::new(b))
}
#[allow(dead_code)]
pub fn plus(a: LlFormula, b: LlFormula) -> Self {
Self::Plus(Box::new(a), Box::new(b))
}
#[allow(dead_code)]
pub fn of_course(a: LlFormula) -> Self {
Self::OfCourse(Box::new(a))
}
#[allow(dead_code)]
pub fn why_not(a: LlFormula) -> Self {
Self::WhyNot(Box::new(a))
}
#[allow(dead_code)]
pub fn neg(a: LlFormula) -> Self {
Self::Neg(Box::new(a))
}
#[allow(dead_code)]
pub fn is_multiplicative(&self) -> bool {
matches!(
self,
LlFormula::Tensor(..) | LlFormula::Par(..) | LlFormula::One | LlFormula::Bottom
)
}
#[allow(dead_code)]
pub fn is_additive(&self) -> bool {
matches!(
self,
LlFormula::Plus(..) | LlFormula::With(..) | LlFormula::Top | LlFormula::Zero
)
}
#[allow(dead_code)]
pub fn is_exponential(&self) -> bool {
matches!(self, LlFormula::OfCourse(..) | LlFormula::WhyNot(..))
}
#[allow(dead_code)]
pub fn linear_negation(&self) -> LlFormula {
match self {
LlFormula::Atom(s) => LlFormula::Neg(Box::new(LlFormula::Atom(s.clone()))),
LlFormula::Neg(a) => *a.clone(),
LlFormula::Tensor(a, b) => LlFormula::par(a.linear_negation(), b.linear_negation()),
LlFormula::Par(a, b) => LlFormula::tensor(a.linear_negation(), b.linear_negation()),
LlFormula::Plus(a, b) => LlFormula::with_op(a.linear_negation(), b.linear_negation()),
LlFormula::With(a, b) => LlFormula::plus(a.linear_negation(), b.linear_negation()),
LlFormula::One => LlFormula::Bottom,
LlFormula::Bottom => LlFormula::One,
LlFormula::Top => LlFormula::Zero,
LlFormula::Zero => LlFormula::Top,
LlFormula::OfCourse(a) => LlFormula::why_not(a.linear_negation()),
LlFormula::WhyNot(a) => LlFormula::of_course(a.linear_negation()),
LlFormula::Forall(x, a) => LlFormula::Exists(x.clone(), Box::new(a.linear_negation())),
LlFormula::Exists(x, a) => LlFormula::Forall(x.clone(), Box::new(a.linear_negation())),
}
}
}
#[derive(Debug, Clone)]
pub struct ProofStructure {
pub n_formulas: usize,
pub links: Vec<Link>,
}
impl ProofStructure {
pub fn new(n_formulas: usize) -> Self {
ProofStructure {
n_formulas,
links: Vec::new(),
}
}
pub fn add_link(&mut self, link: Link) {
self.links.push(link);
}
pub fn is_correct(&self) -> bool {
if self.n_formulas == 0 {
return true;
}
let mut parent: Vec<usize> = (0..self.n_formulas).collect();
fn find(parent: &mut Vec<usize>, x: usize) -> usize {
if parent[x] != x {
parent[x] = find(parent, parent[x]);
}
parent[x]
}
let mut edge_count = 0usize;
for link in &self.links {
if link.kind == LinkKind::Axiom && link.conclusions.len() == 2 {
let a = link.conclusions[0];
let b = link.conclusions[1];
if a >= self.n_formulas || b >= self.n_formulas {
return false;
}
let ra = find(&mut parent, a);
let rb = find(&mut parent, b);
if ra == rb {
return false;
}
parent[ra] = rb;
edge_count += 1;
}
}
edge_count * 2 == self.n_formulas
}
pub fn axiom_count(&self) -> usize {
self.links
.iter()
.filter(|l| l.kind == LinkKind::Axiom)
.count()
}
pub fn cut_count(&self) -> usize {
self.links
.iter()
.filter(|l| l.kind == LinkKind::Cut)
.count()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ProofStructureExt {
pub sequent: String,
pub num_axiom_links: usize,
pub is_correct: bool,
}
impl ProofStructureExt {
#[allow(dead_code)]
pub fn new(sequent: &str, axiom_links: usize, correct: bool) -> Self {
Self {
sequent: sequent.to_string(),
num_axiom_links: axiom_links,
is_correct: correct,
}
}
#[allow(dead_code)]
pub fn correctness_criterion(&self) -> String {
"Girard's correctness criterion: every switching of par is acyclic and connected"
.to_string()
}
#[allow(dead_code)]
pub fn cut_elimination_description(&self) -> String {
"MLL cut elimination: geometrically reduces proof net size (Retoré's criterion)".to_string()
}
}
#[derive(Debug, Clone)]
pub struct ProofNet {
pub formulas: Vec<LinearFormula>,
pub links: Vec<(usize, usize)>,
}
impl ProofNet {
pub fn new(formulas: Vec<LinearFormula>) -> Self {
Self {
formulas,
links: Vec::new(),
}
}
pub fn add_link(&mut self, i: usize, j: usize) {
self.links.push((i, j));
}
pub fn is_well_typed(&self) -> bool {
let n = self.formulas.len();
if n == 0 {
return true;
}
let mut count = vec![0usize; n];
for &(i, j) in &self.links {
if i >= n || j >= n {
return false;
}
count[i] += 1;
count[j] += 1;
}
count.iter().all(|&c| c == 1)
}
pub fn correctness_criterion_danos_regnier(&self) -> bool {
self.is_well_typed() && !self.formulas.is_empty()
}
pub fn size(&self) -> usize {
self.formulas.len()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct GeometryOfInteraction {
pub description: String,
}
impl GeometryOfInteraction {
#[allow(dead_code)]
pub fn girard_goi() -> Self {
Self {
description:
"Girard's GoI: cut elimination as execution formula in a traced monoidal category"
.to_string(),
}
}
#[allow(dead_code)]
pub fn dynamic_description(&self) -> String {
"Paths in proof nets compose via execution formula: feedback loop".to_string()
}
}
#[derive(Debug, Clone)]
pub struct Quantales {
pub quantale: String,
}
impl Quantales {
pub fn new(quantale: impl Into<String>) -> Self {
Self {
quantale: quantale.into(),
}
}
pub fn is_linear_category(&self) -> bool {
!self.quantale.is_empty()
}
pub fn star_autonomous(&self) -> String {
format!("*-Autonomous({})", self.quantale)
}
pub fn free_star_autonomous(atoms: &[&str]) -> Self {
Self::new(format!("Free*Aut({{{}}})", atoms.join(", ")))
}
pub fn phase_space_quantale(monoid: &str) -> Self {
Self::new(format!("PhaseSpace({})", monoid))
}
pub fn chu_construction(&self, k: &str) -> String {
format!("Chu({}, {})", self.quantale, k)
}
pub fn dualizing_object(&self) -> String {
format!("⊥_{}", self.quantale)
}
}