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.
Files changed (48) hide show
  1. pycard.cpython-310-darwin.so +0 -0
  2. pysat/__init__.py +24 -0
  3. pysat/_fileio.py +209 -0
  4. pysat/_utils.py +58 -0
  5. pysat/allies/__init__.py +0 -0
  6. pysat/allies/approxmc.py +385 -0
  7. pysat/allies/unigen.py +435 -0
  8. pysat/card.py +802 -0
  9. pysat/engines.py +1302 -0
  10. pysat/examples/__init__.py +0 -0
  11. pysat/examples/bbscan.py +663 -0
  12. pysat/examples/bica.py +691 -0
  13. pysat/examples/fm.py +527 -0
  14. pysat/examples/genhard.py +516 -0
  15. pysat/examples/hitman.py +653 -0
  16. pysat/examples/lbx.py +638 -0
  17. pysat/examples/lsu.py +496 -0
  18. pysat/examples/mcsls.py +610 -0
  19. pysat/examples/models.py +189 -0
  20. pysat/examples/musx.py +344 -0
  21. pysat/examples/optux.py +710 -0
  22. pysat/examples/primer.py +620 -0
  23. pysat/examples/rc2.py +1858 -0
  24. pysat/examples/usage.py +63 -0
  25. pysat/formula.py +5619 -0
  26. pysat/pb.py +463 -0
  27. pysat/process.py +363 -0
  28. pysat/solvers.py +7591 -0
  29. pysolvers.cpython-310-darwin.so +0 -0
  30. python_sat-1.8.dev25.data/scripts/approxmc.py +385 -0
  31. python_sat-1.8.dev25.data/scripts/bbscan.py +663 -0
  32. python_sat-1.8.dev25.data/scripts/bica.py +691 -0
  33. python_sat-1.8.dev25.data/scripts/fm.py +527 -0
  34. python_sat-1.8.dev25.data/scripts/genhard.py +516 -0
  35. python_sat-1.8.dev25.data/scripts/lbx.py +638 -0
  36. python_sat-1.8.dev25.data/scripts/lsu.py +496 -0
  37. python_sat-1.8.dev25.data/scripts/mcsls.py +610 -0
  38. python_sat-1.8.dev25.data/scripts/models.py +189 -0
  39. python_sat-1.8.dev25.data/scripts/musx.py +344 -0
  40. python_sat-1.8.dev25.data/scripts/optux.py +710 -0
  41. python_sat-1.8.dev25.data/scripts/primer.py +620 -0
  42. python_sat-1.8.dev25.data/scripts/rc2.py +1858 -0
  43. python_sat-1.8.dev25.data/scripts/unigen.py +435 -0
  44. python_sat-1.8.dev25.dist-info/METADATA +45 -0
  45. python_sat-1.8.dev25.dist-info/RECORD +48 -0
  46. python_sat-1.8.dev25.dist-info/WHEEL +6 -0
  47. python_sat-1.8.dev25.dist-info/licenses/LICENSE.txt +21 -0
  48. python_sat-1.8.dev25.dist-info/top_level.txt +3 -0
