python-sat 1.9.dev20__cp315-cp315t-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.
Files changed (52) hide show
  1. pycard.cp315t-win_amd64.pyd +0 -0
  2. pyformula.cp315t-win_amd64.pyd +0 -0
  3. pysat/__init__.py +36 -0
  4. pysat/_fileio.py +224 -0
  5. pysat/_utils.py +58 -0
  6. pysat/allies/__init__.py +0 -0
  7. pysat/allies/approxmc.py +385 -0
  8. pysat/allies/unigen.py +435 -0
  9. pysat/card.py +1024 -0
  10. pysat/engines.py +1328 -0
  11. pysat/examples/__init__.py +0 -0
  12. pysat/examples/bbscan.py +663 -0
  13. pysat/examples/bica.py +701 -0
  14. pysat/examples/fm.py +527 -0
  15. pysat/examples/genhard.py +516 -0
  16. pysat/examples/hitman.py +648 -0
  17. pysat/examples/lbx.py +638 -0
  18. pysat/examples/lsu.py +496 -0
  19. pysat/examples/mcsls.py +610 -0
  20. pysat/examples/models.py +189 -0
  21. pysat/examples/musx.py +344 -0
  22. pysat/examples/optux.py +750 -0
  23. pysat/examples/primer.py +620 -0
  24. pysat/examples/rc2.py +2043 -0
  25. pysat/examples/usage.py +63 -0
  26. pysat/formula.py +5899 -0
  27. pysat/integer.py +2768 -0
  28. pysat/pb.py +454 -0
  29. pysat/process.py +363 -0
  30. pysat/solvers.py +10223 -0
  31. pysolvers.cp315t-win_amd64.pyd +0 -0
  32. python_sat-1.9.dev20.data/platlib/msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll +0 -0
  33. python_sat-1.9.dev20.data/scripts/approxmc.py +385 -0
  34. python_sat-1.9.dev20.data/scripts/bbscan.py +663 -0
  35. python_sat-1.9.dev20.data/scripts/bica.py +701 -0
  36. python_sat-1.9.dev20.data/scripts/fm.py +527 -0
  37. python_sat-1.9.dev20.data/scripts/genhard.py +516 -0
  38. python_sat-1.9.dev20.data/scripts/lbx.py +638 -0
  39. python_sat-1.9.dev20.data/scripts/lsu.py +496 -0
  40. python_sat-1.9.dev20.data/scripts/mcsls.py +610 -0
  41. python_sat-1.9.dev20.data/scripts/models.py +189 -0
  42. python_sat-1.9.dev20.data/scripts/musx.py +344 -0
  43. python_sat-1.9.dev20.data/scripts/optux.py +750 -0
  44. python_sat-1.9.dev20.data/scripts/primer.py +620 -0
  45. python_sat-1.9.dev20.data/scripts/rc2.py +2043 -0
  46. python_sat-1.9.dev20.data/scripts/unigen.py +435 -0
  47. python_sat-1.9.dev20.dist-info/DELVEWHEEL +2 -0
  48. python_sat-1.9.dev20.dist-info/METADATA +47 -0
  49. python_sat-1.9.dev20.dist-info/RECORD +52 -0
  50. python_sat-1.9.dev20.dist-info/WHEEL +5 -0
  51. python_sat-1.9.dev20.dist-info/licenses/LICENSE.txt +21 -0
  52. python_sat-1.9.dev20.dist-info/top_level.txt +4 -0
