passagemath-standard-no-symbolics 10.6.45__cp313-cp313-macosx_13_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_standard_no_symbolics/__init__.py +1 -0
- passagemath_standard_no_symbolics-10.6.45.data/scripts/sage-grep +5 -0
- passagemath_standard_no_symbolics-10.6.45.data/scripts/sage-grepdoc +5 -0
- passagemath_standard_no_symbolics-10.6.45.data/scripts/sage-list-packages +103 -0
- passagemath_standard_no_symbolics-10.6.45.dist-info/METADATA +150 -0
- passagemath_standard_no_symbolics-10.6.45.dist-info/RECORD +83 -0
- passagemath_standard_no_symbolics-10.6.45.dist-info/WHEEL +6 -0
- passagemath_standard_no_symbolics-10.6.45.dist-info/top_level.txt +2 -0
- sage/all.py +207 -0
- sage/all_cmdline.py +36 -0
- sage/cli/__init__.py +61 -0
- sage/cli/__main__.py +5 -0
- sage/cli/eval_cmd.py +45 -0
- sage/cli/eval_cmd_test.py +25 -0
- sage/cli/interactive_shell_cmd.py +28 -0
- sage/cli/notebook_cmd.py +51 -0
- sage/cli/notebook_cmd_test.py +39 -0
- sage/cli/options.py +26 -0
- sage/cli/run_file_cmd.py +50 -0
- sage/cli/version_cmd.py +26 -0
- sage/databases/all.py +83 -0
- sage/databases/cubic_hecke_db.py +1527 -0
- sage/dynamics/all.py +31 -0
- sage/dynamics/surface_dynamics_deprecation.py +32 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/ext_data/mwrank/PRIMES +1 -0
- sage/ext_data/nbconvert/postprocess.py +48 -0
- sage/ext_data/nbconvert/rst_sage.tpl +99 -0
- sage/ext_data/nodoctest +0 -0
- sage/ext_data/notebook-ipython/kernel.json.in +11 -0
- sage/ext_data/notebook-ipython/logo-64x64.png +0 -0
- sage/ext_data/notebook-ipython/logo.svg +352 -0
- sage/ext_data/valgrind/pyalloc.supp +58 -0
- sage/ext_data/valgrind/sage-additional.supp +417 -0
- sage/ext_data/valgrind/sage.supp +43 -0
- sage/ext_data/valgrind/valgrind-python.supp +480 -0
- sage/geometry/all.py +12 -0
- sage/groups/matrix_gps/pickling_overrides.py +110 -0
- sage/homology/tests.py +66 -0
- sage/interacts/algebra.py +20 -0
- sage/interacts/all.py +25 -0
- sage/interacts/calculus.py +24 -0
- sage/interacts/fractals.py +18 -0
- sage/interacts/geometry.py +19 -0
- sage/interacts/library.py +1950 -0
- sage/interacts/library_cython.cpython-313-darwin.so +0 -0
- sage/interacts/statistics.py +19 -0
- sage/interfaces/axiom.py +1002 -0
- sage/interfaces/kash.py +834 -0
- sage/interfaces/lie.py +950 -0
- sage/interfaces/matlab.py +413 -0
- sage/interfaces/mupad.py +686 -0
- sage/interfaces/octave.py +858 -0
- sage/interfaces/phc.py +943 -0
- sage/interfaces/psage.py +189 -0
- sage/interfaces/qsieve.py +4 -0
- sage/interfaces/r.py +2096 -0
- sage/interfaces/read_data.py +46 -0
- sage/interfaces/scilab.py +576 -0
- sage/interfaces/tests.py +81 -0
- sage/libs/all.py +11 -0
- sage/libs/cremona/__init__.py +0 -0
- sage/libs/mwrank/__init__.py +0 -0
- sage/logic/all.py +3 -0
- sage/logic/booleval.py +160 -0
- sage/logic/boolformula.py +1490 -0
- sage/logic/logic.py +856 -0
- sage/logic/logicparser.py +696 -0
- sage/logic/logictable.py +272 -0
- sage/logic/propcalc.py +311 -0
- sage/misc/all.py +28 -0
- sage/misc/lazy_attribute.pyi +11 -0
- sage/rings/all.py +48 -0
- sage/rings/commutative_algebra.py +38 -0
- sage/rings/finite_rings/all.py +21 -0
- sage/rings/numbers_abc.py +58 -0
- sage/rings/polynomial/all.py +22 -0
- sage/rings/polynomial/convolution.py +421 -0
- sage/symbolic/all__sagemath_standard_no_symbolics.py +0 -0
|
@@ -0,0 +1,1490 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
Boolean Formulas
|
|
3
|
+
|
|
4
|
+
Formulas consist of the operators ``&``, ``|``, ``~``, ``^``, ``->``, ``<->``,
|
|
5
|
+
corresponding to ``and``, ``or``, ``not``, ``xor``, ``if...then``, ``if and
|
|
6
|
+
only if``. Operators can be applied to variables that consist of a leading
|
|
7
|
+
letter and trailing underscores and alphanumerics. Parentheses may be used
|
|
8
|
+
to explicitly show order of operation.
|
|
9
|
+
|
|
10
|
+
EXAMPLES:
|
|
11
|
+
|
|
12
|
+
Create boolean formulas and combine them with
|
|
13
|
+
:meth:`~sage.logic.boolformula.BooleanFormula.ifthen()` method::
|
|
14
|
+
|
|
15
|
+
sage: import sage.logic.propcalc as propcalc
|
|
16
|
+
sage: f = propcalc.formula("a&((b|c)^a->c)<->b")
|
|
17
|
+
sage: g = propcalc.formula("boolean<->algebra")
|
|
18
|
+
sage: (f&~g).ifthen(f)
|
|
19
|
+
((a&((b|c)^a->c)<->b)&(~(boolean<->algebra)))->(a&((b|c)^a->c)<->b)
|
|
20
|
+
|
|
21
|
+
We can create a truth table from a formula::
|
|
22
|
+
|
|
23
|
+
sage: f.truthtable()
|
|
24
|
+
a b c value
|
|
25
|
+
False False False True
|
|
26
|
+
False False True True
|
|
27
|
+
False True False False
|
|
28
|
+
False True True False
|
|
29
|
+
True False False True
|
|
30
|
+
True False True False
|
|
31
|
+
True True False True
|
|
32
|
+
True True True True
|
|
33
|
+
sage: f.truthtable(end=3)
|
|
34
|
+
a b c value
|
|
35
|
+
False False False True
|
|
36
|
+
False False True True
|
|
37
|
+
False True False False
|
|
38
|
+
sage: f.truthtable(start=4)
|
|
39
|
+
a b c value
|
|
40
|
+
True False False True
|
|
41
|
+
True False True False
|
|
42
|
+
True True False True
|
|
43
|
+
True True True True
|
|
44
|
+
sage: propcalc.formula("a").truthtable()
|
|
45
|
+
a value
|
|
46
|
+
False False
|
|
47
|
+
True True
|
|
48
|
+
|
|
49
|
+
Now we can evaluate the formula for a given set of inputs::
|
|
50
|
+
|
|
51
|
+
sage: f.evaluate({'a':True, 'b':False, 'c':True})
|
|
52
|
+
False
|
|
53
|
+
sage: f.evaluate({'a':False, 'b':False, 'c':True})
|
|
54
|
+
True
|
|
55
|
+
|
|
56
|
+
And we can convert a boolean formula to conjunctive normal form::
|
|
57
|
+
|
|
58
|
+
sage: f.convert_cnf_table()
|
|
59
|
+
sage: f
|
|
60
|
+
(a|~b|c)&(a|~b|~c)&(~a|b|~c)
|
|
61
|
+
sage: f.convert_cnf_recur()
|
|
62
|
+
sage: f
|
|
63
|
+
(a|~b|c)&(a|~b|~c)&(~a|b|~c)
|
|
64
|
+
|
|
65
|
+
Or determine if an expression is satisfiable, a contradiction, or a tautology::
|
|
66
|
+
|
|
67
|
+
sage: f = propcalc.formula("a|b")
|
|
68
|
+
sage: f.is_satisfiable()
|
|
69
|
+
True
|
|
70
|
+
sage: f = f & ~f
|
|
71
|
+
sage: f.is_satisfiable()
|
|
72
|
+
False
|
|
73
|
+
sage: f.is_contradiction()
|
|
74
|
+
True
|
|
75
|
+
sage: f = f | ~f
|
|
76
|
+
sage: f.is_tautology()
|
|
77
|
+
True
|
|
78
|
+
|
|
79
|
+
The equality operator compares semantic equivalence::
|
|
80
|
+
|
|
81
|
+
sage: f = propcalc.formula("(a|b)&c")
|
|
82
|
+
sage: g = propcalc.formula("c&(b|a)")
|
|
83
|
+
sage: f == g
|
|
84
|
+
True
|
|
85
|
+
sage: g = propcalc.formula("a|b&c")
|
|
86
|
+
sage: f == g
|
|
87
|
+
False
|
|
88
|
+
|
|
89
|
+
It is an error to create a formula with bad syntax::
|
|
90
|
+
|
|
91
|
+
sage: propcalc.formula("")
|
|
92
|
+
Traceback (most recent call last):
|
|
93
|
+
...
|
|
94
|
+
SyntaxError: malformed statement
|
|
95
|
+
sage: propcalc.formula("a&b~(c|(d)")
|
|
96
|
+
Traceback (most recent call last):
|
|
97
|
+
...
|
|
98
|
+
SyntaxError: malformed statement
|
|
99
|
+
sage: propcalc.formula("a&&b")
|
|
100
|
+
Traceback (most recent call last):
|
|
101
|
+
...
|
|
102
|
+
SyntaxError: malformed statement
|
|
103
|
+
sage: propcalc.formula("a&b a")
|
|
104
|
+
Traceback (most recent call last):
|
|
105
|
+
...
|
|
106
|
+
SyntaxError: malformed statement
|
|
107
|
+
|
|
108
|
+
It is also an error to not abide by the naming conventions::
|
|
109
|
+
|
|
110
|
+
sage: propcalc.formula("~a&9b")
|
|
111
|
+
Traceback (most recent call last):
|
|
112
|
+
...
|
|
113
|
+
NameError: invalid variable name 9b: identifiers must begin with a letter and contain only alphanumerics and underscores
|
|
114
|
+
|
|
115
|
+
AUTHORS:
|
|
116
|
+
|
|
117
|
+
- Chris Gorecki (2006): initial version
|
|
118
|
+
|
|
119
|
+
- Paul Scurek (2013-08-03): added polish_notation, full_tree,
|
|
120
|
+
updated docstring formatting
|
|
121
|
+
|
|
122
|
+
- Paul Scurek (2013-08-08): added
|
|
123
|
+
:meth:`~sage.logic.boolformula.BooleanFormula.implies()`
|
|
124
|
+
"""
|
|
125
|
+
# *****************************************************************************
|
|
126
|
+
# Copyright (C) 2006 William Stein <wstein.gmail.com>
|
|
127
|
+
# Copyright (C) 2006 Chris Gorecki <chris.k.gorecki@gmail.com>
|
|
128
|
+
# Copyright (C) 2013 Paul Scurek <scurek86@gmail.com>
|
|
129
|
+
#
|
|
130
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
131
|
+
# as published by the Free Software Foundation; either version 2 of
|
|
132
|
+
# the License, or (at your option) any later version.
|
|
133
|
+
# https://www.gnu.org/licenses/
|
|
134
|
+
# *****************************************************************************
|
|
135
|
+
|
|
136
|
+
from . import booleval
|
|
137
|
+
from . import logictable
|
|
138
|
+
from . import logicparser
|
|
139
|
+
# import boolopt
|
|
140
|
+
from sage.misc.flatten import flatten
|
|
141
|
+
|
|
142
|
+
latex_operators = [('&', '\\wedge '),
|
|
143
|
+
('|', '\\vee '),
|
|
144
|
+
('~', '\\neg '),
|
|
145
|
+
('^', '\\oplus '),
|
|
146
|
+
('<->', '\\leftrightarrow '),
|
|
147
|
+
('->', '\\rightarrow ')]
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class BooleanFormula:
|
|
151
|
+
"""
|
|
152
|
+
Boolean formulas.
|
|
153
|
+
|
|
154
|
+
INPUT:
|
|
155
|
+
|
|
156
|
+
- ``self`` -- calling object
|
|
157
|
+
|
|
158
|
+
- ``exp`` -- string; this contains the boolean expression
|
|
159
|
+
to be manipulated
|
|
160
|
+
|
|
161
|
+
- ``tree`` -- list; this contains the parse tree of the expression
|
|
162
|
+
|
|
163
|
+
- ``vo`` -- list; this contains the variables in the expression, in the
|
|
164
|
+
order that they appear; each variable only occurs once in the list
|
|
165
|
+
"""
|
|
166
|
+
__expression = ""
|
|
167
|
+
__tree = []
|
|
168
|
+
__vars_order = []
|
|
169
|
+
|
|
170
|
+
def __init__(self, exp, tree, vo):
|
|
171
|
+
r"""
|
|
172
|
+
Initialize the data fields.
|
|
173
|
+
|
|
174
|
+
EXAMPLES:
|
|
175
|
+
|
|
176
|
+
This example illustrates the creation of a statement::
|
|
177
|
+
|
|
178
|
+
sage: import sage.logic.propcalc as propcalc
|
|
179
|
+
sage: s = propcalc.formula("a&b|~(c|a)")
|
|
180
|
+
sage: s
|
|
181
|
+
a&b|~(c|a)
|
|
182
|
+
"""
|
|
183
|
+
self.__expression = exp.replace(' ', '')
|
|
184
|
+
self.__tree = tree
|
|
185
|
+
self.__vars_order = vo
|
|
186
|
+
|
|
187
|
+
def __repr__(self):
|
|
188
|
+
r"""
|
|
189
|
+
Return a string representation of this statement.
|
|
190
|
+
|
|
191
|
+
EXAMPLES::
|
|
192
|
+
|
|
193
|
+
sage: import sage.logic.propcalc as propcalc
|
|
194
|
+
sage: propcalc.formula("man->monkey&human")
|
|
195
|
+
man->monkey&human
|
|
196
|
+
"""
|
|
197
|
+
return self.__expression
|
|
198
|
+
|
|
199
|
+
def _latex_(self):
|
|
200
|
+
r"""
|
|
201
|
+
Return a LaTeX representation of this statement.
|
|
202
|
+
|
|
203
|
+
OUTPUT: string containing the latex code for the statement
|
|
204
|
+
|
|
205
|
+
EXAMPLES::
|
|
206
|
+
|
|
207
|
+
sage: import sage.logic.propcalc as propcalc
|
|
208
|
+
sage: s = propcalc.formula("man->monkey&human")
|
|
209
|
+
sage: latex(s)
|
|
210
|
+
man\rightarrow monkey\wedge human
|
|
211
|
+
|
|
212
|
+
sage: f = propcalc.formula("a & ((~b | c) ^ a -> c) <-> ~b")
|
|
213
|
+
sage: latex(f)
|
|
214
|
+
a\wedge ((\neg b\vee c)\oplus a\rightarrow c)\leftrightarrow \neg b
|
|
215
|
+
"""
|
|
216
|
+
latex_expression = self.__expression
|
|
217
|
+
for old, new in latex_operators:
|
|
218
|
+
latex_expression = latex_expression.replace(old, new)
|
|
219
|
+
return latex_expression
|
|
220
|
+
|
|
221
|
+
def polish_notation(self):
|
|
222
|
+
r"""
|
|
223
|
+
Convert the calling boolean formula into polish notation.
|
|
224
|
+
|
|
225
|
+
OUTPUT: string representation of the formula in polish notation
|
|
226
|
+
|
|
227
|
+
EXAMPLES:
|
|
228
|
+
|
|
229
|
+
This example illustrates converting a formula to polish notation::
|
|
230
|
+
|
|
231
|
+
sage: import sage.logic.propcalc as propcalc
|
|
232
|
+
sage: f = propcalc.formula("~~a|(c->b)")
|
|
233
|
+
sage: f.polish_notation()
|
|
234
|
+
'|~~a->cb'
|
|
235
|
+
|
|
236
|
+
sage: g = propcalc.formula("(a|~b)->c")
|
|
237
|
+
sage: g.polish_notation()
|
|
238
|
+
'->|a~bc'
|
|
239
|
+
|
|
240
|
+
AUTHORS:
|
|
241
|
+
|
|
242
|
+
- Paul Scurek (2013-08-03)
|
|
243
|
+
"""
|
|
244
|
+
return ''.join(flatten(logicparser.polish_parse(repr(self))))
|
|
245
|
+
|
|
246
|
+
def tree(self):
|
|
247
|
+
r"""
|
|
248
|
+
Return the parse tree of this boolean expression.
|
|
249
|
+
|
|
250
|
+
OUTPUT: the parse tree as a nested list
|
|
251
|
+
|
|
252
|
+
EXAMPLES:
|
|
253
|
+
|
|
254
|
+
This example illustrates how to find the parse tree of a boolean
|
|
255
|
+
formula::
|
|
256
|
+
|
|
257
|
+
sage: import sage.logic.propcalc as propcalc
|
|
258
|
+
sage: s = propcalc.formula("man -> monkey & human")
|
|
259
|
+
sage: s.tree()
|
|
260
|
+
['->', 'man', ['&', 'monkey', 'human']]
|
|
261
|
+
|
|
262
|
+
::
|
|
263
|
+
|
|
264
|
+
sage: f = propcalc.formula("a & ((~b | c) ^ a -> c) <-> ~b")
|
|
265
|
+
sage: f.tree()
|
|
266
|
+
['<->',
|
|
267
|
+
['&', 'a', ['->', ['^', ['|', ['~', 'b', None], 'c'], 'a'], 'c']],
|
|
268
|
+
['~', 'b', None]]
|
|
269
|
+
|
|
270
|
+
.. NOTE::
|
|
271
|
+
|
|
272
|
+
This function is used by other functions in the logic module
|
|
273
|
+
that perform semantic operations on a boolean formula.
|
|
274
|
+
"""
|
|
275
|
+
return self.__tree
|
|
276
|
+
|
|
277
|
+
def full_tree(self):
|
|
278
|
+
r"""
|
|
279
|
+
Return a full syntax parse tree of the calling formula.
|
|
280
|
+
|
|
281
|
+
OUTPUT: the full syntax parse tree as a nested list
|
|
282
|
+
|
|
283
|
+
EXAMPLES:
|
|
284
|
+
|
|
285
|
+
This example shows how to find the full syntax parse tree
|
|
286
|
+
of a formula::
|
|
287
|
+
|
|
288
|
+
sage: import sage.logic.propcalc as propcalc
|
|
289
|
+
sage: s = propcalc.formula("a->(b&c)")
|
|
290
|
+
sage: s.full_tree()
|
|
291
|
+
['->', 'a', ['&', 'b', 'c']]
|
|
292
|
+
|
|
293
|
+
sage: t = propcalc.formula("a & ((~b | c) ^ a -> c) <-> ~b")
|
|
294
|
+
sage: t.full_tree()
|
|
295
|
+
['<->', ['&', 'a', ['->', ['^', ['|', ['~', 'b'], 'c'], 'a'], 'c']], ['~', 'b']]
|
|
296
|
+
|
|
297
|
+
sage: f = propcalc.formula("~~(a&~b)")
|
|
298
|
+
sage: f.full_tree()
|
|
299
|
+
['~', ['~', ['&', 'a', ['~', 'b']]]]
|
|
300
|
+
|
|
301
|
+
.. NOTE::
|
|
302
|
+
|
|
303
|
+
This function is used by other functions in the logic module
|
|
304
|
+
that perform syntactic operations on a boolean formula.
|
|
305
|
+
|
|
306
|
+
AUTHORS:
|
|
307
|
+
|
|
308
|
+
- Paul Scurek (2013-08-03)
|
|
309
|
+
"""
|
|
310
|
+
return logicparser.polish_parse(repr(self))
|
|
311
|
+
|
|
312
|
+
def __or__(self, other):
|
|
313
|
+
r"""
|
|
314
|
+
Overload the ``|`` operator to 'or' two statements together.
|
|
315
|
+
|
|
316
|
+
INPUT:
|
|
317
|
+
|
|
318
|
+
- ``other`` -- boolean formula; this is the statement
|
|
319
|
+
on the right side of the operator
|
|
320
|
+
|
|
321
|
+
OUTPUT:
|
|
322
|
+
|
|
323
|
+
A boolean formula of the form ``self | other``.
|
|
324
|
+
|
|
325
|
+
EXAMPLES:
|
|
326
|
+
|
|
327
|
+
This example illustrates combining two formulas with ``|``::
|
|
328
|
+
|
|
329
|
+
sage: import sage.logic.propcalc as propcalc
|
|
330
|
+
sage: s = propcalc.formula("a&b")
|
|
331
|
+
sage: f = propcalc.formula("c^d")
|
|
332
|
+
sage: s | f
|
|
333
|
+
(a&b)|(c^d)
|
|
334
|
+
"""
|
|
335
|
+
return self.add_statement(other, '|')
|
|
336
|
+
|
|
337
|
+
def __and__(self, other):
|
|
338
|
+
r"""
|
|
339
|
+
Overload the ``&`` operator to 'and' two statements together.
|
|
340
|
+
|
|
341
|
+
INPUT:
|
|
342
|
+
|
|
343
|
+
- ``other`` -- boolean formula; this is the formula on
|
|
344
|
+
the right side of the operator
|
|
345
|
+
|
|
346
|
+
OUTPUT: a boolean formula of the form ``self & other``
|
|
347
|
+
|
|
348
|
+
EXAMPLES:
|
|
349
|
+
|
|
350
|
+
This example shows how to combine two formulas with ``&``::
|
|
351
|
+
|
|
352
|
+
sage: import sage.logic.propcalc as propcalc
|
|
353
|
+
sage: s = propcalc.formula("a&b")
|
|
354
|
+
sage: f = propcalc.formula("c^d")
|
|
355
|
+
sage: s & f
|
|
356
|
+
(a&b)&(c^d)
|
|
357
|
+
"""
|
|
358
|
+
return self.add_statement(other, '&')
|
|
359
|
+
|
|
360
|
+
def __xor__(self, other):
|
|
361
|
+
r"""
|
|
362
|
+
Overload the ``^`` operator to 'xor' two statements together.
|
|
363
|
+
|
|
364
|
+
INPUT:
|
|
365
|
+
|
|
366
|
+
- ``other`` -- boolean formula; this is the formula on
|
|
367
|
+
the right side of the operator
|
|
368
|
+
|
|
369
|
+
OUTPUT: a boolean formula of the form ``self ^ other``
|
|
370
|
+
|
|
371
|
+
EXAMPLES:
|
|
372
|
+
|
|
373
|
+
This example illustrates how to combine two formulas with ``^``::
|
|
374
|
+
|
|
375
|
+
sage: import sage.logic.propcalc as propcalc
|
|
376
|
+
sage: s = propcalc.formula("a&b")
|
|
377
|
+
sage: f = propcalc.formula("c^d")
|
|
378
|
+
sage: s ^ f
|
|
379
|
+
(a&b)^(c^d)
|
|
380
|
+
"""
|
|
381
|
+
return self.add_statement(other, '^')
|
|
382
|
+
|
|
383
|
+
def __pow__(self, other):
|
|
384
|
+
r"""
|
|
385
|
+
Overload the ``^`` operator to 'xor' two statements together.
|
|
386
|
+
|
|
387
|
+
INPUT:
|
|
388
|
+
|
|
389
|
+
- ``other`` -- boolean formula; this is the formula on
|
|
390
|
+
the right side of the operator
|
|
391
|
+
|
|
392
|
+
OUTPUT: a boolean formula of the form ``self ^ other``
|
|
393
|
+
|
|
394
|
+
EXAMPLES:
|
|
395
|
+
|
|
396
|
+
This example shows how to combine two formulas with ``^``::
|
|
397
|
+
|
|
398
|
+
sage: import sage.logic.propcalc as propcalc
|
|
399
|
+
sage: s = propcalc.formula("a&b")
|
|
400
|
+
sage: f = propcalc.formula("c^d")
|
|
401
|
+
sage: s ^ f
|
|
402
|
+
(a&b)^(c^d)
|
|
403
|
+
|
|
404
|
+
.. TODO::
|
|
405
|
+
|
|
406
|
+
This function seems to be identical to ``__xor__``.
|
|
407
|
+
Thus, this function should be replaced with ``__xor__`` everywhere
|
|
408
|
+
that it appears in the logic module. Then it can be deleted
|
|
409
|
+
altogether.
|
|
410
|
+
"""
|
|
411
|
+
return self.add_statement(other, '^')
|
|
412
|
+
|
|
413
|
+
def __invert__(self):
|
|
414
|
+
r"""
|
|
415
|
+
Overload the ``~`` operator to 'not' a statement.
|
|
416
|
+
|
|
417
|
+
OUTPUT: a boolean formula of the form ``~self``
|
|
418
|
+
|
|
419
|
+
EXAMPLES:
|
|
420
|
+
|
|
421
|
+
This example shows how to negate a boolean formula::
|
|
422
|
+
|
|
423
|
+
sage: import sage.logic.propcalc as propcalc
|
|
424
|
+
sage: s = propcalc.formula("a&b")
|
|
425
|
+
sage: ~s
|
|
426
|
+
~(a&b)
|
|
427
|
+
"""
|
|
428
|
+
exp = '~(' + self.__expression + ')'
|
|
429
|
+
parse_tree, vars_order = logicparser.parse(exp)
|
|
430
|
+
return BooleanFormula(exp, parse_tree, vars_order)
|
|
431
|
+
|
|
432
|
+
def ifthen(self, other):
|
|
433
|
+
r"""
|
|
434
|
+
Combine two formulas with the ``->`` operator.
|
|
435
|
+
|
|
436
|
+
INPUT:
|
|
437
|
+
|
|
438
|
+
- ``other`` -- boolean formula; this is the formula
|
|
439
|
+
on the right side of the operator
|
|
440
|
+
|
|
441
|
+
OUTPUT:
|
|
442
|
+
|
|
443
|
+
A boolean formula of the form ``self -> other``.
|
|
444
|
+
|
|
445
|
+
EXAMPLES:
|
|
446
|
+
|
|
447
|
+
This example illustrates how to combine two formulas with '->'::
|
|
448
|
+
|
|
449
|
+
sage: import sage.logic.propcalc as propcalc
|
|
450
|
+
sage: s = propcalc.formula("a&b")
|
|
451
|
+
sage: f = propcalc.formula("c^d")
|
|
452
|
+
sage: s.ifthen(f)
|
|
453
|
+
(a&b)->(c^d)
|
|
454
|
+
"""
|
|
455
|
+
return self.add_statement(other, '->')
|
|
456
|
+
|
|
457
|
+
def iff(self, other):
|
|
458
|
+
r"""
|
|
459
|
+
Combine two formulas with the ``<->`` operator.
|
|
460
|
+
|
|
461
|
+
INPUT:
|
|
462
|
+
|
|
463
|
+
- ``other`` -- boolean formula; this is the formula
|
|
464
|
+
on the right side of the operator
|
|
465
|
+
|
|
466
|
+
OUTPUT:
|
|
467
|
+
|
|
468
|
+
A boolean formula of the form ``self <-> other``.
|
|
469
|
+
|
|
470
|
+
EXAMPLES:
|
|
471
|
+
|
|
472
|
+
This example illustrates how to combine two formulas with '<->'::
|
|
473
|
+
|
|
474
|
+
sage: import sage.logic.propcalc as propcalc
|
|
475
|
+
sage: s = propcalc.formula("a&b")
|
|
476
|
+
sage: f = propcalc.formula("c^d")
|
|
477
|
+
sage: s.iff(f)
|
|
478
|
+
(a&b)<->(c^d)
|
|
479
|
+
"""
|
|
480
|
+
return self.add_statement(other, '<->')
|
|
481
|
+
|
|
482
|
+
def __eq__(self, other):
|
|
483
|
+
r"""
|
|
484
|
+
Overload the ``==`` operator to determine logical equivalence.
|
|
485
|
+
|
|
486
|
+
INPUT:
|
|
487
|
+
|
|
488
|
+
- ``other`` -- boolean formula; this is the formula
|
|
489
|
+
on the right side of the comparator
|
|
490
|
+
|
|
491
|
+
OUTPUT: a boolean value to be determined as follows:
|
|
492
|
+
|
|
493
|
+
- ``True`` if ``self`` and ``other`` are logically equivalent
|
|
494
|
+
|
|
495
|
+
- ``False`` if ``self`` and ``other`` are not logically equivalent
|
|
496
|
+
|
|
497
|
+
EXAMPLES:
|
|
498
|
+
|
|
499
|
+
This example shows how to determine logical equivalence::
|
|
500
|
+
|
|
501
|
+
sage: import sage.logic.propcalc as propcalc
|
|
502
|
+
sage: f = propcalc.formula("(a|b)&c")
|
|
503
|
+
sage: g = propcalc.formula("c&(b|a)")
|
|
504
|
+
sage: f == g
|
|
505
|
+
True
|
|
506
|
+
|
|
507
|
+
::
|
|
508
|
+
|
|
509
|
+
sage: g = propcalc.formula("a|b&c")
|
|
510
|
+
sage: f == g
|
|
511
|
+
False
|
|
512
|
+
"""
|
|
513
|
+
return self.equivalent(other)
|
|
514
|
+
|
|
515
|
+
def truthtable(self, start=0, end=-1):
|
|
516
|
+
r"""
|
|
517
|
+
Return a truth table for the calling formula.
|
|
518
|
+
|
|
519
|
+
INPUT:
|
|
520
|
+
|
|
521
|
+
- ``start`` -- (default: 0) an integer; this is the first
|
|
522
|
+
row of the truth table to be created
|
|
523
|
+
|
|
524
|
+
- ``end`` -- (default: -1) an integer; this is the last
|
|
525
|
+
row of the truth table to be created
|
|
526
|
+
|
|
527
|
+
OUTPUT: the truth table as a 2-D array
|
|
528
|
+
|
|
529
|
+
EXAMPLES:
|
|
530
|
+
|
|
531
|
+
This example illustrates the creation of a truth table::
|
|
532
|
+
|
|
533
|
+
sage: import sage.logic.propcalc as propcalc
|
|
534
|
+
sage: s = propcalc.formula("a&b|~(c|a)")
|
|
535
|
+
sage: s.truthtable()
|
|
536
|
+
a b c value
|
|
537
|
+
False False False True
|
|
538
|
+
False False True False
|
|
539
|
+
False True False True
|
|
540
|
+
False True True False
|
|
541
|
+
True False False False
|
|
542
|
+
True False True False
|
|
543
|
+
True True False True
|
|
544
|
+
True True True True
|
|
545
|
+
|
|
546
|
+
We can now create a truthtable of rows 1 to 4, inclusive::
|
|
547
|
+
|
|
548
|
+
sage: s.truthtable(1, 5)
|
|
549
|
+
a b c value
|
|
550
|
+
False False True False
|
|
551
|
+
False True False True
|
|
552
|
+
False True True False
|
|
553
|
+
True False False False
|
|
554
|
+
|
|
555
|
+
.. NOTE::
|
|
556
|
+
|
|
557
|
+
Each row of the table corresponds to a binary number, with
|
|
558
|
+
each variable associated to a column of the number, and taking on
|
|
559
|
+
a true value if that column has a value of 1. Please see the
|
|
560
|
+
logictable module for details. The function returns a table that
|
|
561
|
+
start inclusive and end exclusive so ``truthtable(0, 2)`` will
|
|
562
|
+
include row 0, but not row 2.
|
|
563
|
+
|
|
564
|
+
When sent with no start or end parameters, this is an
|
|
565
|
+
exponential time function requiring `O(2^n)` time, where
|
|
566
|
+
`n` is the number of variables in the expression.
|
|
567
|
+
"""
|
|
568
|
+
maximum = 2 ** len(self.__vars_order)
|
|
569
|
+
if end < 0:
|
|
570
|
+
end = maximum
|
|
571
|
+
end = min(end, maximum)
|
|
572
|
+
start = max(start, 0)
|
|
573
|
+
start = min(start, maximum)
|
|
574
|
+
keys, table = [], []
|
|
575
|
+
vars = {}
|
|
576
|
+
for var in self.__vars_order:
|
|
577
|
+
vars[var] = False
|
|
578
|
+
keys.insert(0, var)
|
|
579
|
+
keys = list(keys)
|
|
580
|
+
for i in range(start, end):
|
|
581
|
+
j = 0
|
|
582
|
+
row = []
|
|
583
|
+
for key in keys:
|
|
584
|
+
bit = self.get_bit(i, j)
|
|
585
|
+
vars[key] = bit
|
|
586
|
+
j += 1
|
|
587
|
+
row.insert(0, bit)
|
|
588
|
+
row.append(booleval.eval_formula(self.__tree, vars))
|
|
589
|
+
table.append(row)
|
|
590
|
+
keys.reverse()
|
|
591
|
+
table = logictable.Truthtable(table, keys)
|
|
592
|
+
return table
|
|
593
|
+
|
|
594
|
+
def evaluate(self, var_values):
|
|
595
|
+
r"""
|
|
596
|
+
Evaluate a formula for the given input values.
|
|
597
|
+
|
|
598
|
+
INPUT:
|
|
599
|
+
|
|
600
|
+
- ``var_values`` -- dictionary; this contains the
|
|
601
|
+
pairs of variables and their boolean values
|
|
602
|
+
|
|
603
|
+
OUTPUT: the result of the evaluation as a boolean
|
|
604
|
+
|
|
605
|
+
EXAMPLES:
|
|
606
|
+
|
|
607
|
+
This example illustrates the evaluation of a boolean formula::
|
|
608
|
+
|
|
609
|
+
sage: import sage.logic.propcalc as propcalc
|
|
610
|
+
sage: f = propcalc.formula("a&b|c")
|
|
611
|
+
sage: f.evaluate({'a':False, 'b':False, 'c':True})
|
|
612
|
+
True
|
|
613
|
+
sage: f.evaluate({'a':True, 'b':False, 'c':False})
|
|
614
|
+
False
|
|
615
|
+
"""
|
|
616
|
+
return booleval.eval_formula(self.__tree, var_values)
|
|
617
|
+
|
|
618
|
+
def is_satisfiable(self):
|
|
619
|
+
r"""
|
|
620
|
+
Determine if the formula is ``True`` for some assignment of values.
|
|
621
|
+
|
|
622
|
+
OUTPUT: a boolean value to be determined as follows:
|
|
623
|
+
|
|
624
|
+
- ``True`` if there is an assignment of values that makes the
|
|
625
|
+
formula ``True``.
|
|
626
|
+
|
|
627
|
+
- ``False`` if the formula cannot be made ``True`` by any assignment
|
|
628
|
+
of values.
|
|
629
|
+
|
|
630
|
+
EXAMPLES:
|
|
631
|
+
|
|
632
|
+
This example illustrates how to check a formula for satisfiability::
|
|
633
|
+
|
|
634
|
+
sage: import sage.logic.propcalc as propcalc
|
|
635
|
+
sage: f = propcalc.formula("a|b")
|
|
636
|
+
sage: f.is_satisfiable()
|
|
637
|
+
True
|
|
638
|
+
|
|
639
|
+
sage: g = f & (~f)
|
|
640
|
+
sage: g.is_satisfiable()
|
|
641
|
+
False
|
|
642
|
+
"""
|
|
643
|
+
table = self.truthtable().get_table_list()
|
|
644
|
+
return any(row[-1] is True for row in table[1:])
|
|
645
|
+
|
|
646
|
+
def is_tautology(self):
|
|
647
|
+
r"""
|
|
648
|
+
Determine if the formula is always ``True``.
|
|
649
|
+
|
|
650
|
+
OUTPUT: a boolean value to be determined as follows:
|
|
651
|
+
|
|
652
|
+
- ``True`` if the formula is a tautology.
|
|
653
|
+
|
|
654
|
+
- ``False`` if the formula is not a tautology.
|
|
655
|
+
|
|
656
|
+
EXAMPLES:
|
|
657
|
+
|
|
658
|
+
This example illustrates how to check if a formula is a tautology::
|
|
659
|
+
|
|
660
|
+
sage: import sage.logic.propcalc as propcalc
|
|
661
|
+
sage: f = propcalc.formula("a|~a")
|
|
662
|
+
sage: f.is_tautology()
|
|
663
|
+
True
|
|
664
|
+
|
|
665
|
+
sage: f = propcalc.formula("a&~a")
|
|
666
|
+
sage: f.is_tautology()
|
|
667
|
+
False
|
|
668
|
+
|
|
669
|
+
sage: f = propcalc.formula("a&b")
|
|
670
|
+
sage: f.is_tautology()
|
|
671
|
+
False
|
|
672
|
+
"""
|
|
673
|
+
return not (~self).is_satisfiable()
|
|
674
|
+
|
|
675
|
+
def is_contradiction(self):
|
|
676
|
+
r"""
|
|
677
|
+
Determine if the formula is always ``False``.
|
|
678
|
+
|
|
679
|
+
OUTPUT: a boolean value to be determined as follows:
|
|
680
|
+
|
|
681
|
+
- ``True`` if the formula is a contradiction.
|
|
682
|
+
|
|
683
|
+
- ``False`` if the formula is not a contradiction.
|
|
684
|
+
|
|
685
|
+
EXAMPLES:
|
|
686
|
+
|
|
687
|
+
This example illustrates how to check if a formula is a contradiction.
|
|
688
|
+
|
|
689
|
+
::
|
|
690
|
+
|
|
691
|
+
sage: import sage.logic.propcalc as propcalc
|
|
692
|
+
sage: f = propcalc.formula("a&~a")
|
|
693
|
+
sage: f.is_contradiction()
|
|
694
|
+
True
|
|
695
|
+
|
|
696
|
+
sage: f = propcalc.formula("a|~a")
|
|
697
|
+
sage: f.is_contradiction()
|
|
698
|
+
False
|
|
699
|
+
|
|
700
|
+
sage: f = propcalc.formula("a|b")
|
|
701
|
+
sage: f.is_contradiction()
|
|
702
|
+
False
|
|
703
|
+
"""
|
|
704
|
+
return not self.is_satisfiable()
|
|
705
|
+
|
|
706
|
+
def is_consequence(self, *hypotheses):
|
|
707
|
+
r"""
|
|
708
|
+
Determine if ``self`` (the desired conclusion) is a logical consequence of the
|
|
709
|
+
hypotheses. The function call ``is_consequence(conclusion, *hypotheses)`` is a
|
|
710
|
+
synonym for ``conclusion.is_consequence(*hypotheses)``.
|
|
711
|
+
|
|
712
|
+
INPUT:
|
|
713
|
+
|
|
714
|
+
- ``*hypotheses`` -- instances of :class:`BooleanFormula`
|
|
715
|
+
|
|
716
|
+
OUTPUT: a boolean value to be determined as follows:
|
|
717
|
+
|
|
718
|
+
- ``True`` -- if ``self`` (the desired conclusion) is a logical consequence
|
|
719
|
+
of the set of hypotheses
|
|
720
|
+
|
|
721
|
+
- ``False`` -- if ``self`` (the desired conclusion) is not a logical consequence
|
|
722
|
+
of the set of hypotheses
|
|
723
|
+
|
|
724
|
+
EXAMPLES::
|
|
725
|
+
|
|
726
|
+
sage: from sage.logic.propcalc import formula
|
|
727
|
+
sage: formula("a | b").is_consequence(formula("b"))
|
|
728
|
+
True
|
|
729
|
+
sage: formula("a & b").is_consequence(formula("b"))
|
|
730
|
+
False
|
|
731
|
+
sage: formula("b").is_consequence(formula("a"), formula("a -> b"))
|
|
732
|
+
True
|
|
733
|
+
sage: formula("b -> a").is_consequence(formula("a -> b"))
|
|
734
|
+
False
|
|
735
|
+
sage: formula("~b -> ~a").is_consequence(formula("a -> b"))
|
|
736
|
+
True
|
|
737
|
+
|
|
738
|
+
::
|
|
739
|
+
|
|
740
|
+
sage: f, g, h = propcalc.get_formulas("a & ~b", "c -> b", "c | e")
|
|
741
|
+
sage: propcalc.formula("a & e").is_consequence(f, g, h)
|
|
742
|
+
True
|
|
743
|
+
sage: i = propcalc.formula("a & ~e")
|
|
744
|
+
sage: i.is_consequence(f, g, h)
|
|
745
|
+
False
|
|
746
|
+
sage: from sage.logic.boolformula import is_consequence
|
|
747
|
+
sage: is_consequence(i, f, g, h)
|
|
748
|
+
False
|
|
749
|
+
sage: is_consequence(propcalc.formula("((p <-> q) & r) -> ~c"), f, g, h)
|
|
750
|
+
True
|
|
751
|
+
|
|
752
|
+
Only a tautology is a logical consequence of an empty set of formulas::
|
|
753
|
+
|
|
754
|
+
sage: propcalc.formula("a | ~a").is_consequence()
|
|
755
|
+
True
|
|
756
|
+
sage: propcalc.formula("a | b").is_consequence()
|
|
757
|
+
False
|
|
758
|
+
|
|
759
|
+
TESTS:
|
|
760
|
+
|
|
761
|
+
Arguments must be instances of :class:`BooleanFormula` (not strings, for example)::
|
|
762
|
+
|
|
763
|
+
sage: propcalc.formula("a | b").is_consequence("a | b")
|
|
764
|
+
Traceback (most recent call last):
|
|
765
|
+
...
|
|
766
|
+
TypeError: is_consequence only takes instances of BooleanFormula() class as input
|
|
767
|
+
|
|
768
|
+
AUTHORS:
|
|
769
|
+
|
|
770
|
+
- Paul Scurek (2013-08-12)
|
|
771
|
+
"""
|
|
772
|
+
# make sure every argument is an instance of :class:`BooleanFormula`
|
|
773
|
+
for formula in (self,) + hypotheses:
|
|
774
|
+
if not isinstance(formula, BooleanFormula):
|
|
775
|
+
raise TypeError("is_consequence only takes instances of BooleanFormula() class as input")
|
|
776
|
+
|
|
777
|
+
if not hypotheses:
|
|
778
|
+
# if there are no hypotheses, then we just want to know whether self is a tautology
|
|
779
|
+
return self.is_tautology()
|
|
780
|
+
else:
|
|
781
|
+
# conjoin all of the hypotheses into a single Boolean formula
|
|
782
|
+
conjunction = hypotheses[0]
|
|
783
|
+
for hypothesis in hypotheses[1:]:
|
|
784
|
+
conjunction = conjunction & hypothesis
|
|
785
|
+
|
|
786
|
+
return conjunction.implies(self)
|
|
787
|
+
|
|
788
|
+
def implies(self, other):
|
|
789
|
+
r"""
|
|
790
|
+
Determine if calling formula implies other formula.
|
|
791
|
+
|
|
792
|
+
INPUT:
|
|
793
|
+
|
|
794
|
+
- ``self`` -- calling object
|
|
795
|
+
|
|
796
|
+
- ``other`` -- instance of :class:`BooleanFormula`
|
|
797
|
+
|
|
798
|
+
OUTPUT: a boolean value to be determined as follows:
|
|
799
|
+
|
|
800
|
+
- ``True`` -- if ``self`` implies ``other``
|
|
801
|
+
|
|
802
|
+
- ``False`` -- if ``self does not imply ``other``
|
|
803
|
+
|
|
804
|
+
EXAMPLES:
|
|
805
|
+
|
|
806
|
+
This example illustrates determining if one formula implies another::
|
|
807
|
+
|
|
808
|
+
sage: import sage.logic.propcalc as propcalc
|
|
809
|
+
sage: f = propcalc.formula("a<->b")
|
|
810
|
+
sage: g = propcalc.formula("b->a")
|
|
811
|
+
sage: f.implies(g)
|
|
812
|
+
True
|
|
813
|
+
|
|
814
|
+
::
|
|
815
|
+
|
|
816
|
+
sage: h = propcalc.formula("a->(a|~b)")
|
|
817
|
+
sage: i = propcalc.formula("a")
|
|
818
|
+
sage: h.implies(i)
|
|
819
|
+
False
|
|
820
|
+
|
|
821
|
+
AUTHORS:
|
|
822
|
+
|
|
823
|
+
- Paul Scurek (2013-08-08)
|
|
824
|
+
"""
|
|
825
|
+
# input validation
|
|
826
|
+
if not isinstance(other, BooleanFormula):
|
|
827
|
+
raise TypeError("implies() takes an instance of the BooleanFormula() class as input")
|
|
828
|
+
|
|
829
|
+
conditional = self.ifthen(other)
|
|
830
|
+
return (conditional).is_tautology()
|
|
831
|
+
|
|
832
|
+
def equivalent(self, other):
|
|
833
|
+
r"""
|
|
834
|
+
Determine if two formulas are semantically equivalent.
|
|
835
|
+
|
|
836
|
+
INPUT:
|
|
837
|
+
|
|
838
|
+
- ``self`` -- calling object
|
|
839
|
+
|
|
840
|
+
- ``other`` -- instance of BooleanFormula class
|
|
841
|
+
|
|
842
|
+
OUTPUT: a boolean value to be determined as follows:
|
|
843
|
+
|
|
844
|
+
``True`` -- if the two formulas are logically equivalent
|
|
845
|
+
|
|
846
|
+
``False`` -- if the two formulas are not logically equivalent
|
|
847
|
+
|
|
848
|
+
EXAMPLES:
|
|
849
|
+
|
|
850
|
+
This example shows how to check for logical equivalence::
|
|
851
|
+
|
|
852
|
+
sage: import sage.logic.propcalc as propcalc
|
|
853
|
+
sage: f = propcalc.formula("(a|b)&c")
|
|
854
|
+
sage: g = propcalc.formula("c&(a|b)")
|
|
855
|
+
sage: f.equivalent(g)
|
|
856
|
+
True
|
|
857
|
+
|
|
858
|
+
sage: g = propcalc.formula("a|b&c")
|
|
859
|
+
sage: f.equivalent(g)
|
|
860
|
+
False
|
|
861
|
+
"""
|
|
862
|
+
return self.iff(other).is_tautology()
|
|
863
|
+
|
|
864
|
+
def convert_cnf_table(self):
|
|
865
|
+
r"""
|
|
866
|
+
Convert boolean formula to conjunctive normal form.
|
|
867
|
+
|
|
868
|
+
OUTPUT: an instance of :class:`BooleanFormula` in conjunctive normal form
|
|
869
|
+
|
|
870
|
+
EXAMPLES:
|
|
871
|
+
|
|
872
|
+
This example illustrates how to convert a formula to cnf::
|
|
873
|
+
|
|
874
|
+
sage: import sage.logic.propcalc as propcalc
|
|
875
|
+
sage: s = propcalc.formula("a ^ b <-> c")
|
|
876
|
+
sage: s.convert_cnf()
|
|
877
|
+
sage: s
|
|
878
|
+
(a|b|~c)&(a|~b|c)&(~a|b|c)&(~a|~b|~c)
|
|
879
|
+
|
|
880
|
+
We now show that :meth:`convert_cnf` and :meth:`convert_cnf_table`
|
|
881
|
+
are aliases::
|
|
882
|
+
|
|
883
|
+
sage: t = propcalc.formula("a ^ b <-> c")
|
|
884
|
+
sage: t.convert_cnf_table(); t
|
|
885
|
+
(a|b|~c)&(a|~b|c)&(~a|b|c)&(~a|~b|~c)
|
|
886
|
+
sage: t == s
|
|
887
|
+
True
|
|
888
|
+
|
|
889
|
+
.. NOTE::
|
|
890
|
+
|
|
891
|
+
This method creates the cnf parse tree by examining the logic
|
|
892
|
+
table of the formula. Creating the table requires `O(2^n)` time
|
|
893
|
+
where `n` is the number of variables in the formula.
|
|
894
|
+
"""
|
|
895
|
+
str = ''
|
|
896
|
+
t = self.truthtable()
|
|
897
|
+
table = t.get_table_list()
|
|
898
|
+
vars = table[0]
|
|
899
|
+
for row in table[1:]:
|
|
900
|
+
if row[-1] is False:
|
|
901
|
+
str += '('
|
|
902
|
+
for i in range(len(row) - 1):
|
|
903
|
+
if row[i] is True:
|
|
904
|
+
str += '~'
|
|
905
|
+
str += vars[i]
|
|
906
|
+
str += '|'
|
|
907
|
+
str = str[:-1] + ')&'
|
|
908
|
+
self.__expression = str[:-1]
|
|
909
|
+
# in case of tautology
|
|
910
|
+
if len(self.__expression) == 0:
|
|
911
|
+
self.__expression = '(' + self.__vars_order[0] + '|~' + self.__vars_order[0] + ')'
|
|
912
|
+
self.__tree, self.__vars_order = logicparser.parse(self.__expression)
|
|
913
|
+
|
|
914
|
+
convert_cnf = convert_cnf_table
|
|
915
|
+
|
|
916
|
+
def convert_cnf_recur(self):
|
|
917
|
+
r"""
|
|
918
|
+
Convert boolean formula to conjunctive normal form.
|
|
919
|
+
|
|
920
|
+
OUTPUT: an instance of :class:`BooleanFormula` in conjunctive normal form
|
|
921
|
+
|
|
922
|
+
EXAMPLES:
|
|
923
|
+
|
|
924
|
+
This example hows how to convert a formula to conjunctive normal form::
|
|
925
|
+
|
|
926
|
+
sage: import sage.logic.propcalc as propcalc
|
|
927
|
+
sage: s = propcalc.formula("a^b<->c")
|
|
928
|
+
sage: s.convert_cnf_recur()
|
|
929
|
+
sage: s
|
|
930
|
+
(~a|a|c)&(~b|a|c)&(~a|b|c)&(~b|b|c)&(~c|a|b)&(~c|~a|~b)
|
|
931
|
+
|
|
932
|
+
.. NOTE::
|
|
933
|
+
|
|
934
|
+
This function works by applying a set of rules that are
|
|
935
|
+
guaranteed to convert the formula. Worst case the converted
|
|
936
|
+
expression has an `O(2^n)` increase in size (and time as well), but
|
|
937
|
+
if the formula is already in CNF (or close to) it is only `O(n)`.
|
|
938
|
+
|
|
939
|
+
This function can require an exponential blow up in space from the
|
|
940
|
+
original expression. This in turn can require large amounts of
|
|
941
|
+
time. Unless a formula is already in (or close to) being in cnf
|
|
942
|
+
:meth:`convert_cnf()` is typically preferred, but results can vary.
|
|
943
|
+
"""
|
|
944
|
+
self.__tree = logicparser.apply_func(self.__tree, self.reduce_op)
|
|
945
|
+
self.__tree = logicparser.apply_func(self.__tree, self.dist_not)
|
|
946
|
+
self.__tree = logicparser.apply_func(self.__tree, self.dist_ors)
|
|
947
|
+
self.convert_expression()
|
|
948
|
+
|
|
949
|
+
def satformat(self):
|
|
950
|
+
r"""
|
|
951
|
+
Return the satformat representation of a boolean formula.
|
|
952
|
+
|
|
953
|
+
OUTPUT: the satformat of the formula as a string
|
|
954
|
+
|
|
955
|
+
EXAMPLES:
|
|
956
|
+
|
|
957
|
+
This example illustrates how to find the satformat of a formula::
|
|
958
|
+
|
|
959
|
+
sage: import sage.logic.propcalc as propcalc
|
|
960
|
+
sage: f = propcalc.formula("a&((b|c)^a->c)<->b")
|
|
961
|
+
sage: f.convert_cnf()
|
|
962
|
+
sage: f
|
|
963
|
+
(a|~b|c)&(a|~b|~c)&(~a|b|~c)
|
|
964
|
+
sage: f.satformat()
|
|
965
|
+
'p cnf 3 0\n1 -2 3 0 1 -2 -3 \n0 -1 2 -3'
|
|
966
|
+
|
|
967
|
+
.. NOTE::
|
|
968
|
+
|
|
969
|
+
See www.cs.ubc.ca/~hoos/SATLIB/Benchmarks/SAT/satformat.ps for a
|
|
970
|
+
description of satformat.
|
|
971
|
+
|
|
972
|
+
If the instance of boolean formula has not been converted to
|
|
973
|
+
CNF form by a call to :meth:`convert_cnf()` or
|
|
974
|
+
:meth:`convert_cnf_recur()`, then :meth:`satformat()` will call
|
|
975
|
+
:meth:`convert_cnf()`. Please see the notes for
|
|
976
|
+
:meth:`convert_cnf()` and :meth:`convert_cnf_recur()` for
|
|
977
|
+
performance issues.
|
|
978
|
+
"""
|
|
979
|
+
self.convert_cnf_table()
|
|
980
|
+
s = ''
|
|
981
|
+
vars_num = {}
|
|
982
|
+
i = 0
|
|
983
|
+
clauses = 0
|
|
984
|
+
for e in self.__vars_order:
|
|
985
|
+
vars_num[e] = str(i + 1)
|
|
986
|
+
i += 1
|
|
987
|
+
i = 0
|
|
988
|
+
w = 1
|
|
989
|
+
while i < len(self.__expression):
|
|
990
|
+
c = self.__expression[i]
|
|
991
|
+
if c == ')':
|
|
992
|
+
clauses += 1
|
|
993
|
+
if c in '()|':
|
|
994
|
+
i += 1
|
|
995
|
+
continue
|
|
996
|
+
if c == '~':
|
|
997
|
+
s += '-'
|
|
998
|
+
elif c == '&':
|
|
999
|
+
s += '0 '
|
|
1000
|
+
else:
|
|
1001
|
+
varname = ''
|
|
1002
|
+
if self.__expression[i] not in '|) ':
|
|
1003
|
+
varname += self.__expression[i]
|
|
1004
|
+
i += 1
|
|
1005
|
+
s += vars_num[varname] + ' '
|
|
1006
|
+
if len(s) >= (w * 15) and s[-1] != '-':
|
|
1007
|
+
s += '\n'
|
|
1008
|
+
w += 1
|
|
1009
|
+
i += 1
|
|
1010
|
+
s = 'p cnf ' + str(len(self.__vars_order)) + ' ' + str(clauses) + '\n' + s
|
|
1011
|
+
return s[:-1]
|
|
1012
|
+
|
|
1013
|
+
# def simplify(self):
|
|
1014
|
+
# r"""
|
|
1015
|
+
# This function uses the propcalc package to simplify an expression to
|
|
1016
|
+
# its minimal form.
|
|
1017
|
+
#
|
|
1018
|
+
# OUTPUT: a simplified expression
|
|
1019
|
+
#
|
|
1020
|
+
# EXAMPLES::
|
|
1021
|
+
|
|
1022
|
+
# sage: import sage.logic.propcalc as propcalc
|
|
1023
|
+
# sage: f = propcalc.formula("a&((b|c)^a->c)<->b")
|
|
1024
|
+
# sage: f.truthtable()
|
|
1025
|
+
# a b c value
|
|
1026
|
+
# False False False True
|
|
1027
|
+
# False False True True
|
|
1028
|
+
# False True False False
|
|
1029
|
+
# False True True False
|
|
1030
|
+
# True False False True
|
|
1031
|
+
# True False True False
|
|
1032
|
+
# True True False True
|
|
1033
|
+
# True True True True
|
|
1034
|
+
# sage: f.simplify()
|
|
1035
|
+
# (~a&~b)|(a&~b&~c)|(a&b)
|
|
1036
|
+
# sage: f.truthtable()
|
|
1037
|
+
# a b c value
|
|
1038
|
+
# False False False True
|
|
1039
|
+
# False False True True
|
|
1040
|
+
# False True False False
|
|
1041
|
+
# False True True False
|
|
1042
|
+
# True False False True
|
|
1043
|
+
# True False True False
|
|
1044
|
+
# True True False True
|
|
1045
|
+
# True True True True
|
|
1046
|
+
#
|
|
1047
|
+
# .. NOTE::
|
|
1048
|
+
#
|
|
1049
|
+
# If the instance of boolean formula has not been converted to
|
|
1050
|
+
# cnf form by a call to convert_cnf() or convert_cnf_recur()
|
|
1051
|
+
# satformat() will call convert_cnf(). Please see the notes for
|
|
1052
|
+
# convert_cnf() and convert_cnf_recur() for performance issues.
|
|
1053
|
+
# """
|
|
1054
|
+
# exp = ''
|
|
1055
|
+
# self.__tree = logicparser.apply_func(self.__tree, self.reduce_op)
|
|
1056
|
+
# plf = logicparser.apply_func(self.__tree, self.convert_opt)
|
|
1057
|
+
# wff = boolopt.PLFtoWFF()(plf) # convert to positive-normal form
|
|
1058
|
+
# wtd = boolopt.WFFtoDNF()
|
|
1059
|
+
# dnf = wtd(wff)
|
|
1060
|
+
# dnf = wtd.clean(dnf)
|
|
1061
|
+
# if dnf == [] or dnf == [[]]:
|
|
1062
|
+
# exp = self.__vars_order[0] + '&~' + self.__vars_order[0] + ' '
|
|
1063
|
+
# opt = boolopt.optimize(dnf)
|
|
1064
|
+
# if exp == '' and (opt == [] or opt == [[]]):
|
|
1065
|
+
# exp = self.__vars_order[0] + '|~' + self.__vars_order[0] + ' '
|
|
1066
|
+
# if exp == '':
|
|
1067
|
+
# for con in opt:
|
|
1068
|
+
# s = '('
|
|
1069
|
+
# for prop in con:
|
|
1070
|
+
# if prop[0] == 'notprop':
|
|
1071
|
+
# s += '~'
|
|
1072
|
+
# s += prop[1] + '&'
|
|
1073
|
+
# exp += s[:-1] + ')|'
|
|
1074
|
+
# self.__expression = exp[:-1]
|
|
1075
|
+
# self.__tree, self.__vars_order = logicparser.parse(self.__expression)
|
|
1076
|
+
# return BooleanFormula(self.__expression, self.__tree, self.__vars_order)
|
|
1077
|
+
|
|
1078
|
+
def convert_opt(self, tree):
|
|
1079
|
+
r"""
|
|
1080
|
+
Convert a parse tree to the tuple form used by :meth:`bool_opt()`.
|
|
1081
|
+
|
|
1082
|
+
INPUT:
|
|
1083
|
+
|
|
1084
|
+
- ``tree`` -- list; this is a branch of a
|
|
1085
|
+
parse tree and can only contain the '&', '|'
|
|
1086
|
+
and '~' operators along with variables
|
|
1087
|
+
|
|
1088
|
+
OUTPUT: a 3-tuple
|
|
1089
|
+
|
|
1090
|
+
EXAMPLES:
|
|
1091
|
+
|
|
1092
|
+
This example illustrates the conversion of a formula into its
|
|
1093
|
+
corresponding tuple::
|
|
1094
|
+
|
|
1095
|
+
sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser
|
|
1096
|
+
sage: s = propcalc.formula("a&(b|~c)")
|
|
1097
|
+
sage: tree = ['&', 'a', ['|', 'b', ['~', 'c', None]]]
|
|
1098
|
+
sage: logicparser.apply_func(tree, s.convert_opt)
|
|
1099
|
+
('and', ('prop', 'a'), ('or', ('prop', 'b'), ('not', ('prop', 'c'))))
|
|
1100
|
+
|
|
1101
|
+
.. NOTE::
|
|
1102
|
+
|
|
1103
|
+
This function only works on one branch of the parse tree. To
|
|
1104
|
+
apply the function to every branch of a parse tree, pass the
|
|
1105
|
+
function as an argument in
|
|
1106
|
+
:func:`~sage.logic.logicparser.apply_func()` in
|
|
1107
|
+
:mod:`~sage.logic.logicparser`.
|
|
1108
|
+
"""
|
|
1109
|
+
if not isinstance(tree[1], tuple) and tree[1] is not None:
|
|
1110
|
+
lval = ('prop', tree[1])
|
|
1111
|
+
else:
|
|
1112
|
+
lval = tree[1]
|
|
1113
|
+
if not isinstance(tree[2], tuple) and tree[2] is not None:
|
|
1114
|
+
rval = ('prop', tree[2])
|
|
1115
|
+
else:
|
|
1116
|
+
rval = tree[2]
|
|
1117
|
+
if tree[0] == '~':
|
|
1118
|
+
return ('not', lval)
|
|
1119
|
+
if tree[0] == '&':
|
|
1120
|
+
op = 'and'
|
|
1121
|
+
if tree[0] == '|':
|
|
1122
|
+
op = 'or'
|
|
1123
|
+
return (op, lval, rval)
|
|
1124
|
+
|
|
1125
|
+
def add_statement(self, other, op):
|
|
1126
|
+
r"""
|
|
1127
|
+
Combine two formulas with the given operator.
|
|
1128
|
+
|
|
1129
|
+
INPUT:
|
|
1130
|
+
|
|
1131
|
+
- ``other`` -- instance of :class:`BooleanFormula`; this
|
|
1132
|
+
is the formula on the right of the operator
|
|
1133
|
+
|
|
1134
|
+
- ``op`` -- string; this is the operator used to
|
|
1135
|
+
combine the two formulas
|
|
1136
|
+
|
|
1137
|
+
OUTPUT: the result as an instance of :class:`BooleanFormula`
|
|
1138
|
+
|
|
1139
|
+
EXAMPLES:
|
|
1140
|
+
|
|
1141
|
+
This example shows how to create a new formula from two others::
|
|
1142
|
+
|
|
1143
|
+
sage: import sage.logic.propcalc as propcalc
|
|
1144
|
+
sage: s = propcalc.formula("a&b")
|
|
1145
|
+
sage: f = propcalc.formula("c^d")
|
|
1146
|
+
sage: s.add_statement(f, '|')
|
|
1147
|
+
(a&b)|(c^d)
|
|
1148
|
+
|
|
1149
|
+
sage: s.add_statement(f, '->')
|
|
1150
|
+
(a&b)->(c^d)
|
|
1151
|
+
"""
|
|
1152
|
+
exp = '(' + self.__expression + ')' + op + '(' + other.__expression + ')'
|
|
1153
|
+
parse_tree, vars_order = logicparser.parse(exp)
|
|
1154
|
+
return BooleanFormula(exp, parse_tree, vars_order)
|
|
1155
|
+
|
|
1156
|
+
def get_bit(self, x, c):
|
|
1157
|
+
r"""
|
|
1158
|
+
Determine if bit ``c`` of the number ``x`` is 1.
|
|
1159
|
+
|
|
1160
|
+
INPUT:
|
|
1161
|
+
|
|
1162
|
+
- ``x`` -- integer; this is the number from
|
|
1163
|
+
which to take the bit
|
|
1164
|
+
|
|
1165
|
+
- ``c`` -- integer; this is the but number to
|
|
1166
|
+
be taken, where 0 is the low order bit
|
|
1167
|
+
|
|
1168
|
+
OUTPUT: a boolean to be determined as follows:
|
|
1169
|
+
|
|
1170
|
+
- ``True`` if bit ``c`` of ``x`` is 1.
|
|
1171
|
+
|
|
1172
|
+
- ``False`` if bit c of x is not 1.
|
|
1173
|
+
|
|
1174
|
+
EXAMPLES:
|
|
1175
|
+
|
|
1176
|
+
This example illustrates the use of :meth:`get_bit`::
|
|
1177
|
+
|
|
1178
|
+
sage: import sage.logic.propcalc as propcalc
|
|
1179
|
+
sage: s = propcalc.formula("a&b")
|
|
1180
|
+
sage: s.get_bit(2, 1)
|
|
1181
|
+
True
|
|
1182
|
+
sage: s.get_bit(8, 0)
|
|
1183
|
+
False
|
|
1184
|
+
|
|
1185
|
+
It is not an error to have a bit out of range::
|
|
1186
|
+
|
|
1187
|
+
sage: s.get_bit(64, 7)
|
|
1188
|
+
False
|
|
1189
|
+
|
|
1190
|
+
Nor is it an error to use a negative number::
|
|
1191
|
+
|
|
1192
|
+
sage: s.get_bit(-1, 3)
|
|
1193
|
+
False
|
|
1194
|
+
sage: s.get_bit(64, -1)
|
|
1195
|
+
True
|
|
1196
|
+
sage: s.get_bit(64, -2)
|
|
1197
|
+
False
|
|
1198
|
+
|
|
1199
|
+
.. NOTE::
|
|
1200
|
+
|
|
1201
|
+
The 0 bit is the low order bit. Errors should be handled
|
|
1202
|
+
gracefully by a return of ``False``, and negative numbers ``x``
|
|
1203
|
+
always return ``False`` while a negative ``c`` will index from the
|
|
1204
|
+
high order bit.
|
|
1205
|
+
"""
|
|
1206
|
+
bits = []
|
|
1207
|
+
while x > 0:
|
|
1208
|
+
b = bool(x % 2)
|
|
1209
|
+
x = x // 2
|
|
1210
|
+
bits.append(b)
|
|
1211
|
+
if c > len(bits) - 1:
|
|
1212
|
+
return False
|
|
1213
|
+
else:
|
|
1214
|
+
return bits[c]
|
|
1215
|
+
|
|
1216
|
+
def reduce_op(self, tree):
|
|
1217
|
+
r"""
|
|
1218
|
+
Convert if-and-only-if, if-then, and xor operations to operations
|
|
1219
|
+
only involving and/or operations.
|
|
1220
|
+
|
|
1221
|
+
INPUT:
|
|
1222
|
+
|
|
1223
|
+
- ``tree`` -- list; this represents a branch
|
|
1224
|
+
of a parse tree
|
|
1225
|
+
|
|
1226
|
+
OUTPUT:
|
|
1227
|
+
|
|
1228
|
+
A new list with no '^', '->', or '<->' as first element of list.
|
|
1229
|
+
|
|
1230
|
+
EXAMPLES:
|
|
1231
|
+
|
|
1232
|
+
This example illustrates the use of :meth:`reduce_op` with
|
|
1233
|
+
:func:`apply_func`::
|
|
1234
|
+
|
|
1235
|
+
sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser
|
|
1236
|
+
sage: s = propcalc.formula("a->b^c")
|
|
1237
|
+
sage: tree = ['->', 'a', ['^', 'b', 'c']]
|
|
1238
|
+
sage: logicparser.apply_func(tree, s.reduce_op)
|
|
1239
|
+
['|', ['~', 'a', None], ['&', ['|', 'b', 'c'], ['~', ['&', 'b', 'c'], None]]]
|
|
1240
|
+
|
|
1241
|
+
.. NOTE::
|
|
1242
|
+
|
|
1243
|
+
This function only operates on a single branch of a parse tree.
|
|
1244
|
+
To apply the function to an entire parse tree, pass the function
|
|
1245
|
+
as an argument to :func:`~sage.logic.logicparser.apply_func()`
|
|
1246
|
+
in :mod:`~sage.logic.logicparser`.
|
|
1247
|
+
"""
|
|
1248
|
+
if tree[0] == '<->':
|
|
1249
|
+
# parse tree for (~tree[1]|tree[2])&(~tree[2]|tree[1])
|
|
1250
|
+
new_tree = ['&', ['|', ['~', tree[1], None], tree[2]],
|
|
1251
|
+
['|', ['~', tree[2], None], tree[1]]]
|
|
1252
|
+
elif tree[0] == '^':
|
|
1253
|
+
# parse tree for (tree[1]|tree[2])&~(tree[1]&tree[2])
|
|
1254
|
+
new_tree = ['&', ['|', tree[1], tree[2]],
|
|
1255
|
+
['~', ['&', tree[1], tree[2]], None]]
|
|
1256
|
+
elif tree[0] == '->':
|
|
1257
|
+
# parse tree for ~tree[1]|tree[2]
|
|
1258
|
+
new_tree = ['|', ['~', tree[1], None], tree[2]]
|
|
1259
|
+
else:
|
|
1260
|
+
new_tree = tree
|
|
1261
|
+
return new_tree
|
|
1262
|
+
|
|
1263
|
+
def dist_not(self, tree):
|
|
1264
|
+
r"""
|
|
1265
|
+
Distribute '~' operators over '&' and '|' operators.
|
|
1266
|
+
|
|
1267
|
+
INPUT:
|
|
1268
|
+
|
|
1269
|
+
- ``tree`` a list; this represents a branch
|
|
1270
|
+
of a parse tree
|
|
1271
|
+
|
|
1272
|
+
OUTPUT: a new list
|
|
1273
|
+
|
|
1274
|
+
EXAMPLES:
|
|
1275
|
+
|
|
1276
|
+
This example illustrates the distribution of '~' over '&'::
|
|
1277
|
+
|
|
1278
|
+
sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser
|
|
1279
|
+
sage: s = propcalc.formula("~(a&b)")
|
|
1280
|
+
sage: tree = ['~', ['&', 'a', 'b'], None]
|
|
1281
|
+
sage: logicparser.apply_func(tree, s.dist_not) #long time
|
|
1282
|
+
['|', ['~', 'a', None], ['~', 'b', None]]
|
|
1283
|
+
|
|
1284
|
+
.. NOTE::
|
|
1285
|
+
|
|
1286
|
+
This function only operates on a single branch of a parse tree.
|
|
1287
|
+
To apply the function to an entire parse tree, pass the function
|
|
1288
|
+
as an argument to :func:`~sage.logic.logicparser.apply_func()`
|
|
1289
|
+
in :mod:`~sage.logic.logicparser`.
|
|
1290
|
+
"""
|
|
1291
|
+
if tree[0] == '~' and isinstance(tree[1], list):
|
|
1292
|
+
op = tree[1][0]
|
|
1293
|
+
if op != '~':
|
|
1294
|
+
op = '|' if op == '&' else '&'
|
|
1295
|
+
new_tree = [op, ['~', tree[1][1], None], ['~', tree[1][2], None]]
|
|
1296
|
+
return logicparser.apply_func(new_tree, self.dist_not)
|
|
1297
|
+
else:
|
|
1298
|
+
# cancel double negative
|
|
1299
|
+
return tree[1][1]
|
|
1300
|
+
else:
|
|
1301
|
+
return tree
|
|
1302
|
+
|
|
1303
|
+
def dist_ors(self, tree):
|
|
1304
|
+
r"""
|
|
1305
|
+
Distribute '|' over '&'.
|
|
1306
|
+
|
|
1307
|
+
INPUT:
|
|
1308
|
+
|
|
1309
|
+
- ``tree`` -- list; this represents a branch of
|
|
1310
|
+
a parse tree
|
|
1311
|
+
|
|
1312
|
+
OUTPUT: a new list
|
|
1313
|
+
|
|
1314
|
+
EXAMPLES:
|
|
1315
|
+
|
|
1316
|
+
This example illustrates the distribution of '|' over '&'::
|
|
1317
|
+
|
|
1318
|
+
sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser
|
|
1319
|
+
sage: s = propcalc.formula("(a&b)|(a&c)")
|
|
1320
|
+
sage: tree = ['|', ['&', 'a', 'b'], ['&', 'a', 'c']]
|
|
1321
|
+
sage: logicparser.apply_func(tree, s.dist_ors) #long time
|
|
1322
|
+
['&', ['&', ['|', 'a', 'a'], ['|', 'b', 'a']], ['&', ['|', 'a', 'c'], ['|', 'b', 'c']]]
|
|
1323
|
+
|
|
1324
|
+
.. NOTE::
|
|
1325
|
+
|
|
1326
|
+
This function only operates on a single branch of a parse tree.
|
|
1327
|
+
To apply the function to an entire parse tree, pass the function
|
|
1328
|
+
as an argument to :func:`~sage.logic.logicparser.apply_func()`
|
|
1329
|
+
in :mod:`~sage.logic.logicparser`.
|
|
1330
|
+
"""
|
|
1331
|
+
if tree[0] == '|' and isinstance(tree[2], list) and tree[2][0] == '&':
|
|
1332
|
+
new_tree = ['&', ['|', tree[1], tree[2][1]],
|
|
1333
|
+
['|', tree[1], tree[2][2]]]
|
|
1334
|
+
return logicparser.apply_func(new_tree, self.dist_ors)
|
|
1335
|
+
if tree[0] == '|' and isinstance(tree[1], list) and tree[1][0] == '&':
|
|
1336
|
+
new_tree = ['&', ['|', tree[1][1], tree[2]],
|
|
1337
|
+
['|', tree[1][2], tree[2]]]
|
|
1338
|
+
return logicparser.apply_func(new_tree, self.dist_ors)
|
|
1339
|
+
return tree
|
|
1340
|
+
|
|
1341
|
+
def to_infix(self, tree):
|
|
1342
|
+
r"""
|
|
1343
|
+
Convert a parse tree from prefix to infix form.
|
|
1344
|
+
|
|
1345
|
+
INPUT:
|
|
1346
|
+
|
|
1347
|
+
- ``tree`` -- list; this represents a branch
|
|
1348
|
+
of a parse tree
|
|
1349
|
+
|
|
1350
|
+
OUTPUT: a new list
|
|
1351
|
+
|
|
1352
|
+
EXAMPLES:
|
|
1353
|
+
|
|
1354
|
+
This example shows how to convert a parse tree from prefix to
|
|
1355
|
+
infix form::
|
|
1356
|
+
|
|
1357
|
+
sage: import sage.logic.propcalc as propcalc, sage.logic.logicparser as logicparser
|
|
1358
|
+
sage: s = propcalc.formula("(a&b)|(a&c)")
|
|
1359
|
+
sage: tree = ['|', ['&', 'a', 'b'], ['&', 'a', 'c']]
|
|
1360
|
+
sage: logicparser.apply_func(tree, s.to_infix)
|
|
1361
|
+
[['a', '&', 'b'], '|', ['a', '&', 'c']]
|
|
1362
|
+
|
|
1363
|
+
.. NOTE::
|
|
1364
|
+
|
|
1365
|
+
This function only operates on a single branch of a parse tree.
|
|
1366
|
+
To apply the function to an entire parse tree, pass the function
|
|
1367
|
+
as an argument to :func:`~sage.logic.logicparser.apply_func()`
|
|
1368
|
+
in :mod:`~sage.logic.logicparser`.
|
|
1369
|
+
"""
|
|
1370
|
+
if tree[0] != '~':
|
|
1371
|
+
return [tree[1], tree[0], tree[2]]
|
|
1372
|
+
return tree
|
|
1373
|
+
|
|
1374
|
+
def convert_expression(self):
|
|
1375
|
+
r"""
|
|
1376
|
+
Convert the string representation of a formula to conjunctive
|
|
1377
|
+
normal form.
|
|
1378
|
+
|
|
1379
|
+
EXAMPLES::
|
|
1380
|
+
|
|
1381
|
+
sage: import sage.logic.propcalc as propcalc
|
|
1382
|
+
sage: s = propcalc.formula("a^b<->c")
|
|
1383
|
+
sage: s.convert_expression(); s
|
|
1384
|
+
a^b<->c
|
|
1385
|
+
"""
|
|
1386
|
+
ttree = self.__tree[:]
|
|
1387
|
+
ttree = logicparser.apply_func(ttree, self.to_infix)
|
|
1388
|
+
self.__expression = ''
|
|
1389
|
+
str_tree = str(ttree)
|
|
1390
|
+
open_flag = False
|
|
1391
|
+
i = 0
|
|
1392
|
+
for c in str_tree:
|
|
1393
|
+
if i < len(str_tree) - 1:
|
|
1394
|
+
op = self.get_next_op(str_tree[i:])
|
|
1395
|
+
if op == '|' and not open_flag:
|
|
1396
|
+
self.__expression += '('
|
|
1397
|
+
open_flag = True
|
|
1398
|
+
if i < len(str_tree) - 2 and str_tree[i + 1] == '&' and open_flag:
|
|
1399
|
+
open_flag = False
|
|
1400
|
+
self.__expression += ')'
|
|
1401
|
+
if str_tree[i:i + 4] == 'None':
|
|
1402
|
+
i += 4
|
|
1403
|
+
if i < len(str_tree) and str_tree[i] not in ' \',[]':
|
|
1404
|
+
self.__expression += str_tree[i]
|
|
1405
|
+
i += 1
|
|
1406
|
+
if open_flag is True:
|
|
1407
|
+
self.__expression += ')'
|
|
1408
|
+
|
|
1409
|
+
def get_next_op(self, str):
|
|
1410
|
+
r"""
|
|
1411
|
+
Return the next operator in a string.
|
|
1412
|
+
|
|
1413
|
+
INPUT:
|
|
1414
|
+
|
|
1415
|
+
- ``str`` -- string; this contains a logical
|
|
1416
|
+
expression
|
|
1417
|
+
|
|
1418
|
+
OUTPUT: the next operator as a string
|
|
1419
|
+
|
|
1420
|
+
EXAMPLES:
|
|
1421
|
+
|
|
1422
|
+
This example illustrates how to find the next operator in a formula::
|
|
1423
|
+
|
|
1424
|
+
sage: import sage.logic.propcalc as propcalc
|
|
1425
|
+
sage: s = propcalc.formula("f&p")
|
|
1426
|
+
sage: s.get_next_op("abra|cadabra")
|
|
1427
|
+
'|'
|
|
1428
|
+
|
|
1429
|
+
.. NOTE::
|
|
1430
|
+
|
|
1431
|
+
The parameter ``str`` is not necessarily the string
|
|
1432
|
+
representation of the calling object.
|
|
1433
|
+
"""
|
|
1434
|
+
i = 0
|
|
1435
|
+
while i < len(str) - 1 and str[i] != '&' and str[i] != '|':
|
|
1436
|
+
i += 1
|
|
1437
|
+
return str[i]
|
|
1438
|
+
|
|
1439
|
+
def length(self):
|
|
1440
|
+
r"""
|
|
1441
|
+
Return the length of ``self``.
|
|
1442
|
+
|
|
1443
|
+
OUTPUT:
|
|
1444
|
+
|
|
1445
|
+
The length of the Boolean formula. This is the number of operators plus
|
|
1446
|
+
the number of variables (counting multiplicity). Parentheses are ignored.
|
|
1447
|
+
|
|
1448
|
+
EXAMPLES::
|
|
1449
|
+
|
|
1450
|
+
sage: import sage.logic.propcalc as propcalc
|
|
1451
|
+
sage: s = propcalc.formula("a")
|
|
1452
|
+
sage: s.length()
|
|
1453
|
+
1
|
|
1454
|
+
sage: s = propcalc.formula("(a)")
|
|
1455
|
+
sage: s.length()
|
|
1456
|
+
1
|
|
1457
|
+
sage: s = propcalc.formula("~a")
|
|
1458
|
+
sage: s.length()
|
|
1459
|
+
2
|
|
1460
|
+
sage: s = propcalc.formula("a -> b")
|
|
1461
|
+
sage: s.length()
|
|
1462
|
+
3
|
|
1463
|
+
sage: s = propcalc.formula("alpha -> beta")
|
|
1464
|
+
sage: s.length()
|
|
1465
|
+
3
|
|
1466
|
+
sage: s = propcalc.formula("a -> a")
|
|
1467
|
+
sage: s.length()
|
|
1468
|
+
3
|
|
1469
|
+
sage: s = propcalc.formula("~(a -> b)")
|
|
1470
|
+
sage: s.length()
|
|
1471
|
+
4
|
|
1472
|
+
sage: s = propcalc.formula("((a&b)|(a&c))->~d")
|
|
1473
|
+
sage: s.length()
|
|
1474
|
+
10
|
|
1475
|
+
|
|
1476
|
+
TESTS::
|
|
1477
|
+
|
|
1478
|
+
sage: s = propcalc.formula("(((alpha) -> ((beta))))")
|
|
1479
|
+
sage: s.length()
|
|
1480
|
+
3
|
|
1481
|
+
"""
|
|
1482
|
+
return len(flatten(self.full_tree()))
|
|
1483
|
+
|
|
1484
|
+
# For backward compatibility, we allow `self.length()` to be called as
|
|
1485
|
+
# `len(self)`, but this may be deprecated in the future (see :issue:`32148`):
|
|
1486
|
+
__len__ = length
|
|
1487
|
+
|
|
1488
|
+
|
|
1489
|
+
# allow is_consequence to be called as a function (not only as a method of BooleanFormula)
|
|
1490
|
+
is_consequence = BooleanFormula.is_consequence
|