Binary file
pysat/__init__.py ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env python
2
+ #-*- coding:utf-8 -*-
3
+ ##
4
+ ## __init__.py
5
+ ##
6
+ ## Created on: Mar 4, 2017
7
+ ## Author: Alexey S. Ignatiev
8
+ ## E-mail: aignatiev@ciencias.ulisboa.pt
9
+ ##
10
+
11
+ # current version
12
+ #==============================================================================
13
+ VERSION = (1, 8, 'dev', 25)
14
+
15
+
16
+ # PEP440 Format
17
+ #==============================================================================
18
+ __version__ = '%d.%d.%s%d' % VERSION if len(VERSION) == 4 else \
19
+ '%d.%d.%d' % VERSION
20
+
21
+
22
+ # all submodules
23
+ #==============================================================================
24
+ __all__ = ['card', 'engines', 'formula', 'pb', 'process', 'solvers']
pysat/_fileio.py ADDED
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env python
2
+ #-*- coding:utf-8 -*-
3
+ ##
4
+ ## _fileio.py
5
+ ##
6
+ ## Created on: Aug 18, 2018
7
+ ## Author: Alexey S. Ignatiev
8
+ ## E-mail: aignatiev@ciencias.ulisboa.pt
9
+ ##
10
+
11
+ """
12
+ ===============
13
+ List of classes
14
+ ===============
15
+
16
+ .. autosummary::
17
+ :nosignatures:
18
+
19
+ FileObject
20
+
21
+ ==================
22
+ Module description
23
+ ==================
24
+
25
+ This simple module provides a basic interface to input/output operations
26
+ on files. Its key design feature is the ability to work with both
27
+ uncompressed and compressed files through a unified interface, thus,
28
+ making it easier for a user to deal with various types of compressed
29
+ files. The compression types supported include gzip, bzip2, lzma (xz), and
30
+ zstandard (zstd).
31
+
32
+ The module is supposed to be mainly used by :mod:`pysat.formula`.
33
+
34
+ A simple usage example is the following:
35
+
36
+ .. code-block:: python
37
+
38
+ >>> from pysat._fileio import FileObject
39
+ >>>
40
+ >>> with FileObject(name='formula.cnf', mode='r') as fp1:
41
+ ... contents1 = fp1.readlines()
42
+ >>>
43
+ >>> with FileObject(name='filename.txt.gz', compression='use_ext') as fp2:
44
+ ... contents2 = fp2.readlines()
45
+ >>>
46
+ >>> with FileObject(name='f.txt.bz2', mode='w', compression='bzip2') as fp3:
47
+ ... fp3.write('hello, world!\n')
48
+
49
+ ==============
50
+ Module details
51
+ ==============
52
+ """
53
+
54
+ #
55
+ #==============================================================================
56
+ import bz2
57
+ import codecs
58
+ import gzip
59
+ import os
60
+
61
+ lzma_present = True
62
+ try: # for Python3
63
+ import lzma
64
+ except ImportError:
65
+ try: # for Python2 + backports.lzma installed
66
+ from backports import lzma
67
+ except ImportError: # for Python2 without lzma
68
+ lzma_present = False
69
+
70
+ zstd_present = True
71
+ try:
72
+ from compression import zstd
73
+ except ImportError: # zstandard is introduced in Python 3.14
74
+ zstd_present = False
75
+
76
+
77
+
78
+ #
79
+ #==============================================================================
80
+ class FileObject(object):
81
+ """
82
+ Auxiliary class for convenient and uniform file manipulation, e.g. to
83
+ open files creating standard file pointers and closing them. The class
84
+ is used when opening DIMACS files for reading and writing. Supports
85
+ both uncompressed and compressed files. Compression algorithms
86
+ supported are ``gzip``, ``bzip2``, ``lzma``, and ``zstd``. Algorithm
87
+ ``lzma`` can be used in Python 3 by default and also in Python 2 if
88
+ the ``backports.lzma`` package is installed. Algorithm ``zstandard``
89
+ can be used in Python 3.14 and later.
90
+
91
+ Note that the class opens a file in text mode.
92
+
93
+ :param name: a file name to open
94
+ :param mode: opening mode
95
+ :param compression: compression type
96
+
97
+ :type name: str
98
+ :type mode: str
99
+ :type compression: str
100
+
101
+ Compression type can be ``None``, ``'gzip'``, ``'bzip2'``, ``'lzma'``,
102
+ ``'zstd'``, as well as ``'use_ext'``. If ``'use_ext'`` is specified,
103
+ compression algorithm is defined by the extension of the given file
104
+ name.
105
+ """
106
+
107
+ def __init__(self, name, mode='r', compression=None):
108
+ """
109
+ Constructor.
110
+ """
111
+
112
+ self.fp = None # file pointer to give access to
113
+ self.ctype = None # compression type
114
+
115
+ # in some cases an additional file pointer is needed
116
+ self.fp_extra = None
117
+
118
+ self.open(name, mode=mode, compression=compression)
119
+
120
+ def open(self, name, mode='r', compression=None):
121
+ """
122
+ Open a file pointer. Note that a file is *always* opened in
123
+ text mode. The method inherits its input parameters from the
124
+ constructor of :class:`FileObject`.
125
+ """
126
+
127
+ if compression == 'use_ext':
128
+ self.get_compression_type(name)
129
+ else:
130
+ self.ctype = compression
131
+
132
+ if not self.ctype:
133
+ self.fp = open(name, mode)
134
+ elif self.ctype == 'gzip':
135
+ self.fp = gzip.open(name, mode + 't')
136
+ elif self.ctype == 'bzip2':
137
+ try:
138
+ # Python 3 supports opening bzip2 files in text mode
139
+ # therefore, we prefer to open them this way
140
+ self.fp = bz2.open(name, mode + 't')
141
+ except:
142
+ # BZ2File opens a file in binary mode
143
+ # thus, we have to use codecs.getreader()
144
+ # to be able to use it in text mode
145
+ self.fp_extra = bz2.BZ2File(name, mode)
146
+
147
+ if mode == 'r':
148
+ self.fp = codecs.getreader('ascii')(self.fp_extra)
149
+ else: # mode == 'w'
150
+ self.fp = codecs.getwriter('ascii')(self.fp_extra)
151
+ elif self.ctype == 'lzma':
152
+ # LZMA is available in Python 2 only if backports.lzma is installed
153
+ # Python 3 supports it by default
154
+ assert lzma_present, 'LZMA compression is unavailable.'
155
+ self.fp = lzma.open(name, mode=mode + 't')
156
+ else: # self.ctype == 'zstd'
157
+ # Zstandard is available only in Python 3.14 and later
158
+ assert zstd_present, 'Zstandard compression is unavailable.'
159
+ self.fp = zstd.open(name, mode=mode + 't')
160
+
161
+ def close(self):
162
+ """
163
+ Close a file pointer.
164
+ """
165
+
166
+ if self.fp:
167
+ self.fp.close()
168
+ self.fp = None
169
+
170
+ if self.fp_extra:
171
+ self.fp_extra.close()
172
+ self.fp_extra = None
173
+
174
+ self.ctype = None
175
+
176
+ def get_compression_type(self, file_name):
177
+ """
178
+ Determine compression type for a given file using its extension.
179
+
180
+ :param file_name: a given file name
181
+ :type file_name: str
182
+ """
183
+
184
+ ext = os.path.splitext(file_name)[1]
185
+
186
+ if ext == '.gz':
187
+ self.ctype = 'gzip'
188
+ elif ext == '.bz2':
189
+ self.ctype = 'bzip2'
190
+ elif ext in ('.xz', '.lzma'):
191
+ self.ctype = 'lzma'
192
+ elif ext == '.zst':
193
+ self.ctype = 'zstd'
194
+ else:
195
+ self.ctype = None
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.close()
pysat/_utils.py ADDED
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env python
2
+ #-*- coding:utf-8 -*-
3
+ ##
4
+ ## _utils.py
5
+ ##
6
+ ## Created on: Nov 27, 2019
7
+ ## Author: Alexey S. Ignatiev
8
+ ## E-mail: aignatiev@ciencias.ulisboa.pt
9
+ ##
10
+
11
+ """
12
+ ===============
13
+ List of classes
14
+ ===============
15
+
16
+ .. autosummary::
17
+ :nosignatures:
18
+
19
+ MainThread
20
+
21
+ ==================
22
+ Module description
23
+ ==================
24
+
25
+ This simple module is supposed to implement auxiliary classes and routines
26
+ that are used by the other (main) modules of PySAT.
27
+
28
+ ==============
29
+ Module details
30
+ ==============
31
+ """
32
+
33
+ #
34
+ #==============================================================================
35
+ import threading
36
+
37
+
38
+ #
39
+ #==============================================================================
40
+ class MainThread(object):
41
+ """
42
+ A dummy class for checking whether the current thread is the main one.
43
+ This is currently necessary for proper signal handling when making
44
+ oracle calls and creating cardinality encodings.
45
+ """
46
+
47
+ @staticmethod
48
+ def check():
49
+ """
50
+ The actual checker.
51
+ """
52
+
53
+ try: # for Python > 3.4
54
+ res = threading.current_thread() is threading.main_thread()
55
+ except AttributeError:
56
+ res = isinstance(threading.current_thread(), threading._MainThread)
57
+
58
+ return res
File without changes
@@ -0,0 +1,385 @@
1
+ #!/usr/bin/env 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)