Source code for logic1.firstorder.quantified
r"""We provide subclasses of :class:`Formula <.formula.Formula>` that implement
quantified formulas in the sense that their toplevel operator is one of the
quantifiers :math:`\exists` or :math:`\forall`.
"""
from __future__ import annotations
from abc import abstractmethod
from collections import deque
from typing import final, Sequence
from logic1.firstorder.term import Variable
from logic1.firstorder.formula import α, τ, χ, σ, Formula
from logic1.support.tracing import trace
[docs]
class QuantifiedFormula(Formula[α, τ, χ, σ]):
r"""A class whose instances are quantified formulas in the sense that their
toplevel operator is one of the quantifiers :math:`\exists` or
:math:`\forall`. Note that members of :class:`QuantifiedFormula` may have
subformulas with other logical operators deeper in the expression tree.
"""
@property
def var(self) -> χ:
"""The variable of the quantifier.
>>> from logic1.theories.RCF import *
>>> x, y = VV.get('x', 'y')
>>> f = All(x, Ex(y, x == y))
>>> f.var
x
.. seealso::
* :attr:`args <.formula.Formula.op>` -- all arguments as a tuple
* :attr:`op <.formula.Formula.op>` -- operator
"""
return self.args[0]
@property
def arg(self) -> Formula[α, τ, χ, σ]:
"""The subformula in the scope of the :class:`QuantifiedFormula`.
>>> from logic1.theories.RCF import *
>>> x, y = VV.get('x', 'y')
>>> f = All(x, Ex(y, x == y))
>>> f.arg
Ex(y, x - y == 0)
.. seealso::
* :attr:`args <.formula.Formula.op>` -- all arguments as a tuple
* :attr:`op <.formula.Formula.op>` -- operator
"""
return self.args[1]
@abstractmethod
def __init__(self, vars_: χ | Sequence[χ], arg: Formula[α, τ, χ, σ]) -> None:
"""Construct a quantified formula.
>>> from logic1.theories.RCF import VV
>>> a, b, x = VV.get('a', 'b', 'x')
>>> All((a, b), Ex(x, a*x + b >= 0))
All(a, All(b, Ex(x, a*x + b >= 0)))
"""
assert self.op in (Ex, All) # in lack of abstract class properties
super().__init__()
if not isinstance(arg, Formula):
raise ValueError(f'{arg!r} is not a Formula')
match vars_:
case Variable():
assert not isinstance(vars_, Sequence)
self._args = (vars_, arg)
case (Variable(), *_):
f = arg
for v in reversed(vars_[1:]):
f = self.op(v, f)
self._args = (vars_[0], f)
case _:
raise ValueError(f'{vars_!r} is not a Variable')
def __le__(self, other: Formula[α, τ, χ, σ]) -> bool:
"""Compare two quantified formulas by their quantifier variable and
subformula. Use the :meth:`sort_key <.term.Variable.sort_key>` of the
variables.
>>> from logic1.theories.RCF import *
>>> x, y = VV.get('x', 'y')
>>> f1 = All(x, x**2 >= 0)
>>> f2 = All(y, y**2 >= 0)
>>> f2 <= f1
True
"""
if isinstance(other, QuantifiedFormula) and self.op is other.op:
return self.var.sort_key() <= other.var.sort_key() and self.arg <= other.arg
else:
return super().__le__(other)
[docs]
@final
class Ex(QuantifiedFormula[α, τ, χ, σ]):
r"""A class whose instances are existentially quantified formulas in the
sense that their toplevel operator represents the quantifier symbol
:math:`\exists`. Besides variables, the quantifier accepts sequences of
variables as a shorthand.
>>> from logic1.firstorder import *
>>> from logic1.theories.RCF import *
>>> x, y, z = VV.get('x', 'y', 'z')
>>> Ex(x, x**2 == y)
Ex(x, x**2 - y == 0)
>>> Ex([x, y], And(x > 0, y > 0, z == x - y))
Ex(x, Ex(y, And(x > 0, y > 0, x - y - z == 0)))
"""
[docs]
def __init__(self, vars_: χ | Sequence[χ], arg: Formula[α, τ, χ, σ]) -> None:
"""Construct an existentially quantified formula.
>>> from logic1.theories.RCF import VV
>>> x, y = VV.get('x', 'y')
>>> Ex(x, x**2 == y)
Ex(x, x**2 - y == 0)
"""
super().__init__(vars_, arg)
[docs]
@classmethod
def dual(cls) -> type[All[α, τ, χ, σ]]:
r"""A class method yielding the class :class:`All`, which implements
the dual operator :math:`\forall` of :math:`\exists`.
"""
return All
[docs]
@final
class All(QuantifiedFormula[α, τ, χ, σ]):
r"""A class whose instances are universally quantified formulas in the
sense that their toplevel operator represents the quantifier symbol
:math:`\forall`. Besides variables, the quantifier accepts sequences of
variables as a shorthand.
>>> from logic1.theories.RCF import *
>>> x, y = VV.get('x', 'y')
>>> All(x, x**2 >= 0)
All(x, x**2 >= 0)
>>> All([x, y], (x + y)**2 >= 0)
All(x, All(y, x**2 + 2*x*y + y**2 >= 0))
"""
[docs]
def __init__(self, vars_: χ | Sequence[χ], arg: Formula[α, τ, χ, σ]) -> None:
"""Construct a universally quantified formula.
>>> from logic1.theories.RCF import VV
>>> x, y = VV.get('x', 'y')
>>> All(x, x**2 >= 0)
All(x, x**2 >= 0)
"""
super().__init__(vars_, arg)
[docs]
@classmethod
def dual(cls) -> type[Ex[α, τ, χ, σ]]:
"""A class method yielding the dual class :class:`Ex` of class:`All`.
"""
return Ex
[docs]
class Prefix(deque[tuple[type[All | Ex], list[χ]]]):
"""Holds a quantifier prefix of a formula.
>>> from logic1.theories.RCF import *
>>> x, x0, epsilon, delta = VV.get('x', 'x0', 'epsilon', 'delta')
>>> Prefix((All, [x0, epsilon]), (Ex, [delta]), (All, [x]))
Prefix([(<class 'logic1.firstorder.quantified.All'>, [x0, epsilon]),
(<class 'logic1.firstorder.quantified.Ex'>, [delta]),
(<class 'logic1.firstorder.quantified.All'>, [x])])
>>> print(_)
All [x0, epsilon] Ex [delta] All [x]
.. seealso::
* :external:class:`collections.deque` -- for methods inherited from double-ended queues
* :meth:`matrix <.Formula.matrix>` -- the matrix of a prenex formula
* :meth:`quantify <.Formula.quantify>` -- add quantifier prefix
"""
def __init__(self, *blocks: tuple[type[All[α, τ, χ, σ] | Ex[α, τ, χ, σ]], list[χ]]) -> None:
super().__init__(blocks)
def __str__(self) -> str:
return ' '.join(q.__name__ + ' ' + str(vars_) for q, vars_ in self)