# Copyright (C) 2023 IBM Corp.
# SPDX-License-Identifier: Apache-2.0
from .. import util
from ..commands import *
from ..defined import *
from ..expression import *
from ..theory import *
from .bootstrap import *
__all__ = [
'Formula',
'Equal',
'Iff',
'Truth',
'And',
'Implies',
'Forall',
'Falsity',
'Not',
'Or',
'Exists',
'Exists1',
'equal',
'true',
'false',
'not_',
'and_',
'or_',
'iff',
'implies',
'exists',
'exists1',
'forall',
'eq',
'ne',
]
class Equal(Application):
r"""Equality (:math:`=`).
Constructs an equation by applying the equality constructor
:math:`(\mathtt{equal} : 𝛼 → 𝛼 → \mathtt{bool})` to `arg1` and `arg2`.
Parameters:
arg1 (:class:`Term`): :math:`t_1`.
arg2 (:class:`Term`): :math:`t_2`.
kwargs: Annotations.
Returns:
:class:`Application`:
:math:`t_1 = t_2`.
"""
constructor = new_constant(
'equal', FunctionType(TypeVariable('a'), TypeVariable('a'), bool_))
def __new__( # (term, term)
cls, arg1, arg2, **kwargs):
return cls.constructor(arg1, arg2, **kwargs)
@classmethod
def test(cls, arg):
return (Application.test(arg)
and Application.test(arg.left)
and arg.left.left.is_constant()
and arg.left.left.id == cls.constructor.id)
@classmethod
def _unfold(cls, arg):
return cls._unpack(arg)
@classmethod
def _unpack(cls, arg):
(_, l), r = arg._unpack_application()
return l, r
@classmethod
def eq(cls, x, y, *args, **kwargs): # macro
if not args:
return cls(x, y, **kwargs)
else:
return And(
*map(lambda t: cls(*t),
util.sliding_pairs_args(x, y, *args)), **kwargs)
@classmethod
def ne(cls, x, y, *args, **kwargs): # macro
if not args:
return Not(cls(x, y), **kwargs)
else:
return And(
*map(lambda t: Not(cls(*t)),
util.sliding_pairs_args(x, y, *args)), **kwargs)
class Iff(Equal):
r"""Equivalence (bi-implication, :math:`↔`).
Constructs a logical equivalence by applying :class:`Equal` to the
formulas `arg1` and `arg2`.
Equivalence is right-associative: If more than two arguments are given,
the result is right-folded.
Parameters:
arg1 (:class:`Formula`): :math:`p_1`.
arg2 (:class:`Formula`): :math:`p_2`.
args: Remaining :class:`Formula`'s.
kwargs: Annotations.
Returns:
:class:`Application`:
:math:`p_1 ↔ (p_2 ↔ (… ↔ (p_{n-1} ↔ p_n)))`.
"""
@classmethod
def _constructor(cls, arg1, arg2, **kwargs):
form1 = Formula.check(arg1, cls.__name__, None, 1)
form2 = Formula.check(arg2, cls.__name__, None, 2)
return Equal(form1, form2, **kwargs)
def __new__( # (form, form)
cls, arg1, arg2, *args, **kwargs):
return util.foldr_infix(
cls._constructor, cls, arg1, arg2, *args, **kwargs)
@classmethod
def test(cls, arg):
if not Equal.test(arg):
return False
l, r = arg._unpack_equal()
return l.is_formula() and r.is_formula()
class Truth(
DefinedConstant,
definiendum='true',
definiens=(
lambda p: # ()
Equal(p >> p, p >> p))(
Variable('p', bool_))):
r"""Truth (:math:`⊤`).
Constructs the true formula.
Parameters:
kwargs: Annotations.
Returns:
:class:`Constant`:
:math:`⊤`.
"""
class And(
DefinedInfixOperator,
definiendum='and',
definiens=(
lambda p, q, f, T: # (form, form)
(p, q) >> Equal(f >> f(p, q), f >> f(T, T)))(
*Variables('p', 'q', bool_),
Variable('f', FunctionType(bool_, bool_, bool_)),
Truth()),
associativity='right'):
r"""Conjunction (:math:`∧`).
Constructs a logical conjunction by applying constructor
:math:`(\mathtt{and} : \mathtt{bool} → \mathtt{bool} → \mathtt{bool})`
to `arg1` and `arg2`.
Conjunction is right-associative: If more than two arguments are given,
the result is right-folded.
Parameters:
arg1 (:class:`Formula`): :math:`p_1`
arg2 (:class:`Formula`): :math:`p_2`
args: Remaining :class:`Formula`'s.
kwargs: Annotations.
Returns:
:class:`Application`:
:math:`p_1 ∧ (p_2 ∧ (… ∧ (p_{n-1} ∧ p_n)))`.
"""
class Implies(
DefinedInfixOperator,
definiendum='implies',
definiens=(
lambda p, q: # (form, form)
(p, q) >> Iff(And(p, q), p))(
*Variables('p', 'q', bool_)),
associativity='right'):
r"""Implication (:math:`→`).
Constructs a logical implication by applying constructor
:math:`(\mathtt{implies} : \mathtt{bool} → \mathtt{bool} → \mathtt{bool})`
to `arg1` and `arg2`.
Implication is right-associative: If more than two arguments are given,
the result is right-folded.
Parameters:
arg1 (:class:`Formula`): :math:`p_1`.
arg2 (:class:`Formula`): :math:`p_2`.
args: Remaining :class:`Formula`'s.
kwargs: Annotations.
Returns:
:class:`Application`:
:math:`p_1 → (p_2 → (… → (p_{n-1} → p_n)))`.
"""
class Forall(
DefinedBinder,
definiendum='forall',
definiens=(
lambda p, x, T: # (var, form)
p >> Equal(p, x >> T))(
Variable('p', FunctionType(TypeVariable('a'), bool_)),
Variable('x', TypeVariable('a')),
Truth())):
r"""Universal quantification (:math:`∀`).
Constructs a universally quantified formula by applying constructor
:math:`(\mathtt{forall} : (𝛼 → \mathtt{bool}) → \mathtt{bool})`
to the predicate resulting from abstracting `arg1` over `arg2`.
Universal quantification is right-associative: If more than two
arguments are given, the result is right-folded.
Parameters:
arg1 (:class:`Variable`): :math:`x_1`.
arg2 (:class:`Variable` or :class:`Formula`): :math:`p`.
args: Remaining :class:`Variable`'s followed by a :class:`Formula`.
kwargs: Annotations.
Returns:
:class:`Application`:
:math:`∀ x_1, (∀ x_2, (…, (∀ x_{n-1}, (∀ x_n, p))))`.
"""
class Falsity(
DefinedConstant,
definiendum='false',
definiens=(
lambda p: # ()
Forall(p, p))(
Variable('p', bool_))):
r"""Falsity (:math:`⊥`).
Constructs the false formula.
Parameters:
kwargs: Annotations.
Returns:
:class:`Constant`:
:math:`⊥`.
"""
class Not(
DefinedPrefixOperator,
definiendum='not',
definiens=(
lambda p, F:
p >> Implies(p, F))(
Variable('p', bool_),
Falsity())):
r"""Negation (:math:`¬`).
Constructs a logical negation by applying constructor
:math:`(\mathtt{not} : \mathtt{bool} → \mathtt{bool})`
to `arg1`.
Parameters:
arg1 (:class:`Formula`): :math:`p`
kwargs: Annotations.
Returns:
:class:`Application`:
:math:`¬p`.
"""
class Or(
DefinedInfixOperator,
definiendum='or',
definiens=(
lambda p, q, r: # (form, form)
(p, q) >> Forall(r, Implies(Implies(p, r), Implies(q, r), r)))(
*Variables('p', 'q', 'r', bool_)),
associativity='right'):
r"""Disjunction (:math:`∨`).
Constructs a logical disjunction by applying constructor
:math:`(\mathtt{or} : \mathtt{bool} → \mathtt{bool} → \mathtt{bool})` to
`arg1` and `arg2`.
Disjunction is right-associative: If more than two arguments are given,
the result is right-folded.
Parameters:
arg1 (:class:`Formula`): :math:`p_1`
arg2 (:class:`Formula`): :math:`p_2`
args: Remaining :class:`Formula`'s.
kwargs: Annotations.
Returns:
:class:`Application`:
:math:`p_1 ∨ (p_2 ∨ (… ∨ (p_{n-1} ∨ p_n)))`.
"""
class Exists(
DefinedBinder,
definiendum='exists',
definiens=(
lambda p, q, x: # (var, form)
(p >> Forall(q, Implies((Forall(x, Implies(p(x), q))), q))))(
Variable('p', FunctionType(TypeVariable('a'), bool_)),
Variable('q', bool_),
Variable('x', TypeVariable('a')))):
r"""Existential quantification (:math:`∃`).
Constructs an existentially quantified formula by applying constructor
:math:`(\mathtt{exists} : (𝛼 → \mathtt{bool}) → \mathtt{bool})` to the
predicate resulting from abstracting `arg1` over `arg2`.
Existential quantification is right-associative: If more than two
arguments are given, the result is right-folded.
Parameters:
arg1 (:class:`Variable`): :math:`x_1`.
arg2 (:class:`Variable` or :class:`Formula`): :math:`p`.
args: Remaining :class:`Variable`'s followed by a :class:`Formula`.
kwargs: Annotations.
Returns:
:class:`Application`:
:math:`∃ x_1, (∃ x_2, (…, (∃ x_{n-1}, (∃ x_n, p))))`.
"""
class Exists1(
DefinedBinder,
definiendum='exists1',
definiens=(
lambda p, x, y: # (var, form)
(p >> And(
Exists.constructor(p),
Forall(x, y, Implies(And(p(x), p(y)), Equal(x, y))))))(
Variable('p', FunctionType(TypeVariable('a'), bool_)),
*Variables('x', 'y', TypeVariable('a')))):
r"""Unique existential quantification (:math:`∃!`).
Constructs a unique existentially quantified formula by applying
constructor
:math:`(\mathtt{exists1} : (𝛼 → \mathtt{bool}) → \mathtt{bool})` to the
predicate resulting from abstracting `arg1` over `arg2`.
Unique existential quantification is right-associative: If more than two
arguments are given, the result is right-folded.
Parameters:
arg1 (:class:`Variable`): :math:`x_1`.
arg2 (:class:`Variable` or :class:`Formula`): :math:`p`.
args: Remaining :class:`Variable`'s followed by a :class:`Formula`.
kwargs: Annotations.
Returns:
:class:`Application`:
:math:`∃! x_1, (∃! x_2, (…, (∃! x_{n-1}, (∃! x_n, p))))`.
"""
equal = Equal.constructor
true = Truth.constructor
false = Falsity.constructor
not_ = Not.constructor
and_ = And.constructor
or_ = Or.constructor
iff = Iff.constructor
implies = Implies.constructor
exists = Exists.constructor
exists1 = Exists1.constructor
forall = Forall.constructor
eq = Equal.eq
ne = Equal.ne