Logics
Logics is a Python framework for mathematical logic. It aims at generality (being able to represent as many systems as possible), well-documented and readable code and minimal dependencies, rather than speed. Some of its intended applications are educational software (e.g. TAUT, which uses a previous version of this package), and quick prototyping of ideas for research purposes.
To see the documentation, visit this link.
Installation
To install using pip:
$ pip install logics
Or clone it from this repository:
$ git clone https://github.com/ariroffe/logics.git
Examples
The following are some random examples of what you can do with this package. For a full specification of the functionality see the documentation.
Define a language:
>>> from logics.classes.propositional import Language
>>> language = Language(atomics=['p', 'q', 'r'],
... constant_arity_dict={'~': 1, '∧': 2},
... sentential_constants=['⊥', '⊤'],
... metavariables=['A', 'B', 'C'],
... context_variables=['Γ', 'Δ', 'Σ', 'Λ', 'Π', 'Θ'])
Construct formulae and inferences/metainferences, and use specific methods of those classes:
>>> from logics.utils.parsers import classical_parser
>>> formula = classical_parser.parse('~(p and not q)')
>>> inference = classical_parser.parse('(p / p) // (q / p & not p)')
>>> type(formula)
<class 'logics.classes.propositional.formula.Formula'>
>>> type(inference)
<class 'logics.classes.propositional.inference.Inference'>
>>> formula.depth
3
>>> formula.is_well_formed(language)
True
>>> formula.is_instance_of(classical_parser.parse('not (A and B)'), language)
True
>>> formula2 = formula.substitute(classical_parser.parse("p"), classical_parser.parse("p or q"))
>>> classical_parser.unparse(formula2)
'~((p ∨ q) ∧ ~q)'
>>> inference.level
2
Define a mixed many-valued model theory, and use it:
>>> from logics.instances.propositional.languages import classical_infinite_language_with_sent_constants
>>> from logics.classes.propositional.semantics import MixedManyValuedSemantics
>>> trivalued_truth_values = ['1', 'i', '0']
>>> trivalued_truth_functions = {
... '~': ['0', 'i', '1'],
... '∧': [ #1 #i #0
... ['1', 'i', '0'], # 1
... ['i', 'i', '0'], # i
... ['0', '0', '0']], # 0
... '∨': [ #1 #i #0
... ['1', '1', '1'], # 1
... ['1', 'i', 'i'], # i
... ['1', 'i', '0']], # 0
... '→': [ #1 #i #0
... ['1', 'i', '0'], # 1
... ['1', 'i', 'i'], # i
... ['1', '1', '1']], # 0
... '↔': [ #1 #i #0
... ['1', 'i', '0'], # 1
... ['i', 'i', 'i'], # i
... ['0', 'i', '1']], # 0
... }
>>> ST = MixedManyValuedSemantics(language=classical_infinite_language_with_sent_constants,
... truth_values=trivalued_truth_values,
... premise_designated_values=['1'],
... conclusion_designated_values=['1', 'i'],
... truth_function_dict=trivalued_truth_functions,
... sentential_constant_values_dict={'⊥': '0', '⊤': '1'},
... name='ST')
>>> ST.valuation(classical_parser.parse('p then q'), {'p': '1', 'q': 'i'})
'i'
>>> ST.satisfies(classical_parser.parse('(A / B), (B / C) // (A / C)'), {'A': '1', 'B': 'i', 'C': '0'})
False
>>> ST.is_valid(classical_parser.parse('p and ~p / q'))
True
>>> ST.is_locally_valid(classical_parser.parse('(A / B), (B / C) // (A / C)'))
False
>>> ST.is_globally_valid(classical_parser.parse('(A / B), (B / C) // (A / C)'))
True
>>> # There are also some predefined systems (ST is one of them, the above was unnecesary)
>>> from logics.instances.propositional.many_valued_semantics import TS_mvl_semantics as TS
>>> from logics.instances.propositional.many_valued_semantics import LP_mvl_semantics as LP
>>> LP.is_valid(classical_parser.parse('p and ~p / q'))
False
>>> from logics.classes.propositional.semantics import MixedMetainferentialSemantics
>>> TSST = MixedMetainferentialSemantics([TS, ST])
>>> TSST.is_locally_valid(classical_parser.parse('(A / B), (B / C) // (A / C)'))
True
As in TAUT, logics has natural deduction module:
>>> # You can define your own natural deduction system, here we will just import a predefined instance:
>>> from logics.instances.propositional.natural_deduction import classical_natural_deduction_system
>>> from logics.utils.solvers import classical_natural_deduction_solver
>>> derivation = classical_natural_deduction_solver.solve(classical_parser.parse("A → B, ~B / ~A"))
>>> derivation.print_derivation(classical_parser)
0. A → B; premise; []
1. ~B; premise; []
| 2. A; supposition; []
| 3. B; E→; [0, 2]
| 4. ⊥; E~; [1, 3]
5. ~A; I~; [2, 4]
>>> classical_natural_deduction_system.is_correct_derivation(derivation)
True
>>> # Also in first order!
>>> from logics.utils.parsers import classical_predicate_parser
>>> from logics.utils.solvers.first_order_natural_deduction import first_order_natural_deduction_solver
>>> derivation = first_order_natural_deduction_solver.solve(classical_predicate_parser.parse('~∃x P(x) / ∀x ~P(x)'))
>>> derivation.print_derivation(classical_predicate_parser)
0. ~∃x P(x); premise; []
| 1. P(a); supposition; []
| 2. ∃x P(x); I∃; [1]
| 3. ⊥; E~; [0, 2]
4. ~P(a); I~; [1, 3]
5. ∀x ~P(x); I∀; [4]
I have now added tableaux systems:
>>> from logics.classes.propositional.proof_theories import TableauxNode
>>> # Again, you can define your own tableaux system, here I use a predefined instance
>>> from logics.instances.propositional.tableaux import classical_tableaux_system
>>> n1 = TableauxNode(content=classical_parser.parse('~~~~p'))
>>> n2 = TableauxNode(content=classical_parser.parse('~p'), parent=n1)
>>> n3 = TableauxNode(content=classical_parser.parse('~~p'), justification='R~~', parent=n2)
>>> n1.print_tree(classical_parser)
(~~~p)
└── ~p
└── ~~p (R~~)
>>> classical_tableaux_system.node_is_closed(n2)
False
>>> classical_tableaux_system.tree_is_closed(n1)
True
>>> classical_tableaux_system.rule_is_applicable(n1, 'R~~')
True
>>> classical_tableaux_system.is_correct_tree(n1)
True
>>> # The tableaux solver (unlike ND one) will work for any arbitrary system you define
>>> tree = classical_tableaux_system.solve_tree(classical_parser.parse("~(p ∧ q) / ~p ∨ ~q"))
>>> tree.print_tree(classical_parser)
~(p ∧ q)
└── ~(~p ∨ ~q)
├── ~p (R~∧)
│ └── ~~p (R~∨)
│ └── ~~q (R~∨)
└── ~q (R~∧)
└── ~~p (R~∨)
└── ~~q (R~∨)
>>> # There is even a tableaux class for indexed tableaux, here is a predefined instance
>>> from logics.instances.propositional.tableaux import LP_tableaux_system
>>> tree2 = LP_tableaux_system.solve_tree(classical_parser.parse("~(p ∨ q) / ~~p ∧ ~~q"))
>>> tree2.print_tree(classical_parser)
~(p ∨ q), 1
└── ~~p ∧ ~~q, 0
└── ~p ∧ ~q, 1 (R~∨1)
├── ~~p, 0 (R∧0)
│ └── ~p, 1 (R∧1)
│ └── ~q, 1 (R∧1)
│ └── p, 0 (R~~0)
└── ~~q, 0 (R∧0)
└── ~p, 1 (R∧1)
└── ~q, 1 (R∧1)
└── q, 0 (R~~0)
And sequent calculi:
>>> sequent = classical_parser.parse("Gamma, A ==> B, Delta")
>>> classical_parser.unparse(sequent)
'Γ, A ⇒ B, Δ'
>>> sequent2 = sequent.substitute(language, "Γ", classical_parser.parse("D"))
>>> classical_parser.unparse(sequent2)
'D, A ⇒ B, Δ'
>>> # Again, you can define your sequent calculus, here I use a predefined instance
>>> from logics.instances.propositional.sequents import LK
>>> LK.sequent_is_axiom(classical_parser.parse("p or q ==> p or q"))
True
>>> from logics.classes.propositional.proof_theories import SequentNode
>>> n1 = SequentNode(content=classical_parser.parse('A ==> A'), justification='identity')
>>> n2 = SequentNode(content=classical_parser.parse('A ==> A, Delta'), justification='WR', children=[n1])
>>> n3 = SequentNode(content=classical_parser.parse('Gamma, A ==> A, Delta'), justification='WL', children=[n2])
>>> n3.print_tree(classical_parser) # the root of the tree is the derived node
Γ, A ⇒ A, Δ (WL)
└── A ⇒ A, Δ (WR)
└── A ⇒ A (identity)
>>> LK.is_correct_tree(n1)
True
>>> LK.tree_is_closed(n3)
True
>>> # There is also a solver that will work whenever your system has no elimination rules
>>> # A system that the solver can work with easily, see the docs for a description
>>> from logics.instances.propositional.sequents import LKminEA
>>> tree = LKminEA.reduce(classical_parser.parse("Gamma ==> A or ~A"))
>>> tree.print_tree(classical_parser)
Γ ⇒ A ∨ ~A (∨R1)
└── Γ ⇒ A, ~A (~R)
└── Γ, A ⇒ A (WL)
└── A ⇒ A (identity)
There are also some predicate logic tools:
>>> from logics.classes.predicate.semantics import Model
>>> model = Model({
... 'domain': {1, 2},
... 'a': 1,
... 'b': 2,
... 'P': {1},
... 'R': {(1,1), (1,2)},
... 'f': {((1,), 2), ((2,), 1)},
... 'g': {((1, 1), 1), ((1, 2), 2), ((2, 1), 1), ((2, 2), 2)}
... })
>>> model.denotation('f')
{((2,), 1), ((1,), 1)}
>>> # Again, predefined instance, you can define this yourself
>>> from logics.instances.predicate.model_semantics import classical_functional_model_semantics
>>> classical_functional_model_semantics.valuation(parser.parse("P(a)"), model)
'1'
>>> classical_functional_model_semantics.valuation(parser.parse("R(a, b)"), model)
'1'
>>> classical_functional_model_semantics.valuation(parser.parse("R(f(a), g(f(a), b))"), model)
'0'
>>> classical_functional_model_semantics.valuation(parser.parse("exists x (P(f(x)))"), model)
'1'
>>> classical_functional_model_semantics.valuation(parser.parse("forall X (exists x (X(f(x))))"), model)
'0'
>>> # You can also define theories with fixed denotations for some terms by subclassing Model
>>> from itertools import count
>>> from logics.instances.predicate.model_subclasses import ArithmeticModel
>>> from logics.utils.parsers.predicate_parser import arithmetic_parser
>>> from logics.instances.predicate.model_semantics import arithmetic_model_semantics
>>> arithmetic_model = ArithmeticModel({'domain': count(0)})
>>> arithmetic_model_semantics.valuation(arithmetic_parser.parse("s(0) > 0"), arithmetic_model)
'1'
>>> arithmetic_model_semantics.valuation(arithmetic_parser.parse("s(0) + s(0) = s(s(0))"), arithmetic_model)
'1'
>>> arithmetic_model_semantics.valuation(arithmetic_parser.parse("exists x (x = s(0))"), arithmetic_model)
'1'
And many more things! (see the documentation)
Acknowledgements
logics is a project by Ariel Jonathan Roffé (CONICET / University of Buenos Aires)
Contributors to the project:
Joaquin S. Toranzo Calderon (mapped_logics module)
The author also wishes to thank the Buenos Aires Logic Group who supported this project.
Metadata
Release files for logics 1.10.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| logics-1.10.4.tar.gz | 150.5 kB | Without provenance | Details for logics-1.10.4.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| logics-1.10.4-py3-none-any.whl | Python 3 | none | any | Details for logics-1.10.4-py3-none-any.whl |
Total release size: 320.8 kB
Release files / logics-1.10.4.tar.gz
| Download URL | logics-1.10.4.tar.gz |
|---|---|
| Size | 150.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
24bc9146bdd2017177e5f94b99c47a8af616d1ba75334b495c8ca2a2e5eb3219
|
|
BLAKE2b-256 checksum How to use checksums |
c8fad8d43bbc9e55b5f422f91467b7416e491792cfe6bd88f69ac733dae55ad2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.3
|
Release files / logics-1.10.4-py3-none-any.whl
| Download URL | logics-1.10.4-py3-none-any.whl |
|---|---|
| Size | 170.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
894bd86139d8121ad3cedcb9324bfb33fc80142722a65a6f6aab9981d74f6840
|
|
BLAKE2b-256 checksum How to use checksums |
b68747b24cdbd5bfcb0547462c2af0d7e3684da16172f7ac43e2c36b71a6a2e2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.3
|
Security for logics 1.10.4
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.