python-sat 1.8.dev25__cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of python-sat might be problematic. Click here for more details.

Files changed (48) hide show
  1. pycard.cpython-314-x86_64-linux-gnu.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-314-x86_64-linux-gnu.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
pysat/card.py ADDED
@@ -0,0 +1,802 @@
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
+
23
+ ==================
24
+ Module description
25
+ ==================
26
+
27
+ This module provides access to various *cardinality constraint* [1]_
28
+ encodings to formulas in conjunctive normal form (CNF). These include
29
+ pairwise [2]_, bitwise [2]_, ladder/regular [3]_ [4]_, sequential counters
30
+ [5]_, sorting [6]_ and cardinality networks [7]_, totalizer [8]_, modulo
31
+ totalizer [9]_, and modulo totalizer for :math:`k`-cardinality [10]_, as
32
+ well as a *native* cardinality constraint representation supported by the
33
+ `MiniCard solver <https://github.com/liffiton/minicard>`__.
34
+
35
+ .. [1] Olivier Roussel, Vasco M. Manquinho. *Pseudo-Boolean and Cardinality
36
+ Constraints*. Handbook of Satisfiability. 2009. pp. 695-733
37
+
38
+ .. [2] Steven David Prestwich. *CNF Encodings*. Handbook of Satisfiability.
39
+ 2009. pp. 75-97
40
+
41
+ .. [3] Carlos Ansótegui, Felip Manyà. *Mapping Problems with Finite-Domain
42
+ Variables to Problems with Boolean Variables*. SAT (Selected Papers)
43
+ 2004. pp. 1-15
44
+
45
+ .. [4] Ian P. Gent, Peter Nightingale. *A New Encoding of Alldifferent Into
46
+ SAT*. In International workshop on modelling and reformulating
47
+ constraint satisfaction problems 2004. pp. 95-110
48
+
49
+ .. [5] Carsten Sinz. *Towards an Optimal CNF Encoding of Boolean
50
+ Cardinality Constraints*. CP 2005. pp. 827-831
51
+
52
+ .. [6] Kenneth E. Batcher. *Sorting Networks and Their Applications*.
53
+ AFIPS Spring Joint Computing Conference 1968. pp. 307-314
54
+
55
+ .. [7] Roberto Asin, Robert Nieuwenhuis, Albert Oliveras,
56
+ Enric Rodriguez-Carbonell. *Cardinality Networks and Their
57
+ Applications*. SAT 2009. pp. 167-180
58
+
59
+ .. [8] Olivier Bailleux, Yacine Boufkhad. *Efficient CNF Encoding of
60
+ Boolean Cardinality Constraints*. CP 2003. pp. 108-122
61
+
62
+ .. [9] Toru Ogawa, Yangyang Liu, Ryuzo Hasegawa, Miyuki Koshimura,
63
+ Hiroshi Fujita. *Modulo Based CNF Encoding of Cardinality Constraints
64
+ and Its Application to MaxSAT Solvers*. ICTAI 2013. pp. 9-17
65
+
66
+ .. [10] António Morgado, Alexey Ignatiev, Joao Marques-Silva. *MSCG: Robust
67
+ Core-Guided MaxSAT Solving*. System Description. JSAT 2015. vol. 9,
68
+ pp. 129-134
69
+
70
+ A cardinality constraint is a constraint of the form:
71
+ :math:`\\sum_{i=1}^n{x_i}\\leq k`. Cardinality constraints are ubiquitous
72
+ in practical problem formulations. Note that the implementation of the
73
+ pairwise, bitwise, and ladder encodings can only deal with AtMost1
74
+ constraints, e.g. :math:`\\sum_{i=1}^n{x_i}\\leq 1`.
75
+
76
+ Access to all cardinality encodings can be made through the main class of
77
+ this module, which is :class:`.CardEnc`.
78
+
79
+ Additionally, to the standard cardinality encodings that are basically
80
+ "static" CNF formulas, the module is designed to able to construct
81
+ *incremental* cardinality encodings, i.e. those that can be incrementally
82
+ extended at a later stage. At this point only the *iterative totalizer*
83
+ [11]_ encoding is supported. Iterative totalizer can be accessed with the
84
+ use of the :class:`.ITotalizer` class.
85
+
86
+ .. [11] Ruben Martins, Saurabh Joshi, Vasco M. Manquinho, Inês Lynce.
87
+ *Incremental Cardinality Constraints for MaxSAT*. CP 2014. pp. 531-548
88
+
89
+ ==============
90
+ Module details
91
+ ==============
92
+ """
93
+
94
+ #
95
+ #==============================================================================
96
+ import math
97
+ from pysat.formula import CNF, CNFPlus, IDPool
98
+ from pysat._utils import MainThread
99
+ import pycard
100
+ import signal
101
+
102
+
103
+ #
104
+ #==============================================================================
105
+ class NoSuchEncodingError(Exception):
106
+ """
107
+ This exception is raised when creating an unknown an AtMostK, AtLeastK,
108
+ or EqualK constraint encoding.
109
+ """
110
+
111
+ pass
112
+
113
+
114
+ #
115
+ #==============================================================================
116
+ class UnsupportedBound(Exception):
117
+ """
118
+ This exception is raised when creating a pairwise, or bitwise, or
119
+ ladder encoding of AtMostK, AtLeastK, or EqualsK with the bound
120
+ different from ``1`` and ``N - 1``.
121
+ """
122
+
123
+ pass
124
+
125
+
126
+ #
127
+ #==============================================================================
128
+ class EncType(object):
129
+ """
130
+ This class represents a C-like ``enum`` type for choosing the
131
+ cardinality encoding to use. The values denoting the encodings are:
132
+
133
+ ::
134
+
135
+ pairwise = 0
136
+ seqcounter = 1
137
+ sortnetwrk = 2
138
+ cardnetwrk = 3
139
+ bitwise = 4
140
+ ladder = 5
141
+ totalizer = 6
142
+ mtotalizer = 7
143
+ kmtotalizer = 8
144
+ native = 9
145
+
146
+ The desired encoding can be selected either directly by its integer
147
+ identifier, e.g. ``2``, or by its alphabetical name, e.g.
148
+ ``EncType.sortnetwrk``.
149
+
150
+ Note that while most of the encodings are produced as a list of
151
+ clauses, the "native" encoding of `MiniCard
152
+ <https://github.com/liffiton/minicard>`__ is managed as one clause.
153
+ Given an AtMostK constraint :math:`\\sum_{i=1}^n{x_i\\leq k}`, the
154
+ native encoding represents it as a pair ``[lits, k]``, where ``lits``
155
+ is a list of size ``n`` containing literals in the sum.
156
+ """
157
+
158
+ pairwise = 0
159
+ seqcounter = 1
160
+ sortnetwrk = 2
161
+ cardnetwrk = 3
162
+ bitwise = 4
163
+ ladder = 5
164
+ totalizer = 6
165
+ mtotalizer = 7
166
+ kmtotalizer = 8
167
+ native = 9 # native representation used by Minicard
168
+
169
+
170
+ #
171
+ #==============================================================================
172
+ class CardEnc(object):
173
+ """
174
+ This abstract class is responsible for the creation of cardinality
175
+ constraints encoded to a CNF formula. The class has three *class
176
+ methods* for creating AtMostK, AtLeastK, and EqualsK constraints. Given
177
+ a list of literals, an integer bound and an encoding type, each of
178
+ these methods returns an object of class :class:`pysat.formula.CNFPlus`
179
+ representing the resulting CNF formula.
180
+
181
+ Since the class is abstract, there is no need to create an object of
182
+ it. Instead, the methods should be called directly as class methods,
183
+ e.g. ``CardEnc.atmost(lits, bound)`` or ``CardEnc.equals(lits,
184
+ bound)``. An example usage is the following:
185
+
186
+ .. code-block:: python
187
+
188
+ >>> from pysat.card import *
189
+ >>> cnf = CardEnc.atmost(lits=[1, 2, 3], encoding=EncType.pairwise)
190
+ >>> print(cnf.clauses)
191
+ [[-1, -2], [-1, -3], [-2, -3]]
192
+ >>> cnf = CardEnc.equals(lits=[1, 2, 3], encoding=EncType.pairwise)
193
+ >>> print(cnf.clauses)
194
+ [[1, 2, 3], [-1, -2], [-1, -3], [-2, -3]]
195
+ """
196
+
197
+ @classmethod
198
+ def _update_vids(cls, cnf, inp, vpool):
199
+ """
200
+ Update variable ids in the given formula and id pool. The literals
201
+ serving as the input to the cardinality constraint are not
202
+ updated.
203
+
204
+ :param cnf: a list of literals in the sum.
205
+ :param inp: the list of input literals
206
+ :param vpool: the value of bound :math:`k`.
207
+
208
+ :type cnf: :class:`.formula.CNFPlus`
209
+ :type inp: list(int)
210
+ :type vpool: :class:`.formula.IDPool`
211
+ """
212
+
213
+ top, vmap = max(inp + [vpool.top]), {} # current top and variable mapping
214
+
215
+ # we are going to use this to check if literals are input
216
+ inp = set([abs(l) for l in inp])
217
+
218
+ # creating a new variable mapping, taking into
219
+ # account variables marked as "occupied"
220
+ while top < cnf.nv:
221
+ top += 1
222
+
223
+ # skipping the input literals
224
+ if top in inp:
225
+ vmap[top] = top
226
+ continue
227
+
228
+ vpool.top += 1
229
+
230
+ while vpool._occupied and vpool.top >= vpool._occupied[0][0]:
231
+ if vpool.top <= vpool._occupied[0][1] + 1:
232
+ vpool.top = vpool._occupied[0][1] + 1
233
+
234
+ vpool._occupied.pop(0)
235
+
236
+ # mapping this literal to a free one
237
+ vmap[top] = vpool.top
238
+
239
+ # updating the clauses
240
+ for cl in cnf.clauses:
241
+ cl[:] = map(lambda l: int(math.copysign(vmap[abs(l)], l)) if abs(l) in vmap else l, cl)
242
+
243
+ # updating the number of variables
244
+ cnf.nv = vpool.top
245
+
246
+ @classmethod
247
+ def atmost(cls, lits, bound=1, top_id=None, vpool=None,
248
+ encoding=EncType.seqcounter):
249
+ """
250
+ This method can be used for creating a CNF encoding of an AtMostK
251
+ constraint, i.e. of :math:`\\sum_{i=1}^{n}{x_i}\\leq k`. The
252
+ method shares the arguments and the return type with method
253
+ :meth:`CardEnc.atleast`. Please, see it for details.
254
+ """
255
+
256
+ if encoding < 0 or encoding > 9:
257
+ raise(NoSuchEncodingError(encoding))
258
+
259
+ # checking if the bound is meaningless for any encoding
260
+ if bound < 0:
261
+ raise ValueError('Wrong bound: {0}'.format(bound))
262
+
263
+ if encoding in (0, 4, 5) and 1 < bound < len(lits) - 1:
264
+ raise(UnsupportedBound(encoding, bound))
265
+
266
+ assert not top_id or not vpool, \
267
+ 'Use either a top id or a pool of variables but not both.'
268
+
269
+ # we are going to return this formula
270
+ ret = CNFPlus()
271
+
272
+ # if the list of literals is empty, return empty formula
273
+ if not lits:
274
+ return ret
275
+
276
+ # obtaining the top id from the variable pool
277
+ if vpool:
278
+ top_id = vpool.top
279
+
280
+ # making sure we are dealing with a list of literals
281
+ lits = list(lits)
282
+
283
+ # choosing the maximum id among the current top and the list of literals
284
+ top_id = max(map(lambda x: abs(x), lits + [top_id if top_id != None else 0]))
285
+
286
+ # MiniCard's native representation is handled separately
287
+ if encoding == 9:
288
+ ret.atmosts, ret.nv = [(lits, bound)], top_id
289
+ return ret
290
+
291
+ res = pycard.encode_atmost(lits, bound, top_id, encoding,
292
+ int(MainThread.check()))
293
+
294
+ if res:
295
+ ret.clauses, ret.nv = res
296
+
297
+ # updating vpool if necessary
298
+ if vpool:
299
+ if vpool._occupied and vpool.top <= vpool._occupied[0][0] <= ret.nv:
300
+ cls._update_vids(ret, lits, vpool)
301
+ else:
302
+ # here, ret.nv id is assumed to be larger than the top id
303
+ vpool.top = ret.nv - 1
304
+ vpool._next()
305
+
306
+ return ret
307
+
308
+ @classmethod
309
+ def atleast(cls, lits, bound=1, top_id=None, vpool=None,
310
+ encoding=EncType.seqcounter):
311
+ """
312
+ This method can be used for creating a CNF encoding of an AtLeastK
313
+ constraint, i.e. of :math:`\\sum_{i=1}^{n}{x_i}\\geq k`. The
314
+ method takes 1 mandatory argument ``lits`` and 3 default arguments
315
+ can be specified: ``bound``, ``top_id``, ``vpool``, and
316
+ ``encoding``.
317
+
318
+ :param lits: a list of literals in the sum.
319
+ :param bound: the value of bound :math:`k`.
320
+ :param top_id: top variable identifier used so far.
321
+ :param vpool: variable pool for counting the number of variables.
322
+ :param encoding: identifier of the encoding to use.
323
+
324
+ :type lits: iterable(int)
325
+ :type bound: int
326
+ :type top_id: integer or None
327
+ :type vpool: :class:`.IDPool`
328
+ :type encoding: integer
329
+
330
+ Parameter ``top_id`` serves to increase integer identifiers of
331
+ auxiliary variables introduced during the encoding process. This
332
+ is helpful when augmenting an existing CNF formula with the new
333
+ cardinality encoding to make sure there is no collision between
334
+ identifiers of the variables. If specified, the identifiers of the
335
+ first auxiliary variable will be ``top_id+1``.
336
+
337
+ Instead of ``top_id``, one may want to use a pool of variable
338
+ identifiers ``vpool``, which is automatically updated during the
339
+ method call. In many circumstances, this is more convenient than
340
+ using ``top_id``. Also note that parameters ``top_id`` and
341
+ ``vpool`` **cannot** be specified *simultaneously*.
342
+
343
+ The default value of ``encoding`` is :attr:`Enctype.seqcounter`.
344
+
345
+ The method *translates* the AtLeast constraint into an AtMost
346
+ constraint by *negating* the literals of ``lits``, creating a new
347
+ bound :math:`n-k` and invoking :meth:`CardEnc.atmost` with the
348
+ modified list of literals and the new bound.
349
+
350
+ :raises CardEnc.NoSuchEncodingError: if encoding does not exist.
351
+ :raises ValueError: if bound is meaningless for encoding.
352
+
353
+ :rtype: a :class:`.CNFPlus` object where the new \
354
+ clauses (or the new native atmost constraint) are stored.
355
+ """
356
+
357
+ if encoding < 0 or encoding > 9:
358
+ raise(NoSuchEncodingError(encoding))
359
+
360
+ # checking if the bound is meaningless for any encoding
361
+ if bound > len(lits):
362
+ raise ValueError('Wrong bound: {0}'.format(bound))
363
+
364
+ if encoding in (0, 4, 5) and 1 < bound < len(lits) - 1:
365
+ raise(UnsupportedBound(encoding, bound))
366
+
367
+ assert not top_id or not vpool, \
368
+ 'Use either a top id or a pool of variables but not both.'
369
+
370
+ # we are going to return this formula
371
+ ret = CNFPlus()
372
+
373
+ # if the list of literals is empty, return empty formula
374
+ if not lits:
375
+ return ret
376
+
377
+ # obtaining the top id from the variable pool
378
+ if vpool:
379
+ top_id = vpool.top
380
+
381
+ # making sure we are dealing with a list of literals
382
+ lits = list(lits)
383
+
384
+ # choosing the maximum id among the current top and the list of literals
385
+ top_id = max(map(lambda x: abs(x), lits + [top_id if top_id != None else 0]))
386
+
387
+ # Minicard's native representation is handled separately
388
+ if encoding == 9:
389
+ ret.atmosts, ret.nv = [([-l for l in lits], len(lits) - bound)], top_id
390
+ return ret
391
+
392
+ res = pycard.encode_atleast(lits, bound, top_id, encoding,
393
+ int(MainThread.check()))
394
+
395
+ if res:
396
+ ret.clauses, ret.nv = res
397
+
398
+ # updating vpool if necessary
399
+ if vpool:
400
+ if vpool._occupied and vpool.top <= vpool._occupied[0][0] <= ret.nv:
401
+ cls._update_vids(ret, lits, vpool)
402
+ else:
403
+ # here, ret.nv id is assumed to be larger than the top id
404
+ vpool.top = ret.nv - 1
405
+ vpool._next()
406
+
407
+ return ret
408
+
409
+ @classmethod
410
+ def equals(cls, lits, bound=1, top_id=None, vpool=None,
411
+ encoding=EncType.seqcounter):
412
+ """
413
+ This method can be used for creating a CNF encoding of an EqualsK
414
+ constraint, i.e. of :math:`\\sum_{i=1}^{n}{x_i}= k`. The method
415
+ makes consecutive calls of both :meth:`CardEnc.atleast` and
416
+ :meth:`CardEnc.atmost`. It shares the arguments and the return type
417
+ with method :meth:`CardEnc.atleast`. Please, see it for details.
418
+ """
419
+
420
+ if vpool:
421
+ res1 = cls.atleast(lits, bound=bound, vpool=vpool, encoding=encoding)
422
+ res2 = cls.atmost(lits, bound=bound, vpool=vpool, encoding=encoding)
423
+ else:
424
+ res1 = cls.atleast(lits, bound=bound, top_id=top_id, encoding=encoding)
425
+ res2 = cls.atmost(lits, bound=bound, top_id=res1.nv, encoding=encoding)
426
+
427
+ # merging together AtLeast and AtMost constraints
428
+ res1.nv = max(res1.nv, res2.nv)
429
+ res1.clauses.extend(res2.clauses)
430
+ res1.atmosts.extend(res2.atmosts)
431
+
432
+ return res1
433
+
434
+
435
+ #
436
+ #==============================================================================
437
+ class ITotalizer(object):
438
+ """
439
+ This class implements the iterative totalizer encoding [11]_. Note that
440
+ :class:`ITotalizer` can be used only for creating AtMostK constraints.
441
+ In contrast to class :class:`EncType`, this class is not abstract and
442
+ its objects once created can be reused several times. The idea is that
443
+ a *totalizer tree* can be extended, or the bound can be increased, as
444
+ well as two totalizer trees can be merged into one.
445
+
446
+ The constructor of the class object takes 3 default arguments.
447
+
448
+ :param lits: a list of literals to sum.
449
+ :param ubound: the largest potential bound to use.
450
+ :param top_id: top variable identifier used so far.
451
+
452
+ :type lits: iterable(int)
453
+ :type ubound: int
454
+ :type top_id: integer or None
455
+
456
+ The encoding of the current tree can be accessed with the use of
457
+ :class:`.CNF` variable stored as ``self.cnf``. Potential bounds **are
458
+ not** imposed by default but can be added as unit clauses in the final
459
+ CNF formula. The bounds are stored in the list of Boolean variables as
460
+ ``self.rhs``. A concrete bound :math:`k` can be enforced by considering
461
+ a unit clause ``-self.rhs[k]``. **Note** that ``-self.rhs[0]`` enforces
462
+ all literals of the sum to be *false*.
463
+
464
+ An :class:`ITotalizer` object should be deleted if it is not needed
465
+ anymore.
466
+
467
+ Possible usage of the class is shown below:
468
+
469
+ .. code-block:: python
470
+
471
+ >>> from pysat.card import ITotalizer
472
+ >>> t = ITotalizer(lits=[1, 2, 3], ubound=1)
473
+ >>> print(t.cnf.clauses)
474
+ [[-2, 4], [-1, 4], [-1, -2, 5], [-4, 6], [-5, 7], [-3, 6], [-3, -4, 7]]
475
+ >>> print(t.rhs)
476
+ [6, 7]
477
+ >>> t.delete()
478
+
479
+ Alternatively, an object can be created using the ``with`` keyword. In
480
+ this case, the object is deleted automatically:
481
+
482
+ .. code-block:: python
483
+
484
+ >>> from pysat.card import ITotalizer
485
+ >>> with ITotalizer(lits=[1, 2, 3], ubound=1) as t:
486
+ ... print(t.cnf.clauses)
487
+ [[-2, 4], [-1, 4], [-1, -2, 5], [-4, 6], [-5, 7], [-3, 6], [-3, -4, 7]]
488
+ ... print(t.rhs)
489
+ [6, 7]
490
+ """
491
+
492
+ def __init__(self, lits=[], ubound=1, top_id=None):
493
+ """
494
+ Constructor.
495
+ """
496
+
497
+ # internal totalizer object
498
+ self.tobj = None
499
+
500
+ # its characteristics
501
+ self.lits = []
502
+ self.ubound = 0
503
+ self.top_id = 0
504
+
505
+ # encoding result
506
+ self.cnf = CNF() # CNF formula encoding the totalizer object
507
+ self.rhs = [] # upper bounds on the number of literals (rhs)
508
+
509
+ # number of new clauses
510
+ self.nof_new = 0
511
+
512
+ # this newly created totalizer object is not yet merged in any other
513
+ self._merged = False
514
+
515
+ if lits:
516
+ self.new(lits=lits, ubound=ubound, top_id=top_id)
517
+
518
+ def new(self, lits=[], ubound=1, top_id=None):
519
+ """
520
+ The actual constructor of :class:`ITotalizer`. Invoked from
521
+ ``self.__init__()``. Creates an object of :class:`ITotalizer` given
522
+ a list of literals in the sum, the largest potential bound to
523
+ consider, as well as the top variable identifier used so far. See
524
+ the description of :class:`ITotalizer` for details.
525
+ """
526
+
527
+ self.lits = list(lits)
528
+ self.ubound = ubound
529
+ self.top_id = max(map(lambda x: abs(x), self.lits + [top_id if top_id != None else 0]))
530
+
531
+ # creating the object
532
+ self.tobj, clauses, self.rhs, self.top_id = pycard.itot_new(self.lits,
533
+ self.ubound, self.top_id, int(MainThread.check()))
534
+
535
+ # saving the result
536
+ self.cnf.clauses = clauses
537
+ self.cnf.nv = self.top_id
538
+
539
+ # for convenience, keeping the number of clauses
540
+ self.nof_new = len(clauses)
541
+
542
+ def delete(self):
543
+ """
544
+ Destroys a previously constructed :class:`ITotalizer` object.
545
+ Internal variables ``self.cnf`` and ``self.rhs`` get cleaned.
546
+ """
547
+
548
+ if self.tobj:
549
+ if not self._merged:
550
+ pycard.itot_del(self.tobj)
551
+
552
+ # otherwise, this totalizer object is merged into a larger one
553
+ # therefore, this memory should be freed in its destructor
554
+
555
+ self.tobj = None
556
+
557
+ self.lits = []
558
+ self.ubound = 0
559
+ self.top_id = 0
560
+
561
+ self.cnf = CNF()
562
+ self.rhs = []
563
+
564
+ self.nof_new = 0
565
+
566
+ def __enter__(self):
567
+ """
568
+ 'with' constructor.
569
+ """
570
+
571
+ return self
572
+
573
+ def __exit__(self, exc_type, exc_value, traceback):
574
+ """
575
+ 'with' destructor.
576
+ """
577
+
578
+ self.delete()
579
+
580
+ def __del__(self):
581
+ """
582
+ Destructor.
583
+ """
584
+
585
+ self.delete()
586
+
587
+ def increase(self, ubound=1, top_id=None):
588
+ """
589
+ Increases a potential upper bound that can be imposed on the
590
+ literals in the sum of an existing :class:`ITotalizer` object to a
591
+ new value.
592
+
593
+ :param ubound: a new upper bound.
594
+ :param top_id: a new top variable identifier.
595
+
596
+ :type ubound: int
597
+ :type top_id: integer or None
598
+
599
+ The top identifier ``top_id`` applied only if it is greater than
600
+ the one used in ``self``.
601
+
602
+ This method creates additional clauses encoding the existing
603
+ totalizer tree up to the new upper bound given and appends them to
604
+ the list of clauses of :class:`.CNF` ``self.cnf``. The number of
605
+ newly created clauses is stored in variable ``self.nof_new``.
606
+
607
+ Also, a list of bounds ``self.rhs`` gets increased and its length
608
+ becomes ``ubound+1``.
609
+
610
+ The method can be used in the following way:
611
+
612
+ .. code-block:: python
613
+
614
+ >>> from pysat.card import ITotalizer
615
+ >>> t = ITotalizer(lits=[1, 2, 3], ubound=1)
616
+ >>> print(t.cnf.clauses)
617
+ [[-2, 4], [-1, 4], [-1, -2, 5], [-4, 6], [-5, 7], [-3, 6], [-3, -4, 7]]
618
+ >>> print(t.rhs)
619
+ [6, 7]
620
+ >>>
621
+ >>> t.increase(ubound=2)
622
+ >>> print(t.cnf.clauses)
623
+ [[-2, 4], [-1, 4], [-1, -2, 5], [-4, 6], [-5, 7], [-3, 6], [-3, -4, 7], [-3, -5, 8]]
624
+ >>> print(t.cnf.clauses[-t.nof_new:])
625
+ [[-3, -5, 8]]
626
+ >>> print(t.rhs)
627
+ [6, 7, 8]
628
+ >>> t.delete()
629
+ """
630
+
631
+ self.top_id = max(self.top_id, top_id if top_id != None else 0)
632
+
633
+ # do nothing if the bound is set incorrectly
634
+ if ubound <= self.ubound or self.ubound >= len(self.lits):
635
+ self.nof_new = 0
636
+ return
637
+ else:
638
+ self.ubound = ubound
639
+
640
+ # updating the object and adding more variables and clauses
641
+ clauses, self.rhs, self.top_id = pycard.itot_inc(self.tobj,
642
+ self.ubound, self.top_id, int(MainThread.check()))
643
+
644
+ # saving the result
645
+ self.cnf.clauses.extend(clauses)
646
+ self.cnf.nv = self.top_id
647
+
648
+ # keeping the number of newly added clauses
649
+ self.nof_new = len(clauses)
650
+
651
+ def extend(self, lits=[], ubound=None, top_id=None):
652
+ """
653
+ Extends the list of literals in the sum and (if needed) increases a
654
+ potential upper bound that can be imposed on the complete list of
655
+ literals in the sum of an existing :class:`ITotalizer` object to a
656
+ new value.
657
+
658
+ :param lits: additional literals to be included in the sum.
659
+ :param ubound: a new upper bound.
660
+ :param top_id: a new top variable identifier.
661
+
662
+ :type lits: iterable(int)
663
+ :type ubound: int
664
+ :type top_id: integer or None
665
+
666
+ The top identifier ``top_id`` applied only if it is greater than
667
+ the one used in ``self``.
668
+
669
+ This method creates additional clauses encoding the existing
670
+ totalizer tree augmented with new literals in the sum and updating
671
+ the upper bound. As a result, it appends the new clauses to the
672
+ list of clauses of :class:`.CNF` ``self.cnf``. The number of newly
673
+ created clauses is stored in variable ``self.nof_new``.
674
+
675
+ Also, if the upper bound is updated, a list of bounds ``self.rhs``
676
+ gets increased and its length becomes ``ubound+1``. Otherwise, it
677
+ is updated with new values.
678
+
679
+ The method can be used in the following way:
680
+
681
+ .. code-block:: python
682
+
683
+ >>> from pysat.card import ITotalizer
684
+ >>> t = ITotalizer(lits=[1, 2], ubound=1)
685
+ >>> print(t.cnf.clauses)
686
+ [[-2, 3], [-1, 3], [-1, -2, 4]]
687
+ >>> print(t.rhs)
688
+ [3, 4]
689
+ >>>
690
+ >>> t.extend(lits=[5], ubound=2)
691
+ >>> print(t.cnf.clauses)
692
+ [[-2, 3], [-1, 3], [-1, -2, 4], [-5, 6], [-3, 6], [-4, 7], [-3, -5, 7], [-4, -5, 8]]
693
+ >>> print(t.cnf.clauses[-t.nof_new:])
694
+ [[-5, 6], [-3, 6], [-4, 7], [-3, -5, 7], [-4, -5, 8]]
695
+ >>> print(t.rhs)
696
+ [6, 7, 8]
697
+ >>> t.delete()
698
+ """
699
+
700
+ # preparing a new list of distinct input literals
701
+ lits = sorted(set(lits).difference(set(self.lits)))
702
+
703
+ if not lits:
704
+ # nothing to merge with -> just increase the bound
705
+ if ubound:
706
+ self.increase(ubound=ubound, top_id=top_id)
707
+
708
+ return
709
+
710
+ self.top_id = max(map(lambda x: abs(x), self.lits + [self.top_id, top_id if top_id != None else 0]))
711
+ self.ubound = max(self.ubound, ubound if ubound != None else 0)
712
+
713
+ # updating the object and adding more variables and clauses
714
+ self.tobj, clauses, self.rhs, self.top_id = pycard.itot_ext(self.tobj,
715
+ lits, self.ubound, self.top_id, int(MainThread.check()))
716
+
717
+ # saving the result
718
+ self.cnf.clauses.extend(clauses)
719
+ self.cnf.nv = self.top_id
720
+ self.lits.extend(lits)
721
+
722
+ # for convenience, keeping the number of new clauses
723
+ self.nof_new = len(clauses)
724
+
725
+ def merge_with(self, another, ubound=None, top_id=None):
726
+ """
727
+ This method merges a tree of the current :class:`ITotalizer`
728
+ object, with a tree of another object and (if needed) increases a
729
+ potential upper bound that can be imposed on the complete list of
730
+ literals in the sum of an existing :class:`ITotalizer` object to a
731
+ new value.
732
+
733
+ :param another: another totalizer to merge with.
734
+ :param ubound: a new upper bound.
735
+ :param top_id: a new top variable identifier.
736
+
737
+ :type another: :class:`ITotalizer`
738
+ :type ubound: int
739
+ :type top_id: integer or None
740
+
741
+ The top identifier ``top_id`` applied only if it is greater than
742
+ the one used in ``self``.
743
+
744
+ This method creates additional clauses encoding the existing
745
+ totalizer tree merged with another totalizer tree into *one* sum
746
+ and updating the upper bound. As a result, it appends the new
747
+ clauses to the list of clauses of :class:`.CNF` ``self.cnf``. The
748
+ number of newly created clauses is stored in variable
749
+ ``self.nof_new``.
750
+
751
+ Also, if the upper bound is updated, a list of bounds ``self.rhs``
752
+ gets increased and its length becomes ``ubound+1``. Otherwise, it
753
+ is updated with new values.
754
+
755
+ The method can be used in the following way:
756
+
757
+ .. code-block:: python
758
+
759
+ >>> from pysat.card import ITotalizer
760
+ >>> with ITotalizer(lits=[1, 2], ubound=1) as t1:
761
+ ... print(t1.cnf.clauses)
762
+ [[-2, 3], [-1, 3], [-1, -2, 4]]
763
+ ... print(t1.rhs)
764
+ [3, 4]
765
+ ...
766
+ ... t2 = ITotalizer(lits=[5, 6], ubound=1)
767
+ ... print(t1.cnf.clauses)
768
+ [[-6, 7], [-5, 7], [-5, -6, 8]]
769
+ ... print(t1.rhs)
770
+ [7, 8]
771
+ ...
772
+ ... t1.merge_with(t2)
773
+ ... print(t1.cnf.clauses)
774
+ [[-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]]
775
+ ... print(t1.cnf.clauses[-t1.nof_new:])
776
+ [[-6, 7], [-5, 7], [-5, -6, 8], [-7, 9], [-8, 10], [-3, 9], [-4, 10], [-3, -7, 10]]
777
+ ... print(t1.rhs)
778
+ [9, 10]
779
+ ...
780
+ ... t2.delete()
781
+ """
782
+
783
+ self.top_id = max(self.top_id, top_id if top_id != None else 0, another.top_id)
784
+ self.ubound = max(self.ubound, ubound if ubound != None else 0, another.ubound)
785
+
786
+ # extending the list of input literals
787
+ self.lits.extend(another.lits)
788
+
789
+ # updating the object and adding more variables and clauses
790
+ self.tobj, clauses, self.rhs, self.top_id = pycard.itot_mrg(self.tobj,
791
+ another.tobj, self.ubound, self.top_id, int(MainThread.check()))
792
+
793
+ # saving the result
794
+ self.cnf.clauses.extend(another.cnf.clauses)
795
+ self.cnf.clauses.extend(clauses)
796
+ self.cnf.nv = self.top_id
797
+
798
+ # for convenience, keeping the number of new clauses
799
+ self.nof_new = len(another.cnf.clauses) + len(clauses)
800
+
801
+ # memory deallocation should not be done for the merged tree
802
+ another._merged = True