pysat/card.py ADDED
@@ -0,0 +1,1024 @@
1
+ #!/usr/bin/env python
2
+ #-*- coding:utf-8 -*-
3
+ ##
4
+ ## card.py
5
+ ##
6
+ ## Created on: Sep 26, 2017
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
+ EncType
20
+ CardEnc
21
+ ITotalizer
22
+ ISeqCounter
23
+
24
+ ==================
25
+ Module description
26
+ ==================
27
+
28
+ This module provides access to various *cardinality constraint* [1]_
29
+ encodings to formulas in conjunctive normal form (CNF). These include
30
+ pairwise [2]_, bitwise [2]_, ladder/regular [3]_ [4]_, sequential counters
31
+ [5]_, sorting [6]_ and cardinality networks [7]_, totalizer [8]_, modulo
32
+ totalizer [9]_, and modulo totalizer for :math:`k`-cardinality [10]_, as
33
+ well as a *native* cardinality constraint representation supported by the
34
+ `MiniCard solver <https://github.com/liffiton/minicard>`__.
35
+
36
+ .. [1] Olivier Roussel, Vasco M. Manquinho. *Pseudo-Boolean and Cardinality
37
+ Constraints*. Handbook of Satisfiability. 2009. pp. 695-733
38
+
39
+ .. [2] Steven David Prestwich. *CNF Encodings*. Handbook of Satisfiability.
40
+ 2009. pp. 75-97
41
+
42
+ .. [3] Carlos Ansótegui, Felip Manyà. *Mapping Problems with Finite-Domain
43
+ Variables to Problems with Boolean Variables*. SAT (Selected Papers)
44
+ 2004. pp. 1-15
45
+
46
+ .. [4] Ian P. Gent, Peter Nightingale. *A New Encoding of Alldifferent Into
47
+ SAT*. In International workshop on modelling and reformulating
48
+ constraint satisfaction problems 2004. pp. 95-110
49
+
50
+ .. [5] Carsten Sinz. *Towards an Optimal CNF Encoding of Boolean
51
+ Cardinality Constraints*. CP 2005. pp. 827-831
52
+
53
+ .. [6] Kenneth E. Batcher. *Sorting Networks and Their Applications*.
54
+ AFIPS Spring Joint Computing Conference 1968. pp. 307-314
55
+
56
+ .. [7] Roberto Asin, Robert Nieuwenhuis, Albert Oliveras,
57
+ Enric Rodriguez-Carbonell. *Cardinality Networks and Their
58
+ Applications*. SAT 2009. pp. 167-180
59
+
60
+ .. [8] Olivier Bailleux, Yacine Boufkhad. *Efficient CNF Encoding of
61
+ Boolean Cardinality Constraints*. CP 2003. pp. 108-122
62
+
63
+ .. [9] Toru Ogawa, Yangyang Liu, Ryuzo Hasegawa, Miyuki Koshimura,
64
+ Hiroshi Fujita. *Modulo Based CNF Encoding of Cardinality Constraints
65
+ and Its Application to MaxSAT Solvers*. ICTAI 2013. pp. 9-17
66
+
67
+ .. [10] António Morgado, Alexey Ignatiev, Joao Marques-Silva. *MSCG: Robust
68
+ Core-Guided MaxSAT Solving*. System Description. JSAT 2015. vol. 9,
69
+ pp. 129-134
70
+
71
+ A cardinality constraint is a constraint of the form:
72
+ :math:`\\sum_{i=1}^n{x_i}\\leq k`. Cardinality constraints are ubiquitous
73
+ in practical problem formulations. Note that the implementation of the
74
+ pairwise, bitwise, and ladder encodings can only deal with AtMost1
75
+ constraints, e.g. :math:`\\sum_{i=1}^n{x_i}\\leq 1`.
76
+
77
+ Access to all cardinality encodings can be made through the main class of
78
+ this module, which is :class:`.CardEnc`.
79
+
80
+ Additionally, to the standard cardinality encodings that are basically
81
+ "static" CNF formulas, the module is designed to able to construct
82
+ *incremental* cardinality encodings, i.e. those that can be incrementally
83
+ extended at a later stage. The *iterative totalizer* [11]_ is available
84
+ through :class:`.ITotalizer`. An iterative sequential counter is available
85
+ through :class:`.ISeqCounter`; it supports increasing the bound and
86
+ obtaining constraint literals for prefixes of the input.
87
+
88
+ .. [11] Ruben Martins, Saurabh Joshi, Vasco M. Manquinho, Inês Lynce.
89
+ *Incremental Cardinality Constraints for MaxSAT*. CP 2014. pp. 531-548
90
+
91
+ ==============
92
+ Module details
93
+ ==============
94
+ """
95
+
96
+ #
97
+ #==============================================================================
98
+ import math
99
+ from pysat.formula import CNF, CNFPlus, IDPool
100
+ from pysat._utils import MainThread
101
+ import pycard
102
+ import signal
103
+
104
+
105
+ #
106
+ #==============================================================================
107
+ class NoSuchEncodingError(Exception):
108
+ """
109
+ This exception is raised when creating an unknown an AtMostK, AtLeastK,
110
+ or EqualK constraint encoding.
111
+ """
112
+
113
+ pass
114
+
115
+
116
+ #
117
+ #==============================================================================
118
+ class UnsupportedBound(Exception):
119
+ """
120
+ This exception is raised when creating a pairwise, or bitwise, or
121
+ ladder encoding of AtMostK, AtLeastK, or EqualsK with the bound
122
+ different from ``1`` and ``N - 1``.
123
+ """
124
+
125
+ pass
126
+
127
+
128
+ #
129
+ #==============================================================================
130
+ class EncType(object):
131
+ """
132
+ This class represents a C-like ``enum`` type for choosing the
133
+ cardinality encoding to use. The values denoting the encodings are:
134
+
135
+ ::
136
+
137
+ pairwise = 0
138
+ seqcounter = 1
139
+ sortnetwrk = 2
140
+ cardnetwrk = 3
141
+ bitwise = 4
142
+ ladder = 5
143
+ totalizer = 6
144
+ mtotalizer = 7
145
+ kmtotalizer = 8
146
+ native = 9
147
+
148
+ The desired encoding can be selected either directly by its integer
149
+ identifier, e.g. ``2``, or by its alphabetical name, e.g.
150
+ ``EncType.sortnetwrk``.
151
+
152
+ Note that while most of the encodings are produced as a list of
153
+ clauses, the "native" encoding of `MiniCard
154
+ <https://github.com/liffiton/minicard>`__ is managed as one clause.
155
+ Given an AtMostK constraint :math:`\\sum_{i=1}^n{x_i\\leq k}`, the
156
+ native encoding represents it as a pair ``[lits, k]``, where ``lits``
157
+ is a list of size ``n`` containing literals in the sum.
158
+ """
159
+
160
+ pairwise = 0
161
+ seqcounter = 1
162
+ sortnetwrk = 2
163
+ cardnetwrk = 3
164
+ bitwise = 4
165
+ ladder = 5
166
+ totalizer = 6
167
+ mtotalizer = 7
168
+ kmtotalizer = 8
169
+ native = 9 # native representation used by Minicard
170
+
171
+
172
+ #
173
+ #==============================================================================
174
+ class CardEnc(object):
175
+ """
176
+ This abstract class is responsible for the creation of cardinality
177
+ constraints encoded to a CNF formula. The class has three *class
178
+ methods* for creating AtMostK, AtLeastK, and EqualsK constraints. Given
179
+ a list of literals, an integer bound and an encoding type, each of
180
+ these methods returns an object of class :class:`pysat.formula.CNFPlus`
181
+ representing the resulting CNF formula.
182
+
183
+ Since the class is abstract, there is no need to create an object of
184
+ it. Instead, the methods should be called directly as class methods,
185
+ e.g. ``CardEnc.atmost(lits, bound)`` or ``CardEnc.equals(lits,
186
+ bound)``. An example usage is the following:
187
+
188
+ .. code-block:: python
189
+
190
+ >>> from pysat.card import *
191
+ >>> cnf = CardEnc.atmost(lits=[1, 2, 3], encoding=EncType.pairwise)
192
+ >>> print(cnf.clauses)
193
+ [[-1, -2], [-1, -3], [-2, -3]]
194
+ >>> cnf = CardEnc.equals(lits=[1, 2, 3], encoding=EncType.pairwise)
195
+ >>> print(cnf.clauses)
196
+ [[1, 2, 3], [-1, -2], [-1, -3], [-2, -3]]
197
+ """
198
+
199
+ @classmethod
200
+ def _update_vids(cls, cnf, top, vpool):
201
+ """
202
+ Update the id pool to account for the auxiliary variables of the
203
+ given formula, i.e. all variables larger than ``top``. If these
204
+ overlap with the ranges marked as occupied in the pool, they are
205
+ renumbered to free ids. Variables up to ``top``, e.g. the input
206
+ literals of the constraint, are not updated.
207
+
208
+ :param cnf: the encoding of the constraint
209
+ :param top: the largest variable id of the input
210
+ :param vpool: variable pool to update
211
+
212
+ :type cnf: :class:`.formula.CNFPlus`
213
+ :type top: int
214
+ :type vpool: :class:`.formula.IDPool`
215
+ """
216
+
217
+ # the pool must not hand out the input ids, nor the rest of an
218
+ # occupied range that the input ids reach into
219
+ vpool.top = top
220
+ while vpool._occupied and vpool._occupied[0][0] <= vpool.top:
221
+ vpool.top = max(vpool.top, vpool._occupied[0][1])
222
+ vpool._occupied.pop(0)
223
+
224
+ if vpool.top == top and not (vpool._occupied and vpool._occupied[0][0] <= cnf.nv):
225
+ # the auxiliary variables already have free ids
226
+ vpool.top = max(vpool.top, cnf.nv)
227
+ return
228
+
229
+ # mapping each auxiliary variable to a free id,
230
+ # taking into account variables marked as "occupied"
231
+ vmap = {var: vpool._next() for var in range(top + 1, cnf.nv + 1)}
232
+
233
+ # updating the clauses
234
+ for cl in cnf.clauses:
235
+ cl[:] = map(lambda l: int(math.copysign(vmap[abs(l)], l)) if abs(l) in vmap else l, cl)
236
+
237
+ # updating the number of variables
238
+ cnf.nv = vpool.top
239
+
240
+ @classmethod
241
+ def atmost(cls, lits, bound=1, top_id=None, vpool=None,
242
+ encoding=EncType.seqcounter):
243
+ """
244
+ This method can be used for creating a CNF encoding of an AtMostK
245
+ constraint, i.e. of :math:`\\sum_{i=1}^{n}{x_i}\\leq k`. The
246
+ method shares the arguments and the return type with method
247
+ :meth:`CardEnc.atleast`. Please, see it for details.
248
+ """
249
+
250
+ if encoding < 0 or encoding > 9:
251
+ raise(NoSuchEncodingError(encoding))
252
+
253
+ # checking if the bound is meaningless for any encoding
254
+ if bound < 0:
255
+ raise ValueError('Wrong bound: {0}'.format(bound))
256
+
257
+ if encoding in (0, 4, 5) and 1 < bound < len(lits) - 1:
258
+ raise(UnsupportedBound(encoding, bound))
259
+
260
+ assert not top_id or not vpool, \
261
+ 'Use either a top id or a pool of variables but not both.'
262
+
263
+ # we are going to return this formula
264
+ ret = CNFPlus()
265
+
266
+ # if the list of literals is empty, return empty formula
267
+ if not lits:
268
+ return ret
269
+
270
+ # obtaining the top id from the variable pool
271
+ if vpool:
272
+ top_id = vpool.top
273
+
274
+ # making sure we are dealing with a list of literals
275
+ lits = list(lits)
276
+
277
+ # choosing the maximum id among the current top and the list of literals
278
+ top_id = max(map(lambda x: abs(x), lits + [top_id if top_id != None else 0]))
279
+
280
+ # MiniCard's native representation is handled separately
281
+ if encoding == 9:
282
+ ret.atmosts, ret.nv = [(lits, bound)], top_id
283
+ return ret
284
+
285
+ res = pycard.encode_atmost(lits, bound, top_id, encoding,
286
+ int(MainThread.check()))
287
+
288
+ if res:
289
+ ret.clauses, ret.nv = res
290
+
291
+ # updating vpool if necessary
292
+ if vpool:
293
+ cls._update_vids(ret, top_id, vpool)
294
+
295
+ return ret
296
+
297
+ @classmethod
298
+ def atleast(cls, lits, bound=1, top_id=None, vpool=None,
299
+ encoding=EncType.seqcounter):
300
+ """
301
+ This method can be used for creating a CNF encoding of an AtLeastK
302
+ constraint, i.e. of :math:`\\sum_{i=1}^{n}{x_i}\\geq k`. The
303
+ method takes 1 mandatory argument ``lits`` and 3 default arguments
304
+ can be specified: ``bound``, ``top_id``, ``vpool``, and
305
+ ``encoding``.
306
+
307
+ :param lits: a list of literals in the sum.
308
+ :param bound: the value of bound :math:`k`.
309
+ :param top_id: top variable identifier used so far.
310
+ :param vpool: variable pool for counting the number of variables.
311
+ :param encoding: identifier of the encoding to use.
312
+
313
+ :type lits: iterable(int)
314
+ :type bound: int
315
+ :type top_id: integer or None
316
+ :type vpool: :class:`.IDPool`
317
+ :type encoding: integer
318
+
319
+ Parameter ``top_id`` serves to increase integer identifiers of
320
+ auxiliary variables introduced during the encoding process. This
321
+ is helpful when augmenting an existing CNF formula with the new
322
+ cardinality encoding to make sure there is no collision between
323
+ identifiers of the variables. If specified, the identifiers of the
324
+ first auxiliary variable will be ``top_id+1``.
325
+
326
+ Instead of ``top_id``, one may want to use a pool of variable
327
+ identifiers ``vpool``, which is automatically updated during the
328
+ method call. In many circumstances, this is more convenient than
329
+ using ``top_id``. Also note that parameters ``top_id`` and
330
+ ``vpool`` **cannot** be specified *simultaneously*.
331
+
332
+ The default value of ``encoding`` is :attr:`EncType.seqcounter`.
333
+
334
+ The method *translates* the AtLeast constraint into an AtMost
335
+ constraint by *negating* the literals of ``lits``, creating a new
336
+ bound :math:`n-k` and invoking :meth:`CardEnc.atmost` with the
337
+ modified list of literals and the new bound.
338
+
339
+ :raises CardEnc.NoSuchEncodingError: if encoding does not exist.
340
+ :raises ValueError: if bound is meaningless for encoding.
341
+
342
+ :rtype: a :class:`.CNFPlus` object where the new \
343
+ clauses (or the new native atmost constraint) are stored.
344
+ """
345
+
346
+ if encoding < 0 or encoding > 9:
347
+ raise(NoSuchEncodingError(encoding))
348
+
349
+ # checking if the bound is meaningless for any encoding
350
+ if bound > len(lits):
351
+ raise ValueError('Wrong bound: {0}'.format(bound))
352
+
353
+ if encoding in (0, 4, 5) and 1 < bound < len(lits) - 1:
354
+ raise(UnsupportedBound(encoding, bound))
355
+
356
+ assert not top_id or not vpool, \
357
+ 'Use either a top id or a pool of variables but not both.'
358
+
359
+ # we are going to return this formula
360
+ ret = CNFPlus()
361
+
362
+ # if the list of literals is empty, return empty formula
363
+ if not lits:
364
+ return ret
365
+
366
+ # obtaining the top id from the variable pool
367
+ if vpool:
368
+ top_id = vpool.top
369
+
370
+ # making sure we are dealing with a list of literals
371
+ lits = list(lits)
372
+
373
+ # choosing the maximum id among the current top and the list of literals
374
+ top_id = max(map(lambda x: abs(x), lits + [top_id if top_id != None else 0]))
375
+
376
+ # Minicard's native representation is handled separately
377
+ if encoding == 9:
378
+ ret.atmosts, ret.nv = [([-l for l in lits], len(lits) - bound)], top_id
379
+ return ret
380
+
381
+ res = pycard.encode_atleast(lits, bound, top_id, encoding,
382
+ int(MainThread.check()))
383
+
384
+ if res:
385
+ ret.clauses, ret.nv = res
386
+
387
+ # updating vpool if necessary
388
+ if vpool:
389
+ cls._update_vids(ret, top_id, vpool)
390
+
391
+ return ret
392
+
393
+ @classmethod
394
+ def equals(cls, lits, bound=1, top_id=None, vpool=None,
395
+ encoding=EncType.seqcounter):
396
+ """
397
+ This method can be used for creating a CNF encoding of an EqualsK
398
+ constraint, i.e. of :math:`\\sum_{i=1}^{n}{x_i}= k`. The method
399
+ makes consecutive calls of both :meth:`CardEnc.atleast` and
400
+ :meth:`CardEnc.atmost`. It shares the arguments and the return type
401
+ with method :meth:`CardEnc.atleast`. Please, see it for details.
402
+ """
403
+
404
+ if vpool:
405
+ res1 = cls.atleast(lits, bound=bound, vpool=vpool, encoding=encoding)
406
+ res2 = cls.atmost(lits, bound=bound, vpool=vpool, encoding=encoding)
407
+ else:
408
+ res1 = cls.atleast(lits, bound=bound, top_id=top_id, encoding=encoding)
409
+ res2 = cls.atmost(lits, bound=bound, top_id=res1.nv, encoding=encoding)
410
+
411
+ # merging together AtLeast and AtMost constraints
412
+ res1.nv = max(res1.nv, res2.nv)
413
+ res1.clauses.extend(res2.clauses)
414
+ res1.atmosts.extend(res2.atmosts)
415
+
416
+ return res1
417
+
418
+
419
+ #
420
+ #==============================================================================
421
+ class ITotalizer(object):
422
+ """
423
+ This class implements the iterative totalizer encoding [11]_. Note that
424
+ :class:`ITotalizer` can be used only for creating AtMostK constraints.
425
+ In contrast to class :class:`EncType`, this class is not abstract and
426
+ its objects once created can be reused several times. The idea is that
427
+ a *totalizer tree* can be extended, or the bound can be increased, as
428
+ well as two totalizer trees can be merged into one.
429
+
430
+ The constructor of the class object takes 3 default arguments.
431
+
432
+ :param lits: a list of literals to sum.
433
+ :param ubound: the largest potential bound to use.
434
+ :param top_id: top variable identifier used so far.
435
+
436
+ :type lits: iterable(int)
437
+ :type ubound: int
438
+ :type top_id: integer or None
439
+
440
+ The encoding of the current tree can be accessed with the use of
441
+ :class:`.CNF` variable stored as ``self.cnf``. Potential bounds **are
442
+ not** imposed by default but can be added as unit clauses in the final
443
+ CNF formula. The bounds are stored in the list of Boolean variables as
444
+ ``self.rhs``. A concrete bound :math:`k` can be enforced by considering
445
+ a unit clause ``-self.rhs[k]``. **Note** that ``-self.rhs[0]`` enforces
446
+ all literals of the sum to be *false*.
447
+
448
+ An :class:`ITotalizer` object should be deleted if it is not needed
449
+ anymore.
450
+
451
+ Possible usage of the class is shown below:
452
+
453
+ .. code-block:: python
454
+
455
+ >>> from pysat.card import ITotalizer
456
+ >>> t = ITotalizer(lits=[1, 2, 3], ubound=1)
457
+ >>> print(t.cnf.clauses)
458
+ [[-2, 4], [-1, 4], [-1, -2, 5], [-4, 6], [-5, 7], [-3, 6], [-3, -4, 7]]
459
+ >>> print(t.rhs)
460
+ [6, 7]
461
+ >>> t.delete()
462
+
463
+ Alternatively, an object can be created using the ``with`` keyword. In
464
+ this case, the object is deleted automatically:
465
+
466
+ .. code-block:: python
467
+
468
+ >>> from pysat.card import ITotalizer
469
+ >>> with ITotalizer(lits=[1, 2, 3], ubound=1) as t:
470
+ ... print(t.cnf.clauses)
471
+ [[-2, 4], [-1, 4], [-1, -2, 5], [-4, 6], [-5, 7], [-3, 6], [-3, -4, 7]]
472
+ ... print(t.rhs)
473
+ [6, 7]
474
+ """
475
+
476
+ def __init__(self, lits=[], ubound=1, top_id=None):
477
+ """
478
+ Constructor.
479
+ """
480
+
481
+ # internal totalizer object
482
+ self.tobj = None
483
+
484
+ # its characteristics
485
+ self.lits = []
486
+ self.ubound = 0
487
+ self.top_id = 0
488
+
489
+ # encoding result
490
+ self.cnf = CNF() # CNF formula encoding the totalizer object
491
+ self.rhs = [] # upper bounds on the number of literals (rhs)
492
+
493
+ # number of new clauses
494
+ self.nof_new = 0
495
+
496
+ # this newly created totalizer object is not yet merged in any other
497
+ self._merged = False
498
+
499
+ if lits:
500
+ self.new(lits=lits, ubound=ubound, top_id=top_id)
501
+
502
+ def new(self, lits=[], ubound=1, top_id=None):
503
+ """
504
+ The actual constructor of :class:`ITotalizer`. Invoked from
505
+ ``self.__init__()``. Creates an object of :class:`ITotalizer` given
506
+ a list of literals in the sum, the largest potential bound to
507
+ consider, as well as the top variable identifier used so far. See
508
+ the description of :class:`ITotalizer` for details.
509
+ """
510
+
511
+ self.lits = list(lits)
512
+ self.ubound = ubound
513
+ self.top_id = max(map(lambda x: abs(x), self.lits + [top_id if top_id != None else 0]))
514
+
515
+ # creating the object
516
+ self.tobj, clauses, self.rhs, self.top_id = pycard.itot_new(self.lits,
517
+ self.ubound, self.top_id, int(MainThread.check()))
518
+
519
+ # saving the result
520
+ self.cnf.clauses = clauses
521
+ self.cnf.nv = self.top_id
522
+
523
+ # for convenience, keeping the number of clauses
524
+ self.nof_new = len(clauses)
525
+
526
+ def delete(self):
527
+ """
528
+ Destroys a previously constructed :class:`ITotalizer` object.
529
+ Internal variables ``self.cnf`` and ``self.rhs`` get cleaned.
530
+ """
531
+
532
+ if self.tobj:
533
+ if not self._merged:
534
+ pycard.itot_del(self.tobj)
535
+
536
+ # otherwise, this totalizer object is merged into a larger one
537
+ # therefore, this memory should be freed in its destructor
538
+
539
+ self.tobj = None
540
+
541
+ self.lits = []
542
+ self.ubound = 0
543
+ self.top_id = 0
544
+
545
+ self.cnf = CNF()
546
+ self.rhs = []
547
+
548
+ self.nof_new = 0
549
+
550
+ def __enter__(self):
551
+ """
552
+ 'with' constructor.
553
+ """
554
+
555
+ return self
556
+
557
+ def __exit__(self, exc_type, exc_value, traceback):
558
+ """
559
+ 'with' destructor.
560
+ """
561
+
562
+ self.delete()
563
+
564
+ def __del__(self):
565
+ """
566
+ Destructor.
567
+ """
568
+
569
+ self.delete()
570
+
571
+ def increase(self, ubound=1, top_id=None):
572
+ """
573
+ Increases a potential upper bound that can be imposed on the
574
+ literals in the sum of an existing :class:`ITotalizer` object to a
575
+ new value.
576
+
577
+ :param ubound: a new upper bound.
578
+ :param top_id: a new top variable identifier.
579
+
580
+ :type ubound: int
581
+ :type top_id: integer or None
582
+
583
+ The top identifier ``top_id`` applied only if it is greater than
584
+ the one used in ``self``.
585
+
586
+ This method creates additional clauses encoding the existing
587
+ totalizer tree up to the new upper bound given and appends them to
588
+ the list of clauses of :class:`.CNF` ``self.cnf``. The number of
589
+ newly created clauses is stored in variable ``self.nof_new``.
590
+
591
+ Also, a list of bounds ``self.rhs`` gets increased and its length
592
+ becomes ``ubound+1``.
593
+
594
+ The method can be used in the following way:
595
+
596
+ .. code-block:: python
597
+
598
+ >>> from pysat.card import ITotalizer
599
+ >>> t = ITotalizer(lits=[1, 2, 3], ubound=1)
600
+ >>> print(t.cnf.clauses)
601
+ [[-2, 4], [-1, 4], [-1, -2, 5], [-4, 6], [-5, 7], [-3, 6], [-3, -4, 7]]
602
+ >>> print(t.rhs)
603
+ [6, 7]
604
+ >>>
605
+ >>> t.increase(ubound=2)
606
+ >>> print(t.cnf.clauses)
607
+ [[-2, 4], [-1, 4], [-1, -2, 5], [-4, 6], [-5, 7], [-3, 6], [-3, -4, 7], [-3, -5, 8]]
608
+ >>> print(t.cnf.clauses[-t.nof_new:])
609
+ [[-3, -5, 8]]
610
+ >>> print(t.rhs)
611
+ [6, 7, 8]
612
+ >>> t.delete()
613
+ """
614
+
615
+ self.top_id = max(self.top_id, top_id if top_id != None else 0)
616
+
617
+ # do nothing if the bound is set incorrectly
618
+ if ubound <= self.ubound or self.ubound >= len(self.lits):
619
+ self.nof_new = 0
620
+ return
621
+ else:
622
+ self.ubound = ubound
623
+
624
+ # updating the object and adding more variables and clauses
625
+ clauses, self.rhs, self.top_id = pycard.itot_inc(self.tobj,
626
+ self.ubound, self.top_id, int(MainThread.check()))
627
+
628
+ # saving the result
629
+ self.cnf.clauses.extend(clauses)
630
+ self.cnf.nv = self.top_id
631
+
632
+ # keeping the number of newly added clauses
633
+ self.nof_new = len(clauses)
634
+
635
+ def extend(self, lits=[], ubound=None, top_id=None):
636
+ """
637
+ Extends the list of literals in the sum and (if needed) increases a
638
+ potential upper bound that can be imposed on the complete list of
639
+ literals in the sum of an existing :class:`ITotalizer` object to a
640
+ new value.
641
+
642
+ :param lits: additional literals to be included in the sum.
643
+ :param ubound: a new upper bound.
644
+ :param top_id: a new top variable identifier.
645
+
646
+ :type lits: iterable(int)
647
+ :type ubound: int
648
+ :type top_id: integer or None
649
+
650
+ The top identifier ``top_id`` applied only if it is greater than
651
+ the one used in ``self``.
652
+
653
+ This method creates additional clauses encoding the existing
654
+ totalizer tree augmented with new literals in the sum and updating
655
+ the upper bound. As a result, it appends the new clauses to the
656
+ list of clauses of :class:`.CNF` ``self.cnf``. The number of newly
657
+ created clauses is stored in variable ``self.nof_new``.
658
+
659
+ Also, if the upper bound is updated, a list of bounds ``self.rhs``
660
+ gets increased and its length becomes ``ubound+1``. Otherwise, it
661
+ is updated with new values.
662
+
663
+ The method can be used in the following way:
664
+
665
+ .. code-block:: python
666
+
667
+ >>> from pysat.card import ITotalizer
668
+ >>> t = ITotalizer(lits=[1, 2], ubound=1)
669
+ >>> print(t.cnf.clauses)
670
+ [[-2, 3], [-1, 3], [-1, -2, 4]]
671
+ >>> print(t.rhs)
672
+ [3, 4]
673
+ >>>
674
+ >>> t.extend(lits=[5], ubound=2)
675
+ >>> print(t.cnf.clauses)
676
+ [[-2, 3], [-1, 3], [-1, -2, 4], [-5, 6], [-3, 6], [-4, 7], [-3, -5, 7], [-4, -5, 8]]
677
+ >>> print(t.cnf.clauses[-t.nof_new:])
678
+ [[-5, 6], [-3, 6], [-4, 7], [-3, -5, 7], [-4, -5, 8]]
679
+ >>> print(t.rhs)
680
+ [6, 7, 8]
681
+ >>> t.delete()
682
+ """
683
+
684
+ # preparing a new list of distinct input literals
685
+ lits = sorted(set(lits).difference(set(self.lits)))
686
+
687
+ if not lits:
688
+ # nothing to merge with -> just increase the bound
689
+ if ubound:
690
+ self.increase(ubound=ubound, top_id=top_id)
691
+
692
+ return
693
+
694
+ self.top_id = max(map(lambda x: abs(x), self.lits + [self.top_id, top_id if top_id != None else 0]))
695
+ self.ubound = max(self.ubound, ubound if ubound != None else 0)
696
+
697
+ # updating the object and adding more variables and clauses
698
+ self.tobj, clauses, self.rhs, self.top_id = pycard.itot_ext(self.tobj,
699
+ lits, self.ubound, self.top_id, int(MainThread.check()))
700
+
701
+ # saving the result
702
+ self.cnf.clauses.extend(clauses)
703
+ self.cnf.nv = self.top_id
704
+ self.lits.extend(lits)
705
+
706
+ # for convenience, keeping the number of new clauses
707
+ self.nof_new = len(clauses)
708
+
709
+ def merge_with(self, another, ubound=None, top_id=None):
710
+ """
711
+ This method merges a tree of the current :class:`ITotalizer`
712
+ object, with a tree of another object and (if needed) increases a
713
+ potential upper bound that can be imposed on the complete list of
714
+ literals in the sum of an existing :class:`ITotalizer` object to a
715
+ new value.
716
+
717
+ :param another: another totalizer to merge with.
718
+ :param ubound: a new upper bound.
719
+ :param top_id: a new top variable identifier.
720
+
721
+ :type another: :class:`ITotalizer`
722
+ :type ubound: int
723
+ :type top_id: integer or None
724
+
725
+ The top identifier ``top_id`` applied only if it is greater than
726
+ the one used in ``self``.
727
+
728
+ This method creates additional clauses encoding the existing
729
+ totalizer tree merged with another totalizer tree into *one* sum
730
+ and updating the upper bound. As a result, it appends the new
731
+ clauses to the list of clauses of :class:`.CNF` ``self.cnf``. The
732
+ number of newly created clauses is stored in variable
733
+ ``self.nof_new``.
734
+
735
+ Also, if the upper bound is updated, a list of bounds ``self.rhs``
736
+ gets increased and its length becomes ``ubound+1``. Otherwise, it
737
+ is updated with new values.
738
+
739
+ The method can be used in the following way:
740
+
741
+ .. code-block:: python
742
+
743
+ >>> from pysat.card import ITotalizer
744
+ >>> with ITotalizer(lits=[1, 2], ubound=1) as t1:
745
+ ... print(t1.cnf.clauses)
746
+ [[-2, 3], [-1, 3], [-1, -2, 4]]
747
+ ... print(t1.rhs)
748
+ [3, 4]
749
+ ...
750
+ ... t2 = ITotalizer(lits=[5, 6], ubound=1)
751
+ ... print(t1.cnf.clauses)
752
+ [[-6, 7], [-5, 7], [-5, -6, 8]]
753
+ ... print(t1.rhs)
754
+ [7, 8]
755
+ ...
756
+ ... t1.merge_with(t2)
757
+ ... print(t1.cnf.clauses)
758
+ [[-2, 3], [-1, 3], [-1, -2, 4], [-6, 7], [-5, 7], [-5, -6, 8], [-7, 9], [-8, 10], [-3, 9], [-4, 10], [-3, -7, 10]]
759
+ ... print(t1.cnf.clauses[-t1.nof_new:])
760
+ [[-6, 7], [-5, 7], [-5, -6, 8], [-7, 9], [-8, 10], [-3, 9], [-4, 10], [-3, -7, 10]]
761
+ ... print(t1.rhs)
762
+ [9, 10]
763
+ ...
764
+ ... t2.delete()
765
+ """
766
+
767
+ self.top_id = max(self.top_id, top_id if top_id != None else 0, another.top_id)
768
+ self.ubound = max(self.ubound, ubound if ubound != None else 0, another.ubound)
769
+
770
+ # extending the list of input literals
771
+ self.lits.extend(another.lits)
772
+
773
+ # updating the object and adding more variables and clauses
774
+ self.tobj, clauses, self.rhs, self.top_id = pycard.itot_mrg(self.tobj,
775
+ another.tobj, self.ubound, self.top_id, int(MainThread.check()))
776
+
777
+ # saving the result
778
+ self.cnf.clauses.extend(another.cnf.clauses)
779
+ self.cnf.clauses.extend(clauses)
780
+ self.cnf.nv = self.top_id
781
+
782
+ # for convenience, keeping the number of new clauses
783
+ self.nof_new = len(another.cnf.clauses) + len(clauses)
784
+
785
+ # memory deallocation should not be done for the merged tree
786
+ another._merged = True
787
+
788
+
789
+ # Incremental sequential counter taken almost verbatim from jesselooney/pysat,
790
+ # commit 8d3449e8d1845cc9e0f682a45beb888bbac8be0c
791
+ #==============================================================================
792
+ class ISeqCounter(object):
793
+ """
794
+ This class implements an iterative sequential-counter encoding. Like
795
+ :class:`ITotalizer`, this class is not abstract and its objects once
796
+ created can be reused several times: the bound can be increased, and
797
+ the internal state can be read out as standalone constraint literals
798
+ via :meth:`get_constraint`. The interface intentionally mirrors
799
+ :class:`ITotalizer`'s.
800
+
801
+ The constructor of the class object takes 3 default arguments.
802
+
803
+ :param lits: a list of literals to sum.
804
+ :param ubound: the largest potential bound to use.
805
+ :param top_id: top variable identifier used so far.
806
+
807
+ :type lits: iterable(int)
808
+ :type ubound: int
809
+ :type top_id: integer or None
810
+
811
+ The encoding of the current counter can be accessed with the use of
812
+ :class:`.CNF` variable stored as ``self.cnf``. Potential bounds **are
813
+ not** imposed by default but can be added as unit clauses in the final
814
+ CNF formula. The bounds are stored in the list of Boolean variables as
815
+ ``self.rhs``. A concrete bound :math:`k` can be enforced by
816
+ considering a unit clause ``-self.rhs[k]``. **Note** that
817
+ ``-self.rhs[0]`` enforces all literals of the sum to be *false*.
818
+
819
+ An :class:`ISeqCounter` object should be deleted if it is not needed
820
+ anymore.
821
+
822
+ Possible usage of the class is shown below:
823
+
824
+ .. code-block:: python
825
+
826
+ >>> from pysat.card import ISeqCounter
827
+ >>> t = ISeqCounter(lits=[1, 2, 3], ubound=1)
828
+ >>> print(t.cnf.clauses)
829
+ [[-1, 4], [-4, 5], [-2, 5], [-5, 6], [-3, 6], [-4, -2, 7], [-7, 8], [-5, -3, 8]]
830
+ >>> print(t.rhs)
831
+ [6, 8]
832
+ >>> t.delete()
833
+ """
834
+
835
+ def __init__(self, lits=[], ubound=1, top_id=None):
836
+ """
837
+ Constructor.
838
+ """
839
+
840
+ # internal totalizer object
841
+ self.tobj = None
842
+
843
+ # its characteristics
844
+ self.lits = []
845
+ self.ubound = 0
846
+ self.top_id = 0
847
+
848
+ # encoding result
849
+ self.cnf = CNF() # CNF formula encoding the totalizer object
850
+ self.rhs = [] # upper bounds on the number of literals (rhs)
851
+
852
+ # number of new clauses
853
+ self.nof_new = 0
854
+
855
+ # this newly created totalizer object is not yet merged in any other
856
+ self._merged = False
857
+
858
+ if lits:
859
+ self.new(lits=lits, ubound=ubound, top_id=top_id)
860
+
861
+ def new(self, lits=[], ubound=1, top_id=None):
862
+ """
863
+ The actual constructor of :class:`ISeqCounter`. Invoked from
864
+ ``self.__init__()``. Creates an object of :class:`ISeqCounter`
865
+ given a list of literals in the sum, the largest potential bound
866
+ to consider, as well as the top variable identifier used so far.
867
+ See the description of :class:`ISeqCounter` for details.
868
+
869
+ """
870
+
871
+ self.lits = list(lits)
872
+ self.top_id = max(map(lambda x: abs(x), self.lits + [top_id if top_id != None else 0]))
873
+
874
+ # Fail if the bound is negative. ITotalizer does not do this, but I am
875
+ # not sure how or if it is handling it lower down the chain, so I will
876
+ # stop it here.
877
+ if ubound < 0:
878
+ raise ValueError("`ubound` cannot be negative")
879
+ else:
880
+ self.ubound = ubound
881
+
882
+ # creating the object
883
+ self.tobj, clauses, self.rhs, self.top_id = pycard.iseq_new(self.lits,
884
+ self.ubound, self.top_id, int(MainThread.check()))
885
+
886
+ # saving the result
887
+ self.cnf.clauses = clauses
888
+ self.cnf.nv = self.top_id
889
+
890
+ # for convenience, keeping the number of clauses
891
+ self.nof_new = len(clauses)
892
+
893
+ def delete(self):
894
+ """
895
+ Destroys a previously constructed :class:`ISeqCounter` object.
896
+ Internal variables ``self.cnf`` and ``self.rhs`` get cleaned.
897
+ """
898
+
899
+ if self.tobj:
900
+ if not self._merged:
901
+ pycard.iseq_del(self.tobj)
902
+
903
+ # otherwise, this totalizer object is merged into a larger one
904
+ # therefore, this memory should be freed in its destructor
905
+
906
+ self.tobj = None
907
+
908
+ self.lits = []
909
+ self.ubound = 0
910
+ self.top_id = 0
911
+
912
+ self.cnf = CNF()
913
+ self.rhs = []
914
+
915
+ self.nof_new = 0
916
+
917
+ def __enter__(self):
918
+ """
919
+ 'with' constructor.
920
+ """
921
+
922
+ return self
923
+
924
+ def __exit__(self, exc_type, exc_value, traceback):
925
+ """
926
+ 'with' destructor.
927
+ """
928
+
929
+ self.delete()
930
+
931
+ def __del__(self):
932
+ """
933
+ Destructor.
934
+ """
935
+
936
+ self.delete()
937
+
938
+ def increase(self, ubound=1, top_id=None):
939
+ """
940
+ Increases a potential upper bound that can be imposed on the
941
+ literals in the sum of an existing :class:`ISeqCounter` object to
942
+ a new value.
943
+
944
+ :param ubound: a new upper bound.
945
+ :param top_id: a new top variable identifier.
946
+
947
+ :type ubound: int
948
+ :type top_id: integer or None
949
+
950
+ The top identifier ``top_id`` applied only if it is greater than
951
+ the one used in ``self``.
952
+
953
+ This method creates additional clauses encoding the existing
954
+ counter up to the new upper bound given and appends them to
955
+ the list of clauses of :class:`.CNF` ``self.cnf``. The number of
956
+ newly created clauses is stored in variable ``self.nof_new``.
957
+
958
+ Also, a list of bounds ``self.rhs`` gets increased and its length
959
+ becomes ``ubound+1``.
960
+
961
+ The method can be used in the following way:
962
+
963
+ .. code-block:: python
964
+
965
+ >>> from pysat.card import ISeqCounter
966
+ >>> t = ISeqCounter(lits=[1, 2, 3], ubound=1)
967
+ >>> print(t.cnf.clauses)
968
+ [[-1, 4], [-4, 5], [-2, 5], [-5, 6], [-3, 6], [-4, -2, 7], [-7, 8], [-5, -3, 8]]
969
+ >>> print(t.rhs)
970
+ [6, 8]
971
+ >>>
972
+ >>> t.increase(ubound=2)
973
+ >>> print(t.cnf.clauses)
974
+ [[-1, 4], [-4, 5], [-2, 5], [-5, 6], [-3, 6], [-4, -2, 7], [-7, 8], [-5, -3, 8], [-7, -3, 9]]
975
+ >>> print(t.cnf.clauses[-t.nof_new:])
976
+ [[-7, -3, 9]]
977
+ >>> print(t.rhs)
978
+ [6, 8, 9]
979
+ >>> t.delete()
980
+ """
981
+
982
+ self.top_id = max(self.top_id, top_id if top_id != None else 0)
983
+
984
+ # do nothing if the bound is set incorrectly
985
+ if ubound <= self.ubound or self.ubound >= len(self.lits):
986
+ self.nof_new = 0
987
+ return
988
+ else:
989
+ self.ubound = ubound
990
+
991
+ # updating the object and adding more variables and clauses
992
+ clauses, self.rhs, self.top_id = pycard.iseq_inc(self.tobj,
993
+ self.ubound, self.top_id, int(MainThread.check()))
994
+
995
+ # saving the result
996
+ self.cnf.clauses.extend(clauses)
997
+ self.cnf.nv = self.top_id
998
+
999
+ # keeping the number of newly added clauses
1000
+ self.nof_new = len(clauses)
1001
+
1002
+ def get_constraint(self, prefix_len, bound):
1003
+ """
1004
+ Get the literal whose negation requires that the sum of the first
1005
+ ``prefix_len`` literals of ``self.lits`` is at most ``bound``.
1006
+ This lets a caller reuse the internal state of a partially-built
1007
+ counter as a standalone constraint, rather than only ever
1008
+ asserting the counter's own top-level bound in ``self.rhs``.
1009
+
1010
+ :param prefix_len: length of the prefix of ``self.lits`` to sum.
1011
+ :param bound: an upper bound already covered by the counter, i.e.
1012
+ ``bound < len(self.rhs)``.
1013
+
1014
+ :type prefix_len: int
1015
+ :type bound: int
1016
+
1017
+ :returns: the requested literal, or ``None`` if ``prefix_len`` and
1018
+ ``bound`` do not satisfy ``0 <= bound < len(self.rhs)`` and
1019
+ ``bound < prefix_len <= len(self.lits)``.
1020
+ :rtype: int or None
1021
+ """
1022
+
1023
+ if 0 <= bound < len(self.rhs) and bound < prefix_len <= len(self.lits):
1024
+ return pycard.iseq_get(self.tobj, prefix_len, bound, int(MainThread.check()))