python-sat 0.1.8.dev10__cp310-cp310-win_amd64.whl → 1.8.dev26__cp310-cp310-win_amd64.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.

Files changed (45) hide show
  1. pycard.cp310-win_amd64.pyd +0 -0
  2. pysat/__init__.py +4 -4
  3. pysat/_fileio.py +30 -14
  4. pysat/allies/approxmc.py +22 -22
  5. pysat/allies/unigen.py +435 -0
  6. pysat/card.py +13 -12
  7. pysat/engines.py +1302 -0
  8. pysat/examples/bbscan.py +663 -0
  9. pysat/examples/bica.py +691 -0
  10. pysat/examples/fm.py +12 -8
  11. pysat/examples/genhard.py +24 -23
  12. pysat/examples/hitman.py +53 -37
  13. pysat/examples/lbx.py +56 -15
  14. pysat/examples/lsu.py +28 -14
  15. pysat/examples/mcsls.py +53 -15
  16. pysat/examples/models.py +6 -4
  17. pysat/examples/musx.py +15 -7
  18. pysat/examples/optux.py +71 -32
  19. pysat/examples/primer.py +620 -0
  20. pysat/examples/rc2.py +268 -69
  21. pysat/formula.py +3241 -229
  22. pysat/pb.py +85 -37
  23. pysat/process.py +16 -2
  24. pysat/solvers.py +2119 -724
  25. pysolvers.cp310-win_amd64.pyd +0 -0
  26. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/approxmc.py +22 -22
  27. python_sat-1.8.dev26.data/scripts/bbscan.py +663 -0
  28. python_sat-1.8.dev26.data/scripts/bica.py +691 -0
  29. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/fm.py +12 -8
  30. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/genhard.py +24 -23
  31. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/lbx.py +56 -15
  32. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/lsu.py +28 -14
  33. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/mcsls.py +53 -15
  34. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/models.py +6 -4
  35. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/musx.py +15 -7
  36. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/optux.py +71 -32
  37. python_sat-1.8.dev26.data/scripts/primer.py +620 -0
  38. {python_sat-0.1.8.dev10.data → python_sat-1.8.dev26.data}/scripts/rc2.py +268 -69
  39. python_sat-1.8.dev26.data/scripts/unigen.py +435 -0
  40. {python_sat-0.1.8.dev10.dist-info → python_sat-1.8.dev26.dist-info}/METADATA +19 -5
  41. python_sat-1.8.dev26.dist-info/RECORD +48 -0
  42. {python_sat-0.1.8.dev10.dist-info → python_sat-1.8.dev26.dist-info}/WHEEL +1 -1
  43. python_sat-0.1.8.dev10.dist-info/RECORD +0 -39
  44. {python_sat-0.1.8.dev10.dist-info → python_sat-1.8.dev26.dist-info/licenses}/LICENSE.txt +0 -0
  45. {python_sat-0.1.8.dev10.dist-info → python_sat-1.8.dev26.dist-info}/top_level.txt +0 -0
Binary file
pysat/__init__.py CHANGED
@@ -10,15 +10,15 @@
10
10
 
11
11
  # current version
12
12
  #==============================================================================
13
- VERSION = (0, 1, 8, "dev", 10)
13
+ VERSION = (1, 8, 'dev', 26)
14
14
 
15
15
 
16
16
  # PEP440 Format
17
17
  #==============================================================================
18
- __version__ = "%d.%d.%d.%s%d" % VERSION if len(VERSION) == 5 else \
19
- "%d.%d.%d" % VERSION
18
+ __version__ = '%d.%d.%s%d' % VERSION if len(VERSION) == 4 else \
19
+ '%d.%d.%d' % VERSION
20
20
 
21
21
 
22
22
  # all submodules
23
23
  #==============================================================================
24
- __all__ = ['card', 'formula', 'pb', 'solvers']
24
+ __all__ = ['card', 'engines', 'formula', 'pb', 'process', 'solvers']
pysat/_fileio.py CHANGED
@@ -22,11 +22,12 @@
22
22
  Module description
23
23
  ==================
24
24
 
25
- This simple module provides a basic interface to input/output operations on
26
- files. Its key design feature is the ability to work with both uncompressed
27
- and compressed files through a unified interface, thus, making it easier
28
- for a user to deal with various types of compressed files. The compression
29
- types supported include gzip, bzip2, and lzma (xz).
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).
30
31
 
31
32
  The module is supposed to be mainly used by :mod:`pysat.formula`.
32
33
 
@@ -66,6 +67,13 @@ except ImportError:
66
67
  except ImportError: # for Python2 without lzma
67
68
  lzma_present = False
68
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
+
69
77
 
70
78
  #
71
79
  #==============================================================================
@@ -75,9 +83,10 @@ class FileObject(object):
75
83
  open files creating standard file pointers and closing them. The class
76
84
  is used when opening DIMACS files for reading and writing. Supports
77
85
  both uncompressed and compressed files. Compression algorithms
78
- supported are ``gzip``, ``bzip2``, and ``lzma``. Algorithm ``lzma`` can
79
- be used in Python 3 by default and also in Python 2 if the
80
- ``backports.lzma`` package is installed.
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.
81
90
 
82
91
  Note that the class opens a file in text mode.
83
92
 
@@ -90,8 +99,9 @@ class FileObject(object):
90
99
  :type compression: str
91
100
 
92
101
  Compression type can be ``None``, ``'gzip'``, ``'bzip2'``, ``'lzma'``,
93
- as well as ``'use_ext'``. If ``'use_ext'`` is specified, compression
94
- algorithm is defined by the extension of the given file name.
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.
95
105
  """
96
106
 
97
107
  def __init__(self, name, mode='r', compression=None):
@@ -109,9 +119,9 @@ class FileObject(object):
109
119
 
110
120
  def open(self, name, mode='r', compression=None):
111
121
  """
112
- Open a file pointer. Note that a file is *always* opened in text
113
- mode. The method inherits its input parameters from the constructor
114
- of :class:`FileObject`.
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`.
115
125
  """
116
126
 
117
127
  if compression == 'use_ext':
@@ -138,11 +148,15 @@ class FileObject(object):
138
148
  self.fp = codecs.getreader('ascii')(self.fp_extra)
139
149
  else: # mode == 'w'
140
150
  self.fp = codecs.getwriter('ascii')(self.fp_extra)
141
- else: # self.ctype == 'lzma'
151
+ elif self.ctype == 'lzma':
142
152
  # LZMA is available in Python 2 only if backports.lzma is installed
143
153
  # Python 3 supports it by default
144
154
  assert lzma_present, 'LZMA compression is unavailable.'
145
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')
146
160
 
147
161
  def close(self):
148
162
  """
@@ -175,6 +189,8 @@ class FileObject(object):
175
189
  self.ctype = 'bzip2'
176
190
  elif ext in ('.xz', '.lzma'):
177
191
  self.ctype = 'lzma'
192
+ elif ext == '.zst':
193
+ self.ctype = 'zstd'
178
194
  else:
179
195
  self.ctype = None
180
196
 
pysat/allies/approxmc.py CHANGED
@@ -22,12 +22,12 @@
22
22
  Module description
23
23
  ==================
24
24
 
25
- This module provides interface `ApproxMCv4
25
+ This module provides interface to `ApproxMCv4
26
26
  <https://github.com/meelgroup/approxmc/>`_, a state-of-the-art
27
27
  *approximate* model counter utilising an improved version of CryptoMiniSat
28
- give approximate model counts to problems of size and complexity that are
29
- out of reach for earlier approximate model counters. The original work on
30
- ApproxMCv4 has been published in [1]_ and [2]_.
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
31
 
32
32
  .. [1] Mate Soos, Kuldeep S. Meel. *BIRD: Engineering an Efficient CNF-XOR
33
33
  SAT Solver and Its Applications to Approximate Model Counting*. AAAI
@@ -47,17 +47,17 @@
47
47
  The interface gives access to :class:`Counter`, which expects a formula in
48
48
  :class:`.CNF` as input. Given a few additional (optional) arguments,
49
49
  including a random seed, *tolerance factor* :math:`\\varepsilon`, and
50
- *confidence* :math:`\delta`, the class can be used to get an approximate
50
+ *confidence* :math:`\\delta`, the class can be used to get an approximate
51
51
  number of models of the formula, subject to the given tolerance factor and
52
52
  confidence parameter.
