python-sat 1.8.dev25__cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.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.
Potentially problematic release.
This version of python-sat might be problematic. Click here for more details.
- pycard.cpython-314-x86_64-linux-gnu.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-314-x86_64-linux-gnu.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
|
Binary file
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
#!python
|
|
2
|
+
#-*- coding:utf-8 -*-
|
|
3
|
+
##
|
|
4
|
+
## approxmc.py
|
|
5
|
+
##
|
|
6
|
+
## Created on: Apr 14, 2023
|
|
7
|
+
## Author: Mate Soos
|
|
8
|
+
## E-mail: soos.mate@gmail.com
|
|
9
|
+
##
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
===============
|
|
13
|
+
List of classes
|
|
14
|
+
===============
|
|
15
|
+
|
|
16
|
+
.. autosummary::
|
|
17
|
+
:nosignatures:
|
|
18
|
+
|
|
19
|
+
Counter
|
|
20
|
+
|
|
21
|
+
==================
|
|
22
|
+
Module description
|
|
23
|
+
==================
|
|
24
|
+
|
|
25
|
+
This module provides interface to `ApproxMCv4
|
|
26
|
+
<https://github.com/meelgroup/approxmc/>`_, a state-of-the-art
|
|
27
|
+
*approximate* model counter utilising an improved version of CryptoMiniSat
|
|
28
|
+
to give approximate model counts to problems of size and complexity that
|
|
29
|
+
are out of reach for earlier approximate model counters. The original work
|
|
30
|
+
on ApproxMCv4 has been published in [1]_ and [2]_.
|
|
31
|
+
|
|
32
|
+
.. [1] Mate Soos, Kuldeep S. Meel. *BIRD: Engineering an Efficient CNF-XOR
|
|
33
|
+
SAT Solver and Its Applications to Approximate Model Counting*. AAAI
|
|
34
|
+
2019. pp. 1592-1599
|
|
35
|
+
|
|
36
|
+
.. [2] Mate Soos, Stephan Gocht, Kuldeep S. Meel. *Tinted, Detached, and
|
|
37
|
+
Lazy CNF-XOR Solving and Its Applications to Counting and Sampling*.
|
|
38
|
+
CAV 2020. pp. 463-484
|
|
39
|
+
|
|
40
|
+
Note that to be functional, the module requires package ``pyapproxmc`` to
|
|
41
|
+
be installed:
|
|
42
|
+
|
|
43
|
+
::
|
|
44
|
+
|
|
45
|
+
$ pip install pyapproxmc
|
|
46
|
+
|
|
47
|
+
The interface gives access to :class:`Counter`, which expects a formula in
|
|
48
|
+
:class:`.CNF` as input. Given a few additional (optional) arguments,
|
|
49
|
+
including a random seed, *tolerance factor* :math:`\\varepsilon`, and
|
|
50
|
+
*confidence* :math:`\\delta`, the class can be used to get an approximate
|
|
51
|
+
number of models of the formula, subject to the given tolerance factor and
|
|
52
|
+
confidence parameter.
|
|
53
|
+
|
|
54
|
+
Namely, given a CNF formula :math:`\\mathcal{F}` with
|
|
55
|
+
:math:`\\#\\mathcal{F}` as the exact number of models, and parameters
|
|
56
|
+
:math:`\\varepsilon\\in(0,1]` and :math:`\\delta\\in[0,1)`, the counter
|
|
57
|
+
computes and reports a value :math:`C`, which is an approximate number of
|
|
58
|
+
models of :math:`\\mathcal{F}`, such that
|
|
59
|
+
:math:`\\textrm{Pr}\\left[\\frac{1}{1+\\varepsilon}\\#\\mathcal{F}\\leq
|
|
60
|
+
C\\leq (1+\\varepsilon)\\#\\mathcal{F}\\right]\\geq 1-\\delta`.
|
|
61
|
+
|
|
62
|
+
The implementation can be used as an executable (the list of available
|
|
63
|
+
command-line options can be shown using ``approxmc.py -h``) in the
|
|
64
|
+
following way:
|
|
65
|
+
|
|
66
|
+
::
|
|
67
|
+
|
|
68
|
+
$ xzcat formula.cnf.xz
|
|
69
|
+
p cnf 20 2
|
|
70
|
+
1 2 3 0
|
|
71
|
+
3 20 0
|
|
72
|
+
|
|
73
|
+
$ approxmc.py -p 1,2,3-9 formula.cnf.xz
|
|
74
|
+
s mc 448
|
|
75
|
+
|
|
76
|
+
Alternatively, the algorithm can be accessed and invoked through the
|
|
77
|
+
standard ``import`` interface of Python, e.g.
|
|
78
|
+
|
|
79
|
+
.. code-block:: python
|
|
80
|
+
|
|
81
|
+
>>> from pysat.allies.approxmc import Counter
|
|
82
|
+
>>> from pysat.formula import CNF
|
|
83
|
+
>>>
|
|
84
|
+
>>> cnf = CNF(from_file='formula.cnf.xz')
|
|
85
|
+
>>>
|
|
86
|
+
>>> with Counter(cnf) as counter:
|
|
87
|
+
... print(counter.counter(projection=range(1, 10))
|
|
88
|
+
448
|
|
89
|
+
|
|
90
|
+
As can be seen in the above example, besides model counting across all the
|
|
91
|
+
variables in a given input formula, the counter supports *projected* model
|
|
92
|
+
counting, i.e. when one needs to approximate the number of models with
|
|
93
|
+
respect to a given list of variables rather than with respect to all
|
|
94
|
+
variables appearing in the formula. This feature comes in handy when the
|
|
95
|
+
formula is obtained, for example, through Tseitin transformation [3]_ with
|
|
96
|
+
a number of auxiliary variables introduced.
|
|
97
|
+
|
|
98
|
+
.. [3] G. S. Tseitin. *On the complexity of derivations in the
|
|
99
|
+
propositional calculus*. Studies in Mathematics and Mathematical
|
|
100
|
+
Logic, Part II. pp. 115–125, 1968
|
|
101
|
+
|
|
102
|
+
==============
|
|
103
|
+
Module details
|
|
104
|
+
==============
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
#
|
|
108
|
+
#==============================================================================
|
|
109
|
+
from __future__ import print_function
|
|
110
|
+
import getopt
|
|
111
|
+
import os
|
|
112
|
+
from pysat.formula import CNF
|
|
113
|
+
import re
|
|
114
|
+
import sys
|
|
115
|
+
|
|
116
|
+
# we need pyapproxmc to be installed:
|
|
117
|
+
pyapproxmc_present = True
|
|
118
|
+
try:
|
|
119
|
+
import pyapproxmc
|
|
120
|
+
except ImportError:
|
|
121
|
+
pyapproxmc_present = False
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
#
|
|
125
|
+
#==============================================================================
|
|
126
|
+
class Counter(object):
|
|
127
|
+
"""
|
|
128
|
+
A wrapper for `ApproxMC <https://github.com/meelgroup/approxmc/>`_, a
|
|
129
|
+
state-of-the-art *approximate* model counter. Given a formula in
|
|
130
|
+
:class:`.CNF`, this class can be used to get an approximate number of
|
|
131
|
+
models of the formula, subject to *tolerance factor* ``epsilon`` and
|
|
132
|
+
*confidence parameter* ``delta``.
|
|
133
|
+
|
|
134
|
+
Namely, given a CNF formula :math:`\\mathcal{F}` and parameters
|
|
135
|
+
:math:`\\varepsilon\\in(0,1]` and :math:`\\delta\\in[0,1)`, the
|
|
136
|
+
counter computes and reports a value :math:`C` such that
|
|
137
|
+
:math:`\\textrm{Pr}\\left[\\frac{1}{1+\\varepsilon}\\#\\mathcal{F}\\leq
|
|
138
|
+
C\\leq (1+\\varepsilon)\\#\\mathcal{F}\\right]\\geq 1-\\delta`. Here,
|
|
139
|
+
:math:`\\#\\mathcal{F}` denotes the exact model count for formula
|
|
140
|
+
:math:`\\mathcal{F}`.
|
|
141
|
+
|
|
142
|
+
The ``formula`` argument can be left unspecified at this stage. In
|
|
143
|
+
this case, a user is expected to add all the relevant clauses using
|
|
144
|
+
:meth:`add_clause`.
|
|
145
|
+
|
|
146
|
+
An additional parameter a user may want to specify is integer ``seed``
|
|
147
|
+
used by ApproxMC. The value of ``seed`` is set to ``1`` by default.
|
|
148
|
+
|
|
149
|
+
:param formula: CNF formula
|
|
150
|
+
:param seed: integer seed value
|
|
151
|
+
:param epsilon: tolerance factor
|
|
152
|
+
:param delta: confidence parameter
|
|
153
|
+
:param verbose: verbosity level
|
|
154
|
+
|
|
155
|
+
:type formula: :class:`.CNF`
|
|
156
|
+
:type seed: int
|
|
157
|
+
:type epsilon: float
|
|
158
|
+
:type delta: float
|
|
159
|
+
:type verbose: int
|
|
160
|
+
|
|
161
|
+
.. code-block:: python
|
|
162
|
+
|
|
163
|
+
>>> from pysat.allies.approxmc import Counter
|
|
164
|
+
>>> from pysat.formula import CNF
|
|
165
|
+
>>>
|
|
166
|
+
>>> cnf = CNF(from_file='some-formula.cnf')
|
|
167
|
+
>>> with Counter(formula=cnf, epsilon=0.1, delta=0.9) as counter:
|
|
168
|
+
... num = counter.count() # an approximate number of models
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
def __init__(self, formula=None, seed=1, epsilon=0.8, delta=0.2, verbose=0):
|
|
172
|
+
"""
|
|
173
|
+
Constructor.
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
assert pyapproxmc_present, 'Package \'pyapproxmc\' is unavailable. Check your installation.'
|
|
177
|
+
|
|
178
|
+
# there are no initial counts
|
|
179
|
+
self.cellc, self.hashc = None, None
|
|
180
|
+
|
|
181
|
+
# creating the Counter object
|
|
182
|
+
self.counter = pyapproxmc.Counter(verbosity=verbose, seed=seed,
|
|
183
|
+
epsilon=epsilon, delta=delta)
|
|
184
|
+
|
|
185
|
+
# adding clauses to the counter
|
|
186
|
+
if formula:
|
|
187
|
+
for clause in formula:
|
|
188
|
+
self.add_clause(clause)
|
|
189
|
+
|
|
190
|
+
def __del__(self):
|
|
191
|
+
"""
|
|
192
|
+
Destructor.
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
self.delete()
|
|
196
|
+
|
|
197
|
+
def __enter__(self):
|
|
198
|
+
"""
|
|
199
|
+
'with' constructor.
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
return self
|
|
203
|
+
|
|
204
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
205
|
+
"""
|
|
206
|
+
'with' destructor.
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
self.delete()
|
|
210
|
+
|
|
211
|
+
def add_clause(self, clause):
|
|
212
|
+
"""
|
|
213
|
+
The method for adding a clause to the problem formula. Although
|
|
214
|
+
the input formula can be specified as an argument of the
|
|
215
|
+
constructor of :class:`Counter`, adding clauses may also be
|
|
216
|
+
helpful afterwards, *on the fly*.
|
|
217
|
+
|
|
218
|
+
The clause to add can be any iterable over integer literals.
|
|
219
|
+
|
|
220
|
+
:param clause: a clause to add
|
|
221
|
+
:type clause: iterable(int)
|
|
222
|
+
|
|
223
|
+
.. code-block:: python
|
|
224
|
+
|
|
225
|
+
>>> from pysat.allies.approxmc import Counter
|
|
226
|
+
>>>
|
|
227
|
+
>>> with Counter() as counter:
|
|
228
|
+
... counter.add_clause(range(1, 4))
|
|
229
|
+
... counter.add_clause([3, 20])
|
|
230
|
+
...
|
|
231
|
+
... print(counter.count())
|
|
232
|
+
720896
|
|
233
|
+
"""
|
|
234
|
+
|
|
235
|
+
self.counter.add_clause(clause)
|
|
236
|
+
|
|
237
|
+
def count(self, projection=None):
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
Given the formula provided by the user either in the constructor
|
|
241
|
+
of :class:`Counter` or through a series of calls to
|
|
242
|
+
:meth:`add_clause`, this method runs the ApproxMC counter with the
|
|
243
|
+
specified values of tolerance :math:`\\varepsilon` and confidence
|
|
244
|
+
:math:`\\delta` parameters, as well as the random ``seed`` value,
|
|
245
|
+
and returns the number of models estimated.
|
|
246
|
+
|
|
247
|
+
A user may specify an argument ``projection``, which is a list of
|
|
248
|
+
integers specifying the variables with respect to which projected
|
|
249
|
+
model counting should be performed. If ``projection`` is left as
|
|
250
|
+
``None``, approximate model counting is performed wrt. all the
|
|
251
|
+
variables of the input formula.
|
|
252
|
+
|
|
253
|
+
:param projection: variables to project on
|
|
254
|
+
:type projection: list(int)
|
|
255
|
+
|
|
256
|
+
.. code-block:: python
|
|
257
|
+
|
|
258
|
+
>>> from pysat.allies.approxmc import Counter
|
|
259
|
+
>>> from pysat.card import CardEnc, EncType
|
|
260
|
+
>>>
|
|
261
|
+
>>> # cardinality constraint with auxiliary variables
|
|
262
|
+
>>> # there are exactly 70 models for the constraint
|
|
263
|
+
>>> # over the 8 original variables
|
|
264
|
+
>>> cnf = CardEnc.equals(lits=range(1, 9), bound=4, encoding=EncType.cardnetwrk)
|
|
265
|
+
>>>
|
|
266
|
+
>>> with Counter(formula=cnf, epsilon=0.05, delta=0.95) as counter:
|
|
267
|
+
... print(counter.count())
|
|
268
|
+
123840
|
|
269
|
+
>>>
|
|
270
|
+
>>> with Counter(formula=cnf, epsilon=0.05, delta=0.95) as counter:
|
|
271
|
+
... print(counter.count(projection=range(1, 8)))
|
|
272
|
+
70
|
|
273
|
+
"""
|
|
274
|
+
|
|
275
|
+
if projection is not None:
|
|
276
|
+
self.cellc, self.hashc = self.counter.count(projection=projection)
|
|
277
|
+
else:
|
|
278
|
+
self.cellc, self.hashc = self.counter.count()
|
|
279
|
+
|
|
280
|
+
return self.cellc * (2 ** self.hashc)
|
|
281
|
+
|
|
282
|
+
def delete(self):
|
|
283
|
+
"""
|
|
284
|
+
Explicit destructor of the internal Counter oracle.
|
|
285
|
+
Delete the actual counter object and sets it to ``None``.
|
|
286
|
+
"""
|
|
287
|
+
|
|
288
|
+
if self.counter:
|
|
289
|
+
del self.counter
|
|
290
|
+
self.counter = None
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
#
|
|
294
|
+
#==============================================================================
|
|
295
|
+
def parse_options():
|
|
296
|
+
"""
|
|
297
|
+
Parses command-line option
|
|
298
|
+
"""
|
|
299
|
+
|
|
300
|
+
try:
|
|
301
|
+
opts, args = getopt.getopt(sys.argv[1:], 'd:e:hp:s:v:',
|
|
302
|
+
['delta=', 'epsilon=', 'help', 'projection=', 'seed=', 'verbose='])
|
|
303
|
+
except getopt.GetoptError as err:
|
|
304
|
+
sys.stderr.write(str(err).capitalize())
|
|
305
|
+
usage()
|
|
306
|
+
sys.exit(1)
|
|
307
|
+
|
|
308
|
+
delta = 0.2
|
|
309
|
+
epsilon = 0.8
|
|
310
|
+
projection = None
|
|
311
|
+
seed = 1
|
|
312
|
+
verbose = 0
|
|
313
|
+
|
|
314
|
+
for opt, arg in opts:
|
|
315
|
+
if opt in ('-d', '--delta'):
|
|
316
|
+
delta = float(arg)
|
|
317
|
+
elif opt in ('-e', '--epsilon'):
|
|
318
|
+
epsilon = float(arg)
|
|
319
|
+
elif opt in ('-h', '--help'):
|
|
320
|
+
usage()
|
|
321
|
+
sys.exit(0)
|
|
322
|
+
elif opt in ('-p', '--projection'):
|
|
323
|
+
# parsing the list of variables
|
|
324
|
+
projection, values = [], str(arg).split(',')
|
|
325
|
+
|
|
326
|
+
# checking if there are intervals
|
|
327
|
+
for value in values:
|
|
328
|
+
if value.isnumeric():
|
|
329
|
+
projection.append(int(value))
|
|
330
|
+
elif '-' in value:
|
|
331
|
+
lb, ub = value.split('-')
|
|
332
|
+
assert int(lb) < int(ub)
|
|
333
|
+
projection.extend(list(range(int(lb), int(ub) + 1)))
|
|
334
|
+
|
|
335
|
+
# removing duplicates, if any
|
|
336
|
+
projection = sorted(set(projection))
|
|
337
|
+
elif opt in ('-s', '--seed'):
|
|
338
|
+
seed = int(arg)
|
|
339
|
+
elif opt in ('-v', '--verbose'):
|
|
340
|
+
verbose = int(arg)
|
|
341
|
+
else:
|
|
342
|
+
assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
|
|
343
|
+
|
|
344
|
+
return delta, epsilon, projection, seed, verbose, args
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
#
|
|
348
|
+
#==============================================================================
|
|
349
|
+
def usage():
|
|
350
|
+
"""
|
|
351
|
+
Prints usage message.
|
|
352
|
+
"""
|
|
353
|
+
|
|
354
|
+
print('Usage:', os.path.basename(sys.argv[0]), '[options] dimacs-file')
|
|
355
|
+
print('Options:')
|
|
356
|
+
print(' -d, --delta=<float> Confidence parameter as per PAC guarantees')
|
|
357
|
+
print(' Available values: [0, 1) (default = 0.2)')
|
|
358
|
+
print(' -e, --epsilon=<float> Tolerance factor as per PAC guarantees')
|
|
359
|
+
print(' Available values: (0 .. 1], all (default = 0.8)')
|
|
360
|
+
print(' -p, --projection=<list> Do model counting projected on this set of variables')
|
|
361
|
+
print(' Available values: comma-separated-list, none (default = none)')
|
|
362
|
+
print(' -s, --seed=<int> Random seed')
|
|
363
|
+
print(' Available values: [0 .. INT_MAX] (default = 1)')
|
|
364
|
+
print(' -v, --verbose=<int> Verbosity level')
|
|
365
|
+
print(' Available values: [0 .. 15] (default = 0)')
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
#
|
|
369
|
+
#==============================================================================
|
|
370
|
+
if __name__ == '__main__':
|
|
371
|
+
delta, epsilon, projection, seed, verbose, files = parse_options()
|
|
372
|
+
|
|
373
|
+
# parsing the input formula
|
|
374
|
+
if files and re.search(r'\.cnf(\.(gz|bz2|lzma|xz))?$', files[0]):
|
|
375
|
+
formula = CNF(from_file=files[0])
|
|
376
|
+
|
|
377
|
+
# creating the counter object
|
|
378
|
+
with Counter(formula, seed=seed, epsilon=epsilon, delta=delta,
|
|
379
|
+
verbose=verbose) as counter:
|
|
380
|
+
|
|
381
|
+
# approximate model counting
|
|
382
|
+
count = counter.count(projection=projection)
|
|
383
|
+
|
|
384
|
+
# printing the result
|
|
385
|
+
print('s mc', count)
|