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.
To install using pip
:
$ pip install logics
Or clone it from this repository:
$ git clone https://github.com/ariroffe/logics.git
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)
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.