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
@@ -0,0 +1,496 @@
1
+ #!python
2
+ #-*- coding:utf-8 -*-
3
+ ##
4
+ ## lsu.py
5
+ ##
6
+ ## Created on: Aug 29, 2018
7
+ ## Author: Miguel Neves
8
+ ## E-mail: neves@sat.inesc-id.pt
9
+ ##
10
+
11
+ """
12
+ ===============
13
+ List of classes
14
+ ===============
15
+
16
+ .. autosummary::
17
+ :nosignatures:
18
+
19
+ LSU
20
+ LSUPlus
21
+
22
+ ==================
23
+ Module description
24
+ ==================
25
+
26
+ The module implements a prototype of the known *LSU/LSUS*, e.g. *linear
27
+ (search) SAT-UNSAT*, algorithm for MaxSAT, e.g. see [1]_. The
28
+ implementation is improved by the use of the *iterative totalizer encoding*
29
+ [2]_. The encoding is used in an incremental fashion, i.e. it is created
30
+ once and reused as many times as the number of iterations the algorithm
31
+ makes.
32
+
33
+ .. [1] António Morgado, Federico Heras, Mark H. Liffiton, Jordi Planes,
34
+ Joao Marques-Silva. *Iterative and core-guided MaxSAT solving: A
35
+ survey and assessment*. Constraints 18(4). 2013. pp. 478-534
36
+
37
+ .. [2] Ruben Martins, Saurabh Joshi, Vasco M. Manquinho, Inês Lynce.
38
+ *Incremental Cardinality Constraints for MaxSAT*. CP 2014. pp. 531-548
39
+
40
+ The implementation can be used as an executable (the list of available
41
+ command-line options can be shown using ``lsu.py -h``) in the following
42
+ way:
43
+
44
+ ::
45
+
46
+ $ xzcat formula.wcnf.xz
47
+ p wcnf 3 6 4
48
+ 1 1 0
49
+ 1 2 0
50
+ 1 3 0
51
+ 4 -1 -2 0
52
+ 4 -1 -3 0
53
+ 4 -2 -3 0
54
+
55
+ $ lsu.py -s glucose3 -m -v formula.wcnf.xz
56
+ c formula: 3 vars, 3 hard, 3 soft
57
+ o 2
58
+ s OPTIMUM FOUND
59
+ v -1 -2 3 0
60
+ c oracle time: 0.0000
61
+
62
+ Alternatively, the algorithm can be accessed and invoked through the
63
+ standard ``import`` interface of Python, e.g.
64
+
65
+ .. code-block:: python
66
+
67
+ >>> from pysat.examples.lsu import LSU
68
+ >>> from pysat.formula import WCNF
69
+ >>>
70
+ >>> wcnf = WCNF(from_file='formula.wcnf.xz')
71
+ >>>
72
+ >>> lsu = LSU(wcnf, verbose=0)
73
+ >>> lsu.solve() # set of hard clauses should be satisfiable
74
+ True
75
+ >>> print(lsu.cost) # cost of MaxSAT solution should be 2
76
+ >>> 2
77
+ >>> print(lsu.model)
78
+ [-1, -2, 3]
79
+
80
+ ==============
81
+ Module details
82
+ ==============
83
+ """
84
+
85
+ #
86
+ #==============================================================================
87
+ from __future__ import print_function
88
+ import getopt
89
+ from pysat.card import ITotalizer
90
+ from pysat.formula import CNF, WCNF, WCNFPlus
91
+ from pysat.solvers import Solver, SolverNames
92
+ from threading import Timer
93
+ import os
94
+ import sys
95
+ import re
96
+
97
+
98
+ # TODO: support weighted MaxSAT
99
+ #==============================================================================
100
+ class LSU:
101
+ """
102
+ Linear SAT-UNSAT algorithm for MaxSAT [1]_. The algorithm can be seen
103
+ as a series of satisfiability oracle calls refining an upper bound on
104
+ the MaxSAT cost, followed by one unsatisfiability call, which stops the
105
+ algorithm. The implementation encodes the sum of all selector literals
106
+ using the *iterative totalizer encoding* [2]_. At every iteration, the
107
+ upper bound on the cost is reduced and enforced by adding the
108
+ corresponding unit size clause to the working formula. No clauses are
109
+ removed during the execution of the algorithm. As a result, the SAT
110
+ oracle is used incrementally.
111
+
112
+ .. warning:: At this point, :class:`LSU` supports only
113
+ **unweighted** problems.
114
+
115
+ The constructor receives an input :class:`.WCNF` formula, a name of the
116
+ SAT solver to use (see :class:`.SolverNames` for details), and an
117
+ integer verbosity level.
118
+
119
+ :param formula: input MaxSAT formula
120
+ :param solver: name of SAT solver
121
+ :param incr: enable incremental mode of Glucose
122
+ :param expect_interrupt: whether or not an :meth:`interrupt` call is expected
123
+ :param verbose: verbosity level
124
+
125
+ :type formula: :class:`.WCNF`
126
+ :type solver: str
127
+ :type incr: bool
128
+ :type expect_interrupt: bool
129
+ :type verbose: int
130
+ """
131
+
132
+ def __init__(self, formula, solver='g4', incr=False, expect_interrupt=False, verbose=0):
133
+ """
134
+ Constructor.
135
+ """
136
+
137
+ self.verbose = verbose
138
+ self.solver = solver
139
+ self.incr = incr
140
+ self.expect_interrupt = expect_interrupt
141
+ self.formula = formula
142
+ self.topv = formula.nv # largest variable index
143
+ self.sels = [] # soft clause selector variables
144
+ self.tot = None # totalizer encoder for the cardinality constraint
145
+ self._init(formula) # initiaize SAT oracle
146
+
147
+ def _init(self, formula):
148
+ """
149
+ SAT oracle initialization. The method creates a new SAT oracle and
150
+ feeds it with the formula's hard clauses. Afterwards, all soft
151
+ clauses of the formula are augmented with selector literals and
152
+ also added to the solver. The list of all introduced selectors is
153
+ stored in variable ``self.sels``.
154
+
155
+ :param formula: input MaxSAT formula
156
+ :type formula: :class:`WCNF`
157
+ """
158
+
159
+ self.oracle = Solver(name=self.solver, bootstrap_with=formula.hard,
160
+ incr=self.incr, use_timer=True)
161
+
162
+ for i, cl in enumerate(formula.soft):
163
+ # TODO: if clause is unit, use its literal as selector
164
+ # (ITotalizer must be extended to support PB constraints first)
165
+ self.topv += 1
166
+ selv = self.topv
167
+ cl.append(self.topv)
168
+ self.oracle.add_clause(cl)
169
+ self.sels.append(selv)
170
+
171
+ if self.verbose > 1:
172
+ print('c formula: {0} vars, {1} hard, {2} soft'.format(formula.nv, len(formula.hard), len(formula.soft)))
173
+
174
+ def __del__(self):
175
+ """
176
+ Destructor.
177
+ """
178
+
179
+ self.delete()
180
+
181
+ def __enter__(self):
182
+ """
183
+ 'with' constructor.
184
+ """
185
+
186
+ return self
187
+
188
+ def __exit__(self, exc_type, exc_value, traceback):
189
+ """
190
+ 'with' destructor.
191
+ """
192
+
193
+ self.delete()
194
+
195
+ def delete(self):
196
+ """
197
+ Explicit destructor of the internal SAT oracle and the
198
+ :class:`.ITotalizer` object.
199
+ """
200
+
201
+ if self.oracle:
202
+ self.oracle.delete()
203
+ self.oracle = None
204
+
205
+ if self.tot:
206
+ self.tot.delete()
207
+ self.tot = None
208
+
209
+ def solve(self):
210
+ """
211
+ Computes a solution to the MaxSAT problem. The method implements
212
+ the LSU/LSUS algorithm, i.e. it represents a loop, each iteration
213
+ of which calls a SAT oracle on the working MaxSAT formula and
214
+ refines the upper bound on the MaxSAT cost until the formula
215
+ becomes unsatisfiable.
216
+
217
+ Returns ``True`` if the hard part of the MaxSAT formula is
218
+ satisfiable, i.e. if there is a MaxSAT solution, and ``False``
219
+ otherwise.
220
+
221
+ :rtype: bool
222
+ """
223
+
224
+ is_sat = False
225
+
226
+ while self.oracle.solve_limited(expect_interrupt=self.expect_interrupt):
227
+ is_sat = True
228
+ self.model = self.oracle.get_model()
229
+ self.cost = self._get_model_cost(self.formula, self.model)
230
+ if self.verbose:
231
+ print('o {0}'.format(self.cost))
232
+ sys.stdout.flush()
233
+ if self.cost == 0: # if cost is 0, then model is an optimum solution
234
+ break
235
+ self._assert_lt(self.cost)
236
+
237
+ if is_sat:
238
+ self.model = filter(lambda l: abs(l) <= self.formula.nv, self.model)
239
+ if self.verbose:
240
+ if self.found_optimum():
241
+ print('s OPTIMUM FOUND')
242
+ else:
243
+ print('s SATISFIABLE')
244
+ elif self.verbose:
245
+ print('s UNSATISFIABLE')
246
+
247
+ return is_sat
248
+
249
+ def get_model(self):
250
+ """
251
+ This method returns a model obtained during a prior satisfiability
252
+ oracle call made in :func:`solve`.
253
+
254
+ :rtype: list(int)
255
+ """
256
+
257
+ return self.model
258
+
259
+ def found_optimum(self):
260
+ """
261
+ Checks if the optimum solution was found in a prior call to
262
+ :func:`solve`.
263
+
264
+ :rtype: bool
265
+ """
266
+
267
+ return self.oracle.get_status() is not None
268
+
269
+ def _get_model_cost(self, formula, model):
270
+ """
271
+ Given a WCNF formula and a model, the method computes the MaxSAT
272
+ cost of the model, i.e. the sum of weights of soft clauses that are
273
+ unsatisfied by the model.
274
+
275
+ :param formula: an input MaxSAT formula
276
+ :param model: a satisfying assignment
277
+
278
+ :type formula: :class:`.WCNF`
279
+ :type model: list(int)
280
+
281
+ :rtype: int
282
+ """
283
+
284
+ model_set = set(model)
285
+ cost = 0
286
+
287
+ for i, cl in enumerate(formula.soft):
288
+ cost += formula.wght[i] if all(l not in model_set for l in filter(lambda l: abs(l) <= self.formula.nv, cl)) else 0
289
+
290
+ return cost
291
+
292
+ def _assert_lt(self, cost):
293
+ """
294
+ The method enforces an upper bound on the cost of the MaxSAT
295
+ solution. This is done by encoding the sum of all soft clause
296
+ selectors with the use the iterative totalizer encoding, i.e.
297
+ :class:`.ITotalizer`. Note that the sum is created once, at the
298
+ beginning. Each of the following calls to this method only enforces
299
+ the upper bound on the created sum by adding the corresponding unit
300
+ size clause. Each such clause is added on the fly with no restart
301
+ of the underlying SAT oracle.
302
+
303
+ :param cost: the cost of the next MaxSAT solution is enforced to be
304
+ *lower* than this current cost
305
+
306
+ :type cost: int
307
+ """
308
+
309
+ if self.tot == None:
310
+ self.tot = ITotalizer(lits=self.sels, ubound=cost-1, top_id=self.topv)
311
+ self.topv = self.tot.top_id
312
+
313
+ for cl in self.tot.cnf.clauses:
314
+ self.oracle.add_clause(cl)
315
+
316
+ self.oracle.add_clause([-self.tot.rhs[cost-1]])
317
+
318
+ def interrupt(self):
319
+ """
320
+ Interrupt the current execution of LSU's :meth:`solve` method.
321
+ Can be used to enforce time limits using timer objects. The
322
+ interrupt must be cleared before running the LSU algorithm again
323
+ (see :meth:`clear_interrupt`).
324
+ """
325
+
326
+ self.oracle.interrupt()
327
+
328
+ def clear_interrupt(self):
329
+ """
330
+ Clears an interruption.
331
+ """
332
+
333
+ self.oracle.clear_interrupt()
334
+
335
+ def oracle_time(self):
336
+ """
337
+ Method for calculating and reporting the total SAT solving time.
338
+ """
339
+
340
+ return self.oracle.time_accum()
341
+
342
+
343
+ #
344
+ #==============================================================================
345
+ class LSUPlus(LSU, object):
346
+ """
347
+ LSU-like algorithm extended for :class:`.WCNFPlus` formulas (using
348
+ :class:`.Minicard`).
349
+
350
+ :param formula: input MaxSAT formula in WCNF+ format
351
+ :param expect_interrupt: whether or not an :meth:`interrupt` call is expected
352
+ :param verbose: verbosity level
353
+
354
+ :type formula: :class:`.WCNFPlus`
355
+ :type expect_interrupt: bool
356
+ :type verbose: int
357
+ """
358
+
359
+ def __init__(self, formula, solver='g4', incr=False, expect_interrupt=False, verbose=0):
360
+ """
361
+ Constructor.
362
+ """
363
+
364
+ assert solver in SolverNames.gluecard3 or \
365
+ solver in SolverNames.gluecard4 or \
366
+ solver in SolverNames.minicard or \
367
+ solver in SolverNames.cadical195, '{0} does not support native cardinality constraints'.format(solver)
368
+
369
+ super(LSUPlus, self).__init__(formula, solver=solver, incr=incr,
370
+ expect_interrupt=expect_interrupt, verbose=verbose)
371
+
372
+ # we are using CaDiCaL195 and it can use external linear engine
373
+ if solver in SolverNames.cadical195:
374
+ self.oracle.activate_atmost()
375
+
376
+ # adding atmost constraints
377
+ for am in formula.atms:
378
+ self.oracle.add_atmost(*am)
379
+
380
+ def _assert_lt(self, cost):
381
+ """
382
+ Overrides _assert_lt of :class:`.LSU` in order to use Minicard's
383
+ native support for cardinality constraints
384
+
385
+ :param cost: the cost of the next MaxSAT solution is enforced to
386
+ be *lower* than this current cost
387
+
388
+ :type cost: int
389
+ """
390
+
391
+ self.oracle.add_atmost(self.sels, cost-1)
392
+
393
+
394
+ #
395
+ #==============================================================================
396
+ def parse_options():
397
+ """
398
+ Parses command-line options.
399
+ """
400
+
401
+ try:
402
+ opts, args = getopt.getopt(sys.argv[1:], 'hims:t:v', ['help', 'incr', 'model', 'solver=', 'timeout=', 'verbose'])
403
+ except getopt.GetoptError as err:
404
+ sys.stderr.write(str(err).capitalize())
405
+ print_usage()
406
+ sys.exit(1)
407
+
408
+ solver = 'g4'
409
+ incr = False
410
+ verbose = 1
411
+ print_model = False
412
+ timeout = None
413
+
414
+ for opt, arg in opts:
415
+ if opt in ('-h', '--help'):
416
+ print_usage()
417
+ sys.exit(0)
418
+ elif opt in ('-i', '--incr'):
419
+ incr = True
420
+ elif opt in ('-m', '--model'):
421
+ print_model = True
422
+ elif opt in ('-s', '--solver'):
423
+ solver = str(arg)
424
+ elif opt in ('-t', '--timeout'):
425
+ if str(arg) != 'none':
426
+ timeout = float(arg)
427
+ elif opt in ('-v', '--verbose'):
428
+ verbose += 1
429
+ else:
430
+ assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
431
+
432
+ return incr, print_model, solver, timeout, verbose, args
433
+
434
+
435
+ #
436
+ #==============================================================================
437
+ def print_usage():
438
+ """
439
+ Prints usage message.
440
+ """
441
+
442
+ print('Usage: ' + os.path.basename(sys.argv[0]) + ' [options] dimacs-file')
443
+ print('Options:')
444
+ print(' -h, --help Show this message')
445
+ print(' -i, --incr Enable incremental model (for Glucose only)')
446
+ print(' -m, --model Print model')
447
+ print(' -s, --solver=<string> SAT solver to use')
448
+ print(' Available values: cd15, cd19, g3, g4, mc, m22, mgh (default = g4)')
449
+ print(' -t, --timeout=<float> Set time limit for MaxSAT solver')
450
+ print(' Available values: [0 .. FLOAT_MAX], none (default: none)')
451
+ print(' -v, --verbose Be verbose')
452
+
453
+
454
+ #
455
+ #==============================================================================
456
+ if __name__ == '__main__':
457
+ incr, print_model, solver, timeout, verbose, files = parse_options()
458
+
459
+ if files:
460
+ # reading standard CNF or WCNF
461
+ if re.search(r'cnf(\.(gz|bz2|lzma|xz|zst))?$', files[0]):
462
+ if re.search(r'\.wcnf(\.(gz|bz2|lzma|xz|zst))?$', files[0]):
463
+ formula = WCNF(from_file=files[0])
464
+ else: # expecting '*.cnf'
465
+ formula = CNF(from_file=files[0]).weighted()
466
+
467
+ lsu = LSU(formula, solver=solver, incr=incr,
468
+ expect_interrupt=(timeout != None), verbose=verbose)
469
+
470
+ # reading WCNF+
471
+ elif re.search(r'\.wcnf[p,+](\.(gz|bz2|lzma|xz|zst))?$', files[0]):
472
+ formula = WCNFPlus(from_file=files[0])
473
+ lsu = LSUPlus(formula, solver=solver, incr=incr,
474
+ expect_interrupt=(timeout != None), verbose=verbose)
475
+ else:
476
+ assert False, 'Unknown input file extension'
477
+
478
+ # setting a timer if necessary
479
+ if timeout is not None:
480
+ if verbose > 1:
481
+ print('c timeout: {0}'.format(timeout))
482
+
483
+ timer = Timer(timeout, lambda s: s.interrupt(), [lsu])
484
+ timer.start()
485
+
486
+ if lsu.solve():
487
+ if print_model:
488
+ print('v ' + ' '.join([str(l) for l in lsu.get_model()]), '0')
489
+
490
+ if verbose > 1:
491
+ print('c oracle time: {0:.4f}'.format(lsu.oracle_time()))
492
+
493
+ if timeout is not None:
494
+ timer.cancel()
495
+ else:
496
+ print_usage()