53
53
 
54
- Namely, given a CNF formula :math:`\mathcal{F}` with :math:`\#\mathcal{F}`
55
- as the exact number of models, and parameters :math:`\\varepsilon\in(0,1]`
56
- and :math:`\delta\in[0,1)`, the counter computes and reports a value
57
- :math:`C`, which is an approximate number of models of
58
- :math:`\mathcal{F}`, such that
59
- :math:`\\textrm{Pr}\left[\\frac{1}{1+\\varepsilon}\#\mathcal{F}\leq C\leq
60
- (1+\\varepsilon)\#\mathcal{F}\\right]\geq 1-\delta`.
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
61
 
62
62
  The implementation can be used as an executable (the list of available
63
63
  command-line options can be shown using ``approxmc.py -h``) in the
@@ -131,13 +131,13 @@ class Counter(object):
131
131
  models of the formula, subject to *tolerance factor* ``epsilon`` and
132
132
  *confidence parameter* ``delta``.
133
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 counter
136
- 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}`.
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
141
 
142
142
  The ``formula`` argument can be left unspecified at this stage. In
143
143
  this case, a user is expected to add all the relevant clauses using
@@ -147,7 +147,7 @@ class Counter(object):
147
147
  used by ApproxMC. The value of ``seed`` is set to ``1`` by default.
148
148
 
149
149
  :param formula: CNF formula
150
- :param seed: do core trimming at most this number of times
150
+ :param seed: integer seed value
151
151
  :param epsilon: tolerance factor
152
152
  :param delta: confidence parameter
153
153
  :param verbose: verbosity level
@@ -241,7 +241,7 @@ class Counter(object):
241
241
  of :class:`Counter` or through a series of calls to
242
242
  :meth:`add_clause`, this method runs the ApproxMC counter with the
243
243
  specified values of tolerance :math:`\\varepsilon` and confidence
244
- :math:`\delta` parameters, as well as the random ``seed`` value,
244
+ :math:`\\delta` parameters, as well as the random ``seed`` value,
245
245
  and returns the number of models estimated.
246
246
 
247
247
  A user may specify an argument ``projection``, which is a list of
@@ -371,7 +371,7 @@ if __name__ == '__main__':
371
371
  delta, epsilon, projection, seed, verbose, files = parse_options()
372
372
 
373
373
  # parsing the input formula
374
- if files and re.search('\.cnf(\.(gz|bz2|lzma|xz))?$', files[0]):
374
+ if files and re.search(r'\.cnf(\.(gz|bz2|lzma|xz))?$', files[0]):
375
375
  formula = CNF(from_file=files[0])
376
376
 
377
377
  # creating the counter object
pysat/allies/unigen.py ADDED
@@ -0,0 +1,435 @@
1
+ #!/usr/bin/env python
2
+ #-*- coding:utf-8 -*-
3
+ ##
4
+ ## unigen.py
5
+ ##
6
+ ## Created on: Oct 16, 2023
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
+ Sampler
20
+
21
+ ==================
22
+ Module description
23
+ ==================
24
+
25
+ This module provides interface to `UniGen3
26
+ <https://github.com/meelgroup/unigen/>`_, a state-of-the-art
27
+ almost-uniform sampler utilising an improved version of CryptoMiniSat to
28
+ handle problems of size and complexity that were not possible before. .
29
+ The original work on UniGen3 has been published in [1]_, [2]_, and [3]_.
30
+
31
+ .. [1] Supratik Chakraborty, Kuldeep S. Meel, Moshe Y. Vardi. *Balancing
32
+ Scalability and Uniformity in SAT Witness Generator*. DAC 2014.
33
+ pp. 60:1-60:6
34
+
35
+ .. [2] Supratik Chakraborty, Daniel J. Fremont, Kuldeep S. Meel, Sanjit A.
36
+ Seshia, Moshe Y. Vardi. *On Parallel Scalable Uniform SAT Witness
37
+ Generation*. TACAS 2015. pp. 304-319
38
+
39
+ .. [3] Mate Soos, Stephan Gocht, Kuldeep S. Meel. *Tinted, Detached, and
40
+ Lazy CNF-XOR Solving and Its Applications to Counting and Sampling*.
41
+ CAV 2020. pp. 463-484
42
+
43
+ Note that to be functional, the module requires package ``pyunigen`` to be
44
+ installed:
45
+
46
+ ::
47
+
48
+ $ pip install pyunigen
49
+
50
+ The interface gives access to :class:`Sampler`, which expects a formula in
51
+ :class:`.CNF` as input. Given a few additional (optional) arguments,
52
+ including a random seed, *tolerance factor* :math:`\\varepsilon`,
53
+ *confidence* :math:`\\delta` (to be used by ApproxMC), and *uniformity
54
+ parameter* :math:`\\kappa`, the class can be used to get apply
55
+ almost-uniform sampling and to obtain a requested number of samples as a
56
+ result, subject to the given tolerance factor and confidence parameter.
57
+
58
+ Namely, given a CNF formula :math:`\\mathcal{F}` with the set of
59
+ satisfying assignments (or models) denoted by :math:`sol(\\mathcal{F})`
60
+ and parameter :math:`\\varepsilon\\in(0,1]`, a uniform sampler outputs a
61
+ model :math:`y\\in sol(\\mathcal{F})` such that
62
+ :math:`\\textrm{Pr}\\left[y \\textrm{ is
63
+ output}\\right]=\\frac{1}{|sol(\\mathcal{F})|}`. Almost-uniform sampling
64
+ relaxes the uniformity guarantee and ensures that
65
+ :math:`\\frac{1}{(1+\\varepsilon)|sol(\\mathcal{F})|} \\leq
66
+ \\textrm{Pr}\\left[y \\textrm{ is output}\\right] \\leq
67
+ \\frac{1+\\varepsilon}{|sol(\\mathcal{F})|}`.
68
+
69
+ The implementation can be used as an executable (the list of available
70
+ command-line options can be shown using ``unigen.py -h``) in the
71
+ following way:
72
+
73
+ ::
74
+
75
+ $ xzcat formula.cnf.xz
76
+ p cnf 6 2
77
+ 1 5 0
78
+ 1 6 0
79
+
80
+ $ unigen.py -n 4 formula.cnf.xz
81
+ v +1 -2 +3 -4 -5 -6 0
82
+ v +1 +2 +3 -4 +5 +6 0
83
+ v +1 -2 -3 -4 +5 -6 0
84
+ v -1 -2 -3 -4 +5 +6 0
85
+
86
+ Alternatively, the algorithm can be accessed and invoked through the
87
+ standard ``import`` interface of Python, e.g.
88
+
89
+ .. code-block:: python
90
+
91
+ >>> from pysat.allies.unigen import Sampler
92
+ >>> from pysat.formula import CNF
93
+ >>>
94
+ >>> cnf = CNF(from_file='formula.cnf.xz')
95
+ >>>
96
+ >>> with Sampler(cnf) as sampler:
97
+ ... print(sampler.sample(nof_samples=4, sample_over=[1, 2, 3])
98
+ [[1, 2, 3, 4, 5], [1, -2, -3, -4, -5], [1, -2, -3, -4, 5], [1, 2, -3, 4, 5]]
99
+
100
+ As can be seen in the above example, sampling can be done over a
101
+ user-defined set of variables (rather than the complete set of variables).
102
+
103
+ ==============
104
+ Module details
105
+ ==============
106
+ """
107
+
108
+ #
109
+ #==============================================================================
110
+ from __future__ import print_function
111
+ import getopt
112
+ import os
113
+ from pysat.formula import CNF
114
+ import re
115
+ import sys
116
+
117
+ # we need pyunigen to be installed:
118
+ pyunigen_present = True
119
+ try:
120
+ import pyunigen
121
+ except ImportError:
122
+ pyunigen_present = False
123
+
124
+
125
+ #
126
+ #==============================================================================
127
+ class Sampler(object):
128
+ """
129
+ A wrapper for UniGen3, a state-of-the-art almost-uniform sampler.
130
+ Given a formula in :class:`.CNF`, this class can be used to apply
131
+ almost-uniform sampling of the formula's models, subject to a few
132
+ input parameters.
133
+
134
+ The class initialiser receives a number of input arguments. The
135
+ ``formula`` argument can be left unspecified at this stage. In this
136
+ case, a user is expected to add all the relevant clauses using
137
+ :meth:`add_clause`.
138
+
139
+ Additional parameters a user may want to specify include integer
140
+ ``seed`` (used by ApproxMC), tolerance factor ``epsilon`` (used in the
141
+ probabilistic guarantees of almost-uniformity), confidence parameter
142
+ ``delta`` (used by ApproxMC), and uniformity parameter ``kappa`` (see
143
+ [2]_).
144
+
145
+ :param formula: CNF formula
146
+ :param seed: seed value
147
+ :param epsilon: tolerance factor
148
+ :param delta: confidence parameter (used by ApproxMC)
149
+ :param kappa: uniformity parameter
150
+ :param verbose: verbosity level
151
+
152
+ :type formula: :class:`.CNF`
153
+ :type seed: int
154
+ :type epsilon: float
155
+ :type delta: float
156
+ :type kappa: float
157
+ :type verbose: int
158
+
159
+ .. code-block:: python
160
+
161
+ >>> from pysat.allies.unigen import Sampler
162
+ >>> from pysat.formula import CNF
163
+ >>>
164
+ >>> cnf = CNF(from_file='some-formula.cnf')
165
+ >>> with Sampler(formula=cnf, epsilon=0.1, delta=0.9) as sampler:
166
+ ... for model in sampler.sample(nof_samples=100):
167
+ ... print(model) # printing 100 result samples
168
+ """
169
+
170
+ def __init__(self, formula=None, seed=1, epsilon=0.8, delta=0.2,
171
+ kappa=0.638, verbose=0):
172
+ """
173
+ Constructor.
174
+ """
175
+
176
+ assert pyunigen_present, 'Package \'pyunigen\' is unavailable. Check your installation.'
177
+
178
+ # there are no initial values
179
+ self.cellc, self.hashc, self.samples = None, None, []
180
+
181
+ # creating the Sampler object
182
+ self.sampler = pyunigen.Sampler(verbosity=verbose, seed=seed,
183
+ delta=delta, epsilon=epsilon,
184
+ kappa=kappa)
185
+
186
+ # adding clauses to the sampler
187
+ if formula:
188
+ for clause in formula:
189
+ self.add_clause(clause)
190
+
191
+ def __del__(self):
192
+ """
193
+ Destructor.
194
+ """
195
+
196
+ self.delete()
197
+
198
+ def __enter__(self):
199
+ """
200
+ 'with' constructor.
201
+ """
202
+
203
+ return self
204
+
205
+ def __exit__(self, exc_type, exc_value, traceback):
206
+ """
207
+ 'with' destructor.
208
+ """
209
+
210
+ self.delete()
211
+
212
+ def add_clause(self, clause):
213
+ """
214
+ The method for adding a clause to the problem formula. Although
215
+ the input formula can be specified as an argument of the
216
+ constructor of :class:`Sampler`, adding clauses may also be
217
+ helpful afterwards, *on the fly*.
218
+
219
+ The clause to add can be any iterable over integer literals.
220
+
221
+ :param clause: a clause to add
222
+ :type clause: iterable(int)
223
+
224
+ .. code-block:: python
225
+
226
+ >>> from pysat.allies.unigen import Sampler
227
+ >>>
228
+ >>> with Sampler() as sampler:
229
+ ... sampler.add_clause(range(1, 4))
230
+ ... sampler.add_clause([3, 4])
231
+ ...
232
+ ... print(sampler.sample(nof_samples=4))
233
+ [[1, 2, -3, 4], [-1, 2, -3, 4], [1, 2, 3, -4], [-1, 2, 3, 4]]
234
+ """
235
+
236
+ self.sampler.add_clause(clause)
237
+
238
+ def sample(self, nof_samples, sample_over=None, counts=None):
239
+ """
240
+ Given the formula provided by the user either in the constructor
241
+ of :class:`Sampler` or through a series of calls to
242
+ :meth:`add_clause`, this method runs the UniGen3 sampler with the
243
+ specified values of tolerance :math:`\\varepsilon`, confidence
244
+ :math:`\\delta` parameters, and uniformity parameter :math:`kappa`
245
+ as well as the random ``seed`` value, and outputs a requested
246
+ number of samples.
247
+
248
+ A user may specify an argument ``sample_over``, which is a list of
249
+ integers specifying the variables with respect to which sampling
250
+ should be performed. If ``sample_over`` is left as ``None``,
251
+ almost-uniform sampling is done wrt. all the variables of the
252
+ input formula.
253
+
254
+ Finally, argument ``counts`` can be specified as a pair of integer
255
+ values: *cell count* and *hash count* (in this order) used during
256
+ sampling. If left undefined (``None``), the values are determined
257
+ by ApproxMC.
258
+
259
+ :param nof_samples: number of samples to output
260
+ :param sample_over: variables to sample over
261
+ :param counts: cell count and hash count values
262
+
263
+ :type nof_samples: int
264
+ :type sample_over: list(int)
265
+ :type counts: [int, int]
266
+
267
+ :return: a list of samples
268
+
269
+ .. code-block:: python
270
+
271
+ >>> from pysat.allies.unigen import Sampler
272
+ >>> from pysat.card import CardEnc, EncType
273
+ >>>
274
+ >>> # cardinality constraint with auxiliary variables
275
+ >>> # there are exactly 6 models for the constraint
276
+ >>> # over the 6 original variables
277
+ >>> cnf = CardEnc.equals(lits=range(1, 5), bound=2, encoding=EncType.totalizer)
278
+ >>>
279
+ >>> with Sampler(formula=cnf, epsilon=0.05, delta=0.95) as sampler:
280
+ ... for model in sampler.sample(nof_samples=3):
281
+ ... print(model)
282
+ [1, -2, 3, -4, 5, 6, -7, -8, 9, -10, 11, -12, 13, 14, -15, 16, 17, -18, 19, -20]
283
+ [1, -2, -3, 4, 5, 6, -7, -8, 9, -10, 11, -12, 13, 14, -15, 16, 17, -18, 19, -20]
284
+ [1, 2, -3, -4, 5, 6, -7, 8, -9, -10, 11, 12, 13, 14, -15, 16, 17, 18, -19, -20]
285
+ >>>
286
+ >>> # now, sampling over the original variables
287
+ >>> with Sampler(formula=cnf, epsilon=0.05, delta=0.95) as sampler:
288
+ ... for model in sampler.sample(nof_samples=3, sample_over=range(1, 5)):
289
+ ... print(model)
290
+ [1, 2, -3, -4]
291
+ [1, -2, 3, -4]
292
+ [-1, 2, 3, -4]
293
+ """
294
+
295
+ # we cannot pass None as arguments, hence these if-elif branches
296
+ if sample_over is None and counts is None:
297
+ self.cellc, self.hashc, self.samples = self.sampler.sample(num=nof_samples)
298
+ elif counts is None:
299
+ self.cellc, self.hashc, self.samples = self.sampler.sample(num=nof_samples,
300
+ sampling_set=sample_over)
301
+ elif sample_over is None:
302
+ self.cellc, self.hashc, self.samples = self.sampler.sample(num=nof_samples,
303
+ cell_hash_count=tuple(counts))
304
+ else:
305
+ self.cellc, self.hashc, self.samples = self.sampler.sample(num=nof_samples,
306
+ sampling_set=sample_over,
307
+ cell_hash_count=tuple(counts))
308
+
309
+ return self.samples
310
+
311
+ def delete(self):
312
+ """
313
+ Explicit destructor of the internal Sampler oracle.
314
+ Delete the actual sampler object and sets it to ``None``.
315
+ """
316
+
317
+ if self.sampler:
318
+ del self.sampler
319
+ self.sampler = None
320
+
321
+
322
+ #
323
+ #==============================================================================
324
+ def parse_options():
325
+ """
326
+ Parses command-line option
327
+ """
328
+
329
+ try:
330
+ opts, args = getopt.getopt(sys.argv[1:], 'c:d:e:hk:n:S:s:v:',
331
+ ['counts=', 'delta=', 'epsilon=', 'help', 'kappa=',
332
+ 'nof-samples=' 'sample-over=', 'seed=', 'verbose='])
333
+ except getopt.GetoptError as err:
334
+ sys.stderr.write(str(err).capitalize())
335
+ usage()
336
+ sys.exit(1)
337
+
338
+ counts = None
339
+ delta = 0.2
340
+ epsilon = 0.8
341
+ kappa = 0.638
342
+ nof_samples = 4
343
+ sample_over = None
344
+ seed = 1
345
+ verbose = 0
346
+
347
+ for opt, arg in opts:
348
+ if opt in ('-c', '--counts'):
349
+ counts = tuple([int(v) for v in str(arg).split(',')])
350
+ elif opt in ('-d', '--delta'):
351
+ delta = float(arg)
352
+ elif opt in ('-e', '--epsilon'):
353
+ epsilon = float(arg)
354
+ elif opt in ('-h', '--help'):
355
+ usage()
356
+ sys.exit(0)
357
+ elif opt in ('-k', '--kappa'):
358
+ kappa = float(arg)
359
+ elif opt in ('-n', '--nof-samples'):
360
+ nof_samples = int(arg)
361
+ elif opt in ('-S', '--sample-over'):
362
+ # parsing the list of variables
363
+ sample_over, values = [], str(arg).split(',')
364
+
365
+ # checking if there are intervals
366
+ for value in values:
367
+ if value.isnumeric():
368
+ sample_over.append(int(value))
369
+ elif '-' in value:
370
+ lb, ub = value.split('-')
371
+ assert int(lb) < int(ub)
372
+ sample_over.extend(list(range(int(lb), int(ub) + 1)))
373
+
374
+ # removing duplicates, if any
375
+ sample_over = sorted(set(sample_over))
376
+ elif opt in ('-s', '--seed'):
377
+ seed = int(arg)
378
+ elif opt in ('-v', '--verbose'):
379
+ verbose = int(arg)
380
+ else:
381
+ assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
382
+
383
+ return nof_samples, counts, delta, epsilon, kappa, sample_over, seed, \
384
+ verbose, args
385
+
386
+
387
+ #
388
+ #==============================================================================
389
+ def usage():
390
+ """
391
+ Prints usage message.
392
+ """
393
+
394
+ print('Usage:', os.path.basename(sys.argv[0]), '[options] dimacs-file')
395
+ print('Options:')
396
+ print(' -c, --counts=<float> A comma-separated pair of integer values representing cell count and hash count parameters (if any)')
397
+ print(' Note: if omitted, there values are computed by ApproxMC')
398
+ print(' Default: none')
399
+ print(' -d, --delta=<float> Confidence parameter as per PAC guarantees')
400
+ print(' Available values: [0, 1) (default = 0.2)')
401
+ print(' -e, --epsilon=<float> Tolerance factor as per PAC guarantees')
402
+ print(' Available values: (0 .. 1], all (default = 0.8)')
403
+ print(' -k, --kappa=<float> Uniformity parameter')
404
+ print(' Available values: (0 .. 1], all (default = 0.638)')
405
+ print(' -n, --nof-samples=<int> Number of required samples')
406
+ print(' Available values: [1 .. INT_MAX] (default = 4)')
407
+ print(' -S, --sample-over=<list> If provided, solutions are almost uniformly sampled over this set of variables')
408
+ print(' Available values: comma-separated-list, none (default = none)')
409
+ print(' -s, --seed=<int> Random seed')
410
+ print(' Available values: [0 .. INT_MAX] (default = 1)')
411
+ print(' -v, --verbose=<int> Verbosity level')
412
+ print(' Available values: [0 .. 15] (default = 0)')
413
+
414
+
415
+ #
416
+ #==============================================================================
417
+ if __name__ == '__main__':
418
+ nof_samples, counts, delta, epsilon, kappa, sample_over, seed, verbose, \
419
+ files = parse_options()
420
+
421
+ # parsing the input formula
422
+ if files and re.search(r'\.cnf(\.(gz|bz2|lzma|xz))?$', files[0]):
423
+ formula = CNF(from_file=files[0])
424
+
425
+ # creating the sampler object
426
+ with Sampler(formula, seed=seed, epsilon=epsilon, delta=delta,
427
+ kappa=kappa, verbose=verbose) as sampler:
428
+
429
+ # almost uniform sampling
430
+ samples = sampler.sample(nof_samples, sample_over=sample_over,
431
+ counts=counts)
432
+
433
+ # printing the result
434
+ for sample in samples:
435
+ print('v {0} 0'.format(' '.join(['{0}{1}'.format('+' if v > 0 else '', v) for v in sample])))