python-sat 1.8.dev25__cp310-cp310-macosx_11_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.
- pycard.cpython-310-darwin.so +0 -0
- pysat/__init__.py +24 -0
- pysat/_fileio.py +209 -0
- pysat/_utils.py +58 -0
- pysat/allies/__init__.py +0 -0
- pysat/allies/approxmc.py +385 -0
- pysat/allies/unigen.py +435 -0
- pysat/card.py +802 -0
- pysat/engines.py +1302 -0
- pysat/examples/__init__.py +0 -0
- pysat/examples/bbscan.py +663 -0
- pysat/examples/bica.py +691 -0
- pysat/examples/fm.py +527 -0
- pysat/examples/genhard.py +516 -0
- pysat/examples/hitman.py +653 -0
- pysat/examples/lbx.py +638 -0
- pysat/examples/lsu.py +496 -0
- pysat/examples/mcsls.py +610 -0
- pysat/examples/models.py +189 -0
- pysat/examples/musx.py +344 -0
- pysat/examples/optux.py +710 -0
- pysat/examples/primer.py +620 -0
- pysat/examples/rc2.py +1858 -0
- pysat/examples/usage.py +63 -0
- pysat/formula.py +5619 -0
- pysat/pb.py +463 -0
- pysat/process.py +363 -0
- pysat/solvers.py +7591 -0
- pysolvers.cpython-310-darwin.so +0 -0
- python_sat-1.8.dev25.data/scripts/approxmc.py +385 -0
- python_sat-1.8.dev25.data/scripts/bbscan.py +663 -0
- python_sat-1.8.dev25.data/scripts/bica.py +691 -0
- python_sat-1.8.dev25.data/scripts/fm.py +527 -0
- python_sat-1.8.dev25.data/scripts/genhard.py +516 -0
- python_sat-1.8.dev25.data/scripts/lbx.py +638 -0
- python_sat-1.8.dev25.data/scripts/lsu.py +496 -0
- python_sat-1.8.dev25.data/scripts/mcsls.py +610 -0
- python_sat-1.8.dev25.data/scripts/models.py +189 -0
- python_sat-1.8.dev25.data/scripts/musx.py +344 -0
- python_sat-1.8.dev25.data/scripts/optux.py +710 -0
- python_sat-1.8.dev25.data/scripts/primer.py +620 -0
- python_sat-1.8.dev25.data/scripts/rc2.py +1858 -0
- python_sat-1.8.dev25.data/scripts/unigen.py +435 -0
- python_sat-1.8.dev25.dist-info/METADATA +45 -0
- python_sat-1.8.dev25.dist-info/RECORD +48 -0
- python_sat-1.8.dev25.dist-info/WHEEL +6 -0
- python_sat-1.8.dev25.dist-info/licenses/LICENSE.txt +21 -0
- python_sat-1.8.dev25.dist-info/top_level.txt +3 -0
pysat/examples/bica.py
ADDED
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
#-*- coding:utf-8 -*-
|
|
3
|
+
##
|
|
4
|
+
## bica.py
|
|
5
|
+
##
|
|
6
|
+
## Created on: Jul 28, 2025
|
|
7
|
+
## Author: Alexey Ignatiev
|
|
8
|
+
## E-mail: alexey.ignatiev@monash.edu
|
|
9
|
+
##
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
===============
|
|
13
|
+
List of classes
|
|
14
|
+
===============
|
|
15
|
+
|
|
16
|
+
.. autosummary::
|
|
17
|
+
:nosignatures:
|
|
18
|
+
|
|
19
|
+
Bica
|
|
20
|
+
|
|
21
|
+
==================
|
|
22
|
+
Module description
|
|
23
|
+
==================
|
|
24
|
+
|
|
25
|
+
A reimplementation of the Bica algorithm for SAT-based minimisation /
|
|
26
|
+
simplification of Boolean formulas [1]_. Given an arbitrary Boolean
|
|
27
|
+
formula, it computes its smallest size prime cover, i.e. it constructs a
|
|
28
|
+
smallest CNF (DNF, resp.) representation comprising the formula's prime
|
|
29
|
+
implicates (implicants, reps.), which is *equivalent* to the original
|
|
30
|
+
formula.
|
|
31
|
+
|
|
32
|
+
The original Bica algorithm is inspired by the well-known Quine-McCluskey
|
|
33
|
+
algorithm [2]_ [3]_ [4]_. It is *entirely* SAT-based and can deal with
|
|
34
|
+
arbitrary Boolean formulas. The algorithm involves two steps: first, it
|
|
35
|
+
enumerates all the prime implicants (or implicates, depending on the
|
|
36
|
+
user's choice) of the formula by utilising the :class:`.Primer` algorithm
|
|
37
|
+
[5]_; second, it computes the formula's smallest prime cover by means of
|
|
38
|
+
reducing the problem to the computation of a smallest minimal
|
|
39
|
+
unsatisfiable subformula (SMUS), invoking the :class:`.OptUx` SMUS solver
|
|
40
|
+
[6]_.
|
|
41
|
+
|
|
42
|
+
The word "bica" is Portuguese and means a cup of extremely strong and
|
|
43
|
+
high-quality coffee (often seen as a synonym of espresso). This way, the
|
|
44
|
+
Bica algorithm continues the *coffee inspiration* of the `Espresso logic
|
|
45
|
+
minimizer
|
|
46
|
+
<https://ptolemy.berkeley.edu/projects/embedded/pubs/downloads/espresso/>`_
|
|
47
|
+
[7]_, hence signifying the construction of the formula's *essence* or
|
|
48
|
+
*crux*. (In fact, this reimplementation was initially planned to have the
|
|
49
|
+
name *Crux*, to relate with `one of the most prominent constellations of
|
|
50
|
+
the southern sky <https://en.wikipedia.org/wiki/Crux>`_, often used in
|
|
51
|
+
navigation to determine the Southern Celestial Pole.)
|
|
52
|
+
|
|
53
|
+
.. [1] Alexey Ignatiev, Alessandro Previti, Joao Marques-Silva.
|
|
54
|
+
*SAT-Based Formula Simplification*. SAT 2015. pp. 287-298
|
|
55
|
+
|
|
56
|
+
.. [2] Willard V. Quine. *The Problem of Simplifying Truth Functions*.
|
|
57
|
+
American Mathematical Monthly 59(8). 1952. pp. 521-531
|
|
58
|
+
|
|
59
|
+
.. [3] Willard V. Quine. *A Way to Simplify Truth Functions*.
|
|
60
|
+
American Mathematical Monthly 62(9). 1955. pp. 627-631
|
|
61
|
+
|
|
62
|
+
.. [4] Edward J. McCluskey. *Minimization of Boolean Functions*. Bell
|
|
63
|
+
System Technical Journal 35(6). 1956. pp 1417-1444
|
|
64
|
+
|
|
65
|
+
.. [5] Alessandro Previti, Alexey Ignatiev, António Morgado,
|
|
66
|
+
Joao Marques-Silva. *Prime Compilation of Non-Clausal Formulae.*
|
|
67
|
+
IJCAI 2015. pp. 1980-1988
|
|
68
|
+
|
|
69
|
+
.. [6] Alexey Ignatiev, Alessandro Previti, Mark H. Liffiton, Joao
|
|
70
|
+
Marques-Silva. *Smallest MUS Extraction with Minimal Hitting Set
|
|
71
|
+
Dualization*. CP 2015. pp. 173-182
|
|
72
|
+
|
|
73
|
+
.. [7] Robert K. Brayton, Alberto L. Sangiovanni-Vincentelli,, Curtis T.
|
|
74
|
+
McMullen, Gary D. Hachtel. *Logic Minimization Algorithms for VLSI
|
|
75
|
+
Synthesis*. Kluwer Academic Publishers. 1984
|
|
76
|
+
|
|
77
|
+
The file provides a class :class:`Bica`, which contains the above
|
|
78
|
+
algorithm reimplementation. It can be applied to any formula in the
|
|
79
|
+
:class:`.CNF` or :class:`.Formula` format.
|
|
80
|
+
|
|
81
|
+
The implementation can be used as an executable (the list of available
|
|
82
|
+
command-line options can be shown using ``bica.py -h``) in the following
|
|
83
|
+
way:
|
|
84
|
+
|
|
85
|
+
::
|
|
86
|
+
|
|
87
|
+
$ xzcat formula.cnf.xz
|
|
88
|
+
p cnf 7 7
|
|
89
|
+
-1 2 0
|
|
90
|
+
1 -2 0
|
|
91
|
+
1 2 -3 4 0
|
|
92
|
+
1 2 3 -4 0
|
|
93
|
+
1 2 3 4 -5 6 0
|
|
94
|
+
1 2 3 4 5 -6 0
|
|
95
|
+
1 2 3 4 5 6 7 0
|
|
96
|
+
|
|
97
|
+
$ bica.py -v formula.cnf.xz
|
|
98
|
+
c prime -1 +2 0
|
|
99
|
+
c prime +1 -2 0
|
|
100
|
+
c prime +1 -3 +4 0
|
|
101
|
+
c prime +2 -3 +4 0
|
|
102
|
+
c prime +1 +3 -4 0
|
|
103
|
+
c prime +2 +3 -4 0
|
|
104
|
+
c prime +1 +3 -5 +6 0
|
|
105
|
+
c prime +1 +3 +5 -6 0
|
|
106
|
+
c prime +1 +3 +5 +7 0
|
|
107
|
+
c prime +1 +3 +6 +7 0
|
|
108
|
+
c prime +1 +4 +5 +7 0
|
|
109
|
+
c prime +1 +4 +6 +7 0
|
|
110
|
+
c prime +1 +4 +5 -6 0
|
|
111
|
+
c prime +1 +4 -5 +6 0
|
|
112
|
+
c prime +2 +4 -5 +6 0
|
|
113
|
+
c prime +2 +3 -5 +6 0
|
|
114
|
+
c prime +2 +3 +5 -6 0
|
|
115
|
+
c prime +2 +4 +5 -6 0
|
|
116
|
+
c prime +2 +3 +5 +7 0
|
|
117
|
+
c prime +2 +4 +5 +7 0
|
|
118
|
+
c prime +2 +3 +6 +7 0
|
|
119
|
+
c prime +2 +4 +6 +7 0
|
|
120
|
+
p cnf 7 7
|
|
121
|
+
-1 2 0
|
|
122
|
+
1 -2 0
|
|
123
|
+
2 -3 4 0
|
|
124
|
+
2 3 -4 0
|
|
125
|
+
2 3 -5 6 0
|
|
126
|
+
2 4 5 -6 0
|
|
127
|
+
2 4 6 7 0
|
|
128
|
+
o 7
|
|
129
|
+
c primer time: 0.0002
|
|
130
|
+
c optux time: 0.0001
|
|
131
|
+
c total time: 0.0004
|
|
132
|
+
|
|
133
|
+
Alternatively, the algorithm can be accessed and invoked through the
|
|
134
|
+
standard ``import`` interface of Python, e.g.
|
|
135
|
+
|
|
136
|
+
.. code-block:: python
|
|
137
|
+
|
|
138
|
+
>>> from pysat.examples.bica import Bica
|
|
139
|
+
>>> from pysat.formula import Atom, CNF, Formula
|
|
140
|
+
>>>
|
|
141
|
+
>>> x, y, z = [Atom(v + 1) for v in range(3)]
|
|
142
|
+
>>> formula = ~(~x >> y) | (x & z)
|
|
143
|
+
>>>
|
|
144
|
+
>>> with Bica(formula) as bica:
|
|
145
|
+
... for minf in bica.enumerate():
|
|
146
|
+
... minf = CNF(from_clauses=[bica.primes[i - 1] for i in minf])
|
|
147
|
+
... print(minf)
|
|
148
|
+
CNF(from_string='p cnf 3 2\\n-1 3 0\\n-2 1 0')
|
|
149
|
+
|
|
150
|
+
As the example above demonstrates, Bica can enumerate all irreducible
|
|
151
|
+
prime covers of an input formula (or as many as the user wants). In this
|
|
152
|
+
particular example, the formula :math:`f\\triangleq\\neg{(\\neg{x}
|
|
153
|
+
\\rightarrow y)} \\vee (x \\wedge z)` has the only irreducible prime cover
|
|
154
|
+
:math:`(\\neg{x} \\vee z) \\vee (\\neg{y} \\vee x)`.
|
|
155
|
+
|
|
156
|
+
==============
|
|
157
|
+
Module details
|
|
158
|
+
==============
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
#
|
|
162
|
+
#==============================================================================
|
|
163
|
+
import collections
|
|
164
|
+
import getopt
|
|
165
|
+
import os
|
|
166
|
+
from pysat.examples.optux import OptUx
|
|
167
|
+
from pysat.examples.primer import Primer
|
|
168
|
+
from pysat.formula import IDPool, CNF, Neg, WCNF
|
|
169
|
+
from pysat.solvers import Solver
|
|
170
|
+
import re
|
|
171
|
+
import sys
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
#
|
|
175
|
+
#==============================================================================
|
|
176
|
+
class Bica:
|
|
177
|
+
"""
|
|
178
|
+
A simple reimplementation of the Bica algorithm. Although the original
|
|
179
|
+
implementation of Bica was also written in Python, both phases of the
|
|
180
|
+
algorithm (prime enumeration and prime cover computation) were written
|
|
181
|
+
in C++ and interfaced through the file IO. The current implementation
|
|
182
|
+
relies on pure Python alternatives for both of the phases. Namely, it
|
|
183
|
+
first calls :class:`.Primer` to enumerate all the primes of a given
|
|
184
|
+
input formula followed by the computation of a smallest size cover
|
|
185
|
+
with :class:`.OptUx`.
|
|
186
|
+
|
|
187
|
+
Importantly, thanks to the capabilities of :class:`.OptUx`, the
|
|
188
|
+
current implementation allows one to enumerate multiple (or all)
|
|
189
|
+
smallest size prime covers. Alternatively, one can even opt for
|
|
190
|
+
computing **a minimal** prime cover (not a minimum one).
|
|
191
|
+
|
|
192
|
+
The second phase of the approach also allows a user to compute the
|
|
193
|
+
target representation either *unweighted* or *weighted*. In the former
|
|
194
|
+
case, the size is measured as the number of primes included in the
|
|
195
|
+
minimal representation. In the latter case, the lenght of each prime
|
|
196
|
+
is taken into account such that the overall size is counted as the sum
|
|
197
|
+
of lengths of the primes included. This is controlled with the use of
|
|
198
|
+
input parameter ``weighted``.
|
|
199
|
+
|
|
200
|
+
Additionally, one may additionally want to specify the negation of the
|
|
201
|
+
input formula, which is required either in first phase of the
|
|
202
|
+
algorithm or in both phases, depending on whether the user wants to
|
|
203
|
+
compute a DNF or CNF representation. If no negated formula is
|
|
204
|
+
provided, Bica will create one internally.
|
|
205
|
+
|
|
206
|
+
As both phases of the algorithm rely on implicit hitting set
|
|
207
|
+
enumeration, they share the same input parameters. To allow a user to
|
|
208
|
+
apply various parameters for the two phases, :class:`.Hitman`'s
|
|
209
|
+
parameters of the :class:`.Primer` phase are prefixed with a ``p``
|
|
210
|
+
while the same parameters used by :class:`.OptUx` are prefixed with an
|
|
211
|
+
``o``.
|
|
212
|
+
|
|
213
|
+
The complete list of initialiser's arguments is as follows:
|
|
214
|
+
|
|
215
|
+
:param formula: input formula whose prime representation is sought
|
|
216
|
+
:param negated: input's formula negation (if any)
|
|
217
|
+
:param target: either ``'cnf'`` or ``'dnf'``
|
|
218
|
+
:param psolver: SAT oracle name
|
|
219
|
+
:param padapt: detect and adapt intrinsic AtMost1 constraints
|
|
220
|
+
:param pdcalls: apply clause D oracle calls (for unsorted enumeration only)
|
|
221
|
+
:param pexhaust: do core exhaustion
|
|
222
|
+
:param pminz: do heuristic core reduction
|
|
223
|
+
:param ppuresat: use pure SAT-based hitting set enumeration
|
|
224
|
+
:param psearch: dual prime reduction strategy
|
|
225
|
+
:param punsorted: apply unsorted MUS enumeration
|
|
226
|
+
:param ptrim: do core trimming at most this number of times
|
|
227
|
+
:param osolver: SAT oracle name
|
|
228
|
+
:param oadapt: detect and adapt intrinsic AtMost1 constraints
|
|
229
|
+
:param odcalls: apply clause D oracle calls (for unsorted enumeration only)
|
|
230
|
+
:param oexhaust: do core exhaustion
|
|
231
|
+
:param ominz: do heuristic core reduction
|
|
232
|
+
:param onodisj: do not enumerate disjoint MCSes with OptUx
|
|
233
|
+
:param opuresat: use pure SAT-based hitting set enumeration
|
|
234
|
+
:param ounsorted: apply unsorted MUS enumeration
|
|
235
|
+
:param otrim: do core trimming at most this number of times
|
|
236
|
+
:param weighted: get a minimal cover wrt. the total number of literals
|
|
237
|
+
:param verbose: verbosity level
|
|
238
|
+
|
|
239
|
+
:type formula: :class:`.Formula` or :class:`.CNF`
|
|
240
|
+
:type negated: :class:`.Formula` or :class:`.CNF`
|
|
241
|
+
:type target: str
|
|
242
|
+
:type psolver: str
|
|
243
|
+
:type padapt: bool
|
|
244
|
+
:type pdcalls: bool
|
|
245
|
+
:type pexhaust: bool
|
|
246
|
+
:type pminz: bool
|
|
247
|
+
:type ppuresat: str
|
|
248
|
+
:type psearch: str
|
|
249
|
+
:type punsorted: bool
|
|
250
|
+
:type ptrim: int
|
|
251
|
+
:type osolver: str
|
|
252
|
+
:type oadapt: bool
|
|
253
|
+
:type odcalls: bool
|
|
254
|
+
:type oexhaust: bool
|
|
255
|
+
:type ominz: bool
|
|
256
|
+
:type onodisj: bool
|
|
257
|
+
:type opuresat: str
|
|
258
|
+
:type ounsorted: bool
|
|
259
|
+
:type otrim: int
|
|
260
|
+
:type weighted: bool
|
|
261
|
+
:type verbose: int
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
def __init__(self, formula, negated=None, target='cnf', psolver='cd19',
|
|
265
|
+
padapt=False, pdcalls=False, pexhaust=False, pminz=False,
|
|
266
|
+
ppuresat=False, psearch='lin', punsorted=False, ptrim=False,
|
|
267
|
+
osolver='mgh', oadapt=False, odcalls=False, oexhaust=False,
|
|
268
|
+
ominz=False, onodisj=False, opuresat=False, ounsorted=False,
|
|
269
|
+
otrim=False, weighted=False, verbose=0):
|
|
270
|
+
"""
|
|
271
|
+
Initialiser.
|
|
272
|
+
"""
|
|
273
|
+
|
|
274
|
+
# copying some of the arguments
|
|
275
|
+
self.form = formula
|
|
276
|
+
self.fneg = negated
|
|
277
|
+
self.target = target
|
|
278
|
+
self.verbose = verbose
|
|
279
|
+
self.weighted = weighted
|
|
280
|
+
|
|
281
|
+
# Primer's parameters
|
|
282
|
+
self.psolver = psolver
|
|
283
|
+
self.padapt = padapt
|
|
284
|
+
self.pdcalls = pdcalls
|
|
285
|
+
self.pexhaust = pexhaust
|
|
286
|
+
self.pminz = pminz
|
|
287
|
+
self.ppuresat = ppuresat
|
|
288
|
+
self.psearch = psearch
|
|
289
|
+
self.punsorted = punsorted
|
|
290
|
+
self.ptrim = ptrim
|
|
291
|
+
|
|
292
|
+
# OptUx's parameters
|
|
293
|
+
self.osolver = osolver
|
|
294
|
+
self.oadapt = oadapt
|
|
295
|
+
self.odcalls = odcalls
|
|
296
|
+
self.oexhaust = oexhaust
|
|
297
|
+
self.ominz = ominz
|
|
298
|
+
self.onodisj = onodisj
|
|
299
|
+
self.opuresat = opuresat
|
|
300
|
+
self.ounsorted = ounsorted
|
|
301
|
+
self.otrim = otrim
|
|
302
|
+
|
|
303
|
+
# oracles solving the two subproblems
|
|
304
|
+
self.primer, self.optux, self.primes = None, None, []
|
|
305
|
+
|
|
306
|
+
# if no negated formula was given, we create one
|
|
307
|
+
if self.fneg is None:
|
|
308
|
+
self.fneg = Neg(self.form)
|
|
309
|
+
|
|
310
|
+
# input formula for OptUx to be gradually constructed
|
|
311
|
+
# during prime enumeration phase;
|
|
312
|
+
self.pform = WCNF()
|
|
313
|
+
if self.target == 'dnf':
|
|
314
|
+
for cl in self.form:
|
|
315
|
+
self.pform.append(cl)
|
|
316
|
+
else:
|
|
317
|
+
# if the target is CNF, we need to add the negated formula
|
|
318
|
+
for cl in self.fneg:
|
|
319
|
+
self.pform.append(cl)
|
|
320
|
+
|
|
321
|
+
self._init_primer()
|
|
322
|
+
|
|
323
|
+
def _init_primer(self):
|
|
324
|
+
"""
|
|
325
|
+
Constructs and initialises the Primer object.
|
|
326
|
+
"""
|
|
327
|
+
|
|
328
|
+
implicates = False if self.target == 'dnf' else True
|
|
329
|
+
|
|
330
|
+
self.primer = Primer(self.form, negated=self.fneg,
|
|
331
|
+
solver=self.psolver, implicates=implicates,
|
|
332
|
+
adapt=self.padapt, dcalls=self.pdcalls,
|
|
333
|
+
exhaust=self.pexhaust, minz=self.pminz,
|
|
334
|
+
puresat=self.ppuresat, search=self.psearch,
|
|
335
|
+
unsorted=self.punsorted, trim=self.ptrim,
|
|
336
|
+
verbose=self.verbose - 1)
|
|
337
|
+
|
|
338
|
+
def __del__(self):
|
|
339
|
+
"""
|
|
340
|
+
Destructor.
|
|
341
|
+
"""
|
|
342
|
+
|
|
343
|
+
self.delete()
|
|
344
|
+
|
|
345
|
+
def __enter__(self):
|
|
346
|
+
"""
|
|
347
|
+
'with' constructor.
|
|
348
|
+
"""
|
|
349
|
+
|
|
350
|
+
return self
|
|
351
|
+
|
|
352
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
353
|
+
"""
|
|
354
|
+
'with' destructor.
|
|
355
|
+
"""
|
|
356
|
+
|
|
357
|
+
self.delete()
|
|
358
|
+
|
|
359
|
+
def delete(self):
|
|
360
|
+
"""
|
|
361
|
+
Explicit destructor of both primer and optux. Also, clears the
|
|
362
|
+
list of primes computed.
|
|
363
|
+
|
|
364
|
+
(The former is constructed in Bica's initialiser while the latter
|
|
365
|
+
is created on demand, once the first phase of the algorithm has
|
|
366
|
+
been finished, i.e. when all the primes have been enumerated.)
|
|
367
|
+
"""
|
|
368
|
+
|
|
369
|
+
self.primes = []
|
|
370
|
+
|
|
371
|
+
if self.primer:
|
|
372
|
+
self.primer.delete()
|
|
373
|
+
self.primer = None
|
|
374
|
+
|
|
375
|
+
if self.optux:
|
|
376
|
+
self.optux.delete()
|
|
377
|
+
self.optux = None
|
|
378
|
+
|
|
379
|
+
def compute(self):
|
|
380
|
+
"""
|
|
381
|
+
Computes a single minimum representation of the input formula as a
|
|
382
|
+
list of indices of prime implicants (or implicates) determined to
|
|
383
|
+
comprise the representation.
|
|
384
|
+
|
|
385
|
+
Note that the indices in the returned list start from ``1``, i.e.
|
|
386
|
+
the user is supposed to subtract ``1`` for each of them to get the
|
|
387
|
+
correct list of primes. Consider the following example:
|
|
388
|
+
|
|
389
|
+
.. code-block:: python
|
|
390
|
+
|
|
391
|
+
>>> from pysat.examples.bica import Bica
|
|
392
|
+
>>> from pysat.formula import CNF
|
|
393
|
+
>>>
|
|
394
|
+
>>> cnf = CNF(from_clauses=[[-1, 2], [-2, 3], [-3, 4], [4, 5]])
|
|
395
|
+
>>>
|
|
396
|
+
>>> with Bica(formula) as bica:
|
|
397
|
+
... for minf in bica.enumerate():
|
|
398
|
+
... minf = CNF(from_clauses=[bica.primes[i - 1] for i in minf])
|
|
399
|
+
... print(minf)
|
|
400
|
+
... print(f'all primes implicates: {bica.primes}')
|
|
401
|
+
CNF(from_string='p cnf 4 3\\n4 0\\n-2 3 0\\n-1 2 0')
|
|
402
|
+
all prime implicates: [[4], [-2, 3], [-1, 3], [-1, 2]]
|
|
403
|
+
|
|
404
|
+
:rtype: list(int)
|
|
405
|
+
"""
|
|
406
|
+
|
|
407
|
+
# first, collecing all the primes
|
|
408
|
+
if not self.primes:
|
|
409
|
+
for prime in self.primer.enumerate():
|
|
410
|
+
self.primes.append(prime)
|
|
411
|
+
|
|
412
|
+
# recording the prime
|
|
413
|
+
if self.target == 'cnf':
|
|
414
|
+
self.pform.append(prime, weight=len(prime) if self.weighted else 1)
|
|
415
|
+
else:
|
|
416
|
+
self.pform.append([-l for l in prime], weight=len(prime) if self.weighted else 1)
|
|
417
|
+
|
|
418
|
+
# reporting it
|
|
419
|
+
if self.verbose > 1:
|
|
420
|
+
print('c prime {0} 0'.format(' '.join(['{0}{1}'.format('+' if v > 0 else '', v) for v in prime])))
|
|
421
|
+
|
|
422
|
+
if not self.optux:
|
|
423
|
+
# creating an OptUx object
|
|
424
|
+
self.optux = OptUx(self.pform, solver=self.osolver,
|
|
425
|
+
adapt=self.oadapt, cover=None,
|
|
426
|
+
dcalls=self.odcalls, exhaust=self.oexhaust,
|
|
427
|
+
minz=self.ominz, nodisj=self.onodisj,
|
|
428
|
+
puresat=self.opuresat, unsorted=self.ounsorted,
|
|
429
|
+
trim=self.otrim, verbose=self.verbose)
|
|
430
|
+
|
|
431
|
+
return self.optux.compute()
|
|
432
|
+
|
|
433
|
+
def enumerate(self):
|
|
434
|
+
"""
|
|
435
|
+
This is generator method iterating through minimum representations
|
|
436
|
+
and enumerating them until no more of them exists, or a user
|
|
437
|
+
decides to stop the process.
|
|
438
|
+
|
|
439
|
+
.. code-block:: python
|
|
440
|
+
|
|
441
|
+
>>> from pysat.examples.bica import Bica
|
|
442
|
+
>>> from pysat.formula import *
|
|
443
|
+
>>>
|
|
444
|
+
>>> cnf = CNF(from_file='test.cnf')
|
|
445
|
+
>>> print(cnf)
|
|
446
|
+
CNF(from_string='c n orig vars 5\\np cnf 11 5\\n-1 2 0\\n1 -2 0\\n1 2 -3 4 0\\n1 2 3 -4 0\\n1 2 3 4 5 0')
|
|
447
|
+
>>>
|
|
448
|
+
>>> with Bica(cnf) as bica:
|
|
449
|
+
... for minf in bica.enumerate():
|
|
450
|
+
... print(minf) # prime indices start from 1!
|
|
451
|
+
[1, 2, 8, 9, 10]
|
|
452
|
+
[1, 2, 3, 8, 10]
|
|
453
|
+
[1, 2, 3, 6, 10]
|
|
454
|
+
[1, 2, 3, 4, 8]
|
|
455
|
+
[1, 2, 3, 4, 6]
|
|
456
|
+
[1, 2, 4, 7, 8]
|
|
457
|
+
[1, 2, 4, 8, 9]
|
|
458
|
+
[1, 2, 4, 5, 8]
|
|
459
|
+
[1, 2, 4, 6, 7]
|
|
460
|
+
[1, 2, 4, 5, 6]
|
|
461
|
+
[1, 2, 4, 6, 9]
|
|
462
|
+
[1, 2, 6, 7, 10]
|
|
463
|
+
[1, 2, 7, 8, 10]
|
|
464
|
+
[1, 2, 5, 8, 10]
|
|
465
|
+
[1, 2, 5, 6, 10]
|
|
466
|
+
[1, 2, 6, 9, 10]
|
|
467
|
+
|
|
468
|
+
:rtype: list(int)
|
|
469
|
+
"""
|
|
470
|
+
|
|
471
|
+
done = False
|
|
472
|
+
|
|
473
|
+
while not done:
|
|
474
|
+
minf = self.compute()
|
|
475
|
+
|
|
476
|
+
if minf is not None:
|
|
477
|
+
yield minf
|
|
478
|
+
else:
|
|
479
|
+
done = True
|
|
480
|
+
|
|
481
|
+
def oracle_time(self):
|
|
482
|
+
"""
|
|
483
|
+
This method computes and returns the total SAT solving time
|
|
484
|
+
involved, including the time spent by :class:`.Primer` and
|
|
485
|
+
:class:`.OptUx`, which implement the first and second phases
|
|
486
|
+
of the algorithm, respectively.
|
|
487
|
+
|
|
488
|
+
:rtype: float
|
|
489
|
+
"""
|
|
490
|
+
|
|
491
|
+
return self.primer.oracle_time() + self.optux.oracle_time()
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
class Crux(Bica):
|
|
495
|
+
"""
|
|
496
|
+
A clone of Bica, to be used interchangeably.
|
|
497
|
+
"""
|
|
498
|
+
|
|
499
|
+
pass
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
#
|
|
503
|
+
#==============================================================================
|
|
504
|
+
def parse_options():
|
|
505
|
+
"""
|
|
506
|
+
Parses command-line options:
|
|
507
|
+
"""
|
|
508
|
+
|
|
509
|
+
try:
|
|
510
|
+
opts, args = getopt.getopt(sys.argv[1:],
|
|
511
|
+
'aAdDe:hmMnp:P:r:R:s:S:t:T:uUvwxX',
|
|
512
|
+
['padapt', 'oadapt', 'pdcalls', 'odcalls',
|
|
513
|
+
'enum=', 'help', 'pminimize', 'ominimize',
|
|
514
|
+
'no-disj', 'ppuresat=', 'opuresat=',
|
|
515
|
+
'reduce=', 'repr=', 'psolver=',
|
|
516
|
+
'osolver=', 'ptrim=', 'otrim=',
|
|
517
|
+
'punsorted', 'ounsorted', 'verbose',
|
|
518
|
+
'weighted', 'pexhaust', 'oexhaust'])
|
|
519
|
+
except getopt.GetoptError as err:
|
|
520
|
+
sys.stderr.write(str(err).capitalize())
|
|
521
|
+
usage()
|
|
522
|
+
sys.exit(1)
|
|
523
|
+
|
|
524
|
+
to_enum = 1
|
|
525
|
+
mode = 'cnf'
|
|
526
|
+
search = 'lin'
|
|
527
|
+
verbose = 1
|
|
528
|
+
weighted = False
|
|
529
|
+
|
|
530
|
+
padapt = False
|
|
531
|
+
oadapt = False
|
|
532
|
+
pdcalls = False
|
|
533
|
+
odcalls = False
|
|
534
|
+
pexhaust = False
|
|
535
|
+
oexhaust = False
|
|
536
|
+
pminz = False
|
|
537
|
+
ominz = False
|
|
538
|
+
nodisj = False
|
|
539
|
+
psolver = 'cd19'
|
|
540
|
+
osolver = 'mgh'
|
|
541
|
+
ppuresat = False
|
|
542
|
+
opuresat = False
|
|
543
|
+
punsorted = False
|
|
544
|
+
ounsorted = False
|
|
545
|
+
ptrim = 0
|
|
546
|
+
otrim = 0
|
|
547
|
+
|
|
548
|
+
for opt, arg in opts:
|
|
549
|
+
if opt in ('-a', '--padapt'):
|
|
550
|
+
padapt = True
|
|
551
|
+
elif opt in ('-A', '--oadapt'):
|
|
552
|
+
oadapt = True
|
|
553
|
+
elif opt in ('-d', '--pdcalls'):
|
|
554
|
+
pdcalls = True
|
|
555
|
+
elif opt in ('-D', '--odcalls'):
|
|
556
|
+
odcalls = True
|
|
557
|
+
elif opt in ('-e', '--enum'):
|
|
558
|
+
to_enum = str(arg)
|
|
559
|
+
if to_enum != 'all':
|
|
560
|
+
to_enum = int(to_enum)
|
|
561
|
+
elif opt in ('-h', '--help'):
|
|
562
|
+
usage()
|
|
563
|
+
sys.exit(0)
|
|
564
|
+
elif opt in ('-m', '--pminimize'):
|
|
565
|
+
pminz = True
|
|
566
|
+
elif opt in ('-M', '--ominimize'):
|
|
567
|
+
ominz = True
|
|
568
|
+
elif opt in ('-n', '--no-disj'):
|
|
569
|
+
nodisj = True
|
|
570
|
+
elif opt in ('-p', '--ppuresat'):
|
|
571
|
+
ppuresat = str(arg)
|
|
572
|
+
elif opt in ('-P', '--opuresat'):
|
|
573
|
+
opuresat = str(arg)
|
|
574
|
+
elif opt in ('-r', '--reduce'):
|
|
575
|
+
search = str(arg)
|
|
576
|
+
assert search in ('lin', 'bin'), 'Wrong minimisation method: {0}'.format(search)
|
|
577
|
+
elif opt in ('-R', '--repr'):
|
|
578
|
+
mode = str(arg)
|
|
579
|
+
elif opt in ('-s', '--psolver'):
|
|
580
|
+
psolver = str(arg)
|
|
581
|
+
elif opt in ('-S', '--osolver'):
|
|
582
|
+
osolver = str(arg)
|
|
583
|
+
elif opt in ('-u', '--punsorted'):
|
|
584
|
+
punsorted = True
|
|
585
|
+
elif opt in ('-U', '--ounsorted'):
|
|
586
|
+
ounsorted = True
|
|
587
|
+
elif opt in ('-t', '--ptrim'):
|
|
588
|
+
ptrim = int(arg)
|
|
589
|
+
elif opt in ('-T', '--otrim'):
|
|
590
|
+
otrim = int(arg)
|
|
591
|
+
elif opt in ('-v', '--verbose'):
|
|
592
|
+
verbose += 1
|
|
593
|
+
elif opt in ('-w', '--weighted'):
|
|
594
|
+
weighted = True
|
|
595
|
+
elif opt in ('-x', '--pexhaust'):
|
|
596
|
+
pexhaust = True
|
|
597
|
+
elif opt in ('-X', '--oexhaust'):
|
|
598
|
+
oexhaust = True
|
|
599
|
+
else:
|
|
600
|
+
assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
|
|
601
|
+
|
|
602
|
+
return to_enum, mode, padapt, oadapt, pdcalls, odcalls, pexhaust, \
|
|
603
|
+
oexhaust, pminz, ominz, nodisj, ptrim, otrim, search, psolver, \
|
|
604
|
+
osolver, ppuresat, opuresat, punsorted, ounsorted, verbose, \
|
|
605
|
+
weighted, args
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
#
|
|
609
|
+
#==============================================================================
|
|
610
|
+
def usage():
|
|
611
|
+
"""
|
|
612
|
+
Prints usage message.
|
|
613
|
+
"""
|
|
614
|
+
|
|
615
|
+
print('Usage:', os.path.basename(sys.argv[0]), '[options]')
|
|
616
|
+
print('Options:')
|
|
617
|
+
print(' -a, --padapt Try to adapt (simplify) input formula [Primer]')
|
|
618
|
+
print(' -A, --oadapt Try to adapt (simplify) input formula [OptUx]')
|
|
619
|
+
print(' -d, --pdcalls Apply clause D calls (in unsorted enumeration only) [Primer]')
|
|
620
|
+
print(' -D, --odcalls Apply clause D calls (in unsorted enumeration only) [OptUx]')
|
|
621
|
+
print(' -e, --enum=<int> Enumerate this many "top-k" solutions')
|
|
622
|
+
print(' Available values: [1 .. INT_MAX], all (default = 1)')
|
|
623
|
+
print(' -h, --help Print this help message')
|
|
624
|
+
print(' -m, --pminimize Use a heuristic unsatisfiable core minimizer [Primer]')
|
|
625
|
+
print(' -M, --ominimize Use a heuristic unsatisfiable core minimizer [OptUx]')
|
|
626
|
+
print(' -n, --no-disj Do not enumerate disjoint MCSes [OptUx]')
|
|
627
|
+
print(' -p, --ppuresat=<string> Use a pure SAT-based hitting set enumerator [Primer]')
|
|
628
|
+
print(' Available values: cd15, cd19, lgl, mgh (default = mgh)')
|
|
629
|
+
print(' Requires: unsorted mode, i.e. \'-u\'')
|
|
630
|
+
print(' -P, --opuresat=<string> Use a pure SAT-based hitting set enumerator [OptUx]')
|
|
631
|
+
print(' Available values: cd15, cd19, lgl, mgh (default = mgh)')
|
|
632
|
+
print(' Requires: unsorted mode, i.e. \'-U\'')
|
|
633
|
+
print(' -r, --reduce Dual prime minimiser [Primer]')
|
|
634
|
+
print(' Available values: lin, bin (default = lin)')
|
|
635
|
+
print(' -R, --repr=<string> Target representation')
|
|
636
|
+
print(' Available values: dnf, cnf (default = cnf)')
|
|
637
|
+
print(' -s, --psolver SAT solver to use [Primer]')
|
|
638
|
+
print(' Available values: cd, cd15, cd19, g3, g41, g42, lgl, mcb, mcm, mpl, m22, mc, mg3, mgh (default = cd19)')
|
|
639
|
+
print(' -S, --osolver SAT solver to use [OptUx]')
|
|
640
|
+
print(' Available values: cd, cd15, cd19, g3, g41, g42, lgl, mcb, mcm, mpl, m22, mc, mg3, mgh (default = mgh)')
|
|
641
|
+
print(' -t, --ptrim=<int> How many times to trim unsatisfiable cores [Primer]')
|
|
642
|
+
print(' Available values: [0 .. INT_MAX] (default = 0)')
|
|
643
|
+
print(' -T, --otrim=<int> How many times to trim unsatisfiable cores [OptUx]')
|
|
644
|
+
print(' Available values: [0 .. INT_MAX] (default = 0)')
|
|
645
|
+
print(' -u, --punsorted Enumerate MUSes in an unsorted way using LBX [Primer]')
|
|
646
|
+
print(' -U, --ounsorted Enumerate MUSes in an unsorted way using LBX [OptUx]')
|
|
647
|
+
print(' -v, --verbose Be verbose')
|
|
648
|
+
print(' -w, --weighted Target optimal representations with respect the total number of literals')
|
|
649
|
+
print(' -x, --pexhaust Exhaust new unsatisfiable cores [Primer]')
|
|
650
|
+
print(' -X, --oexhaust Exhaust new unsatisfiable cores [OptUx]')
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
#
|
|
654
|
+
#==============================================================================
|
|
655
|
+
if __name__ == '__main__':
|
|
656
|
+
# parse command-line options
|
|
657
|
+
to_enum, mode, padapt, oadapt, pdcalls, odcalls, pexhaust, oexhaust, \
|
|
658
|
+
pminz, ominz, nodisj, ptrim, otrim, search, psolver, osolver, \
|
|
659
|
+
ppuresat, opuresat, punsorted, ounsorted, verbose, weighted, \
|
|
660
|
+
files = parse_options()
|
|
661
|
+
|
|
662
|
+
if files:
|
|
663
|
+
# read CNF from file
|
|
664
|
+
assert re.search(r'cnf(\.(gz|bz2|lzma|xz|zst))?$', files[0]), 'Unknown input file extension'
|
|
665
|
+
formula = CNF(from_file=files[0])
|
|
666
|
+
|
|
667
|
+
# creating an object of Primer
|
|
668
|
+
with Bica(formula, negated=None, target=mode, psolver=psolver,
|
|
669
|
+
padapt=padapt, pdcalls=pdcalls, pexhaust=pexhaust,
|
|
670
|
+
pminz=pminz, ppuresat=ppuresat, psearch=search,
|
|
671
|
+
punsorted=punsorted, ptrim=ptrim, osolver=osolver,
|
|
672
|
+
oadapt=oadapt, odcalls=odcalls, oexhaust=oexhaust,
|
|
673
|
+
ominz=ominz, onodisj=nodisj, opuresat=opuresat,
|
|
674
|
+
ounsorted=ounsorted, otrim=otrim, weighted=weighted,
|
|
675
|
+
verbose=verbose) as bica:
|
|
676
|
+
|
|
677
|
+
for i, minf in enumerate(bica.enumerate()):
|
|
678
|
+
if verbose > 1:
|
|
679
|
+
minf = CNF(from_clauses=[bica.primes[i - 1] for i in minf])
|
|
680
|
+
minf.to_fp(sys.stdout, as_dnf=True if mode == 'dnf' else False)
|
|
681
|
+
|
|
682
|
+
print('o {0}'.format(bica.optux.cost))
|
|
683
|
+
|
|
684
|
+
# checking if we are done
|
|
685
|
+
if to_enum and i + 1 == to_enum:
|
|
686
|
+
break
|
|
687
|
+
|
|
688
|
+
# reporting the total oracle time
|
|
689
|
+
print('c primer time: {0:.4f}'.format(bica.primer.oracle_time()))
|
|
690
|
+
print('c optux time: {0:.4f}'.format(bica.optux.oracle_time()))
|
|
691
|
+
print('c total time: {0:.4f}'.format(bica.oracle_time()))
|