python-sat 1.8.dev25__cp310-cp310-musllinux_1_2_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.
Files changed (50) hide show
  1. pycard.cpython-310-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-310-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 +50 -0
  46. python_sat-1.8.dev25.dist-info/WHEEL +5 -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
  49. python_sat.libs/libgcc_s-0cd532bd.so.1 +0 -0
  50. python_sat.libs/libstdc++-5d72f927.so.6.0.33 +0 -0
@@ -0,0 +1,527 @@
1
+ #!python
2
+ #-*- coding:utf-8 -*-
3
+ ##
4
+ ## fm.py
5
+ ##
6
+ ## Created on: Feb 5, 2018
7
+ ## Author: Antonio Morgado, Alexey Ignatiev
8
+ ## E-mail: {ajmorgado, aignatiev}@ciencias.ulisboa.pt
9
+ ##
10
+
11
+ """
12
+ ===============
13
+ List of classes
14
+ ===============
15
+
16
+ .. autosummary::
17
+ :nosignatures:
18
+
19
+ FM
20
+
21
+ ==================
22
+ Module description
23
+ ==================
24
+
25
+ This module implements a variant of the seminal core-guided MaxSAT
26
+ algorithm originally proposed by [1]_ and then improved and modified
27
+ further in [2]_ [3]_ [4]_ [5]_. Namely, the implementation follows the
28
+ WMSU1 variant [5]_ of the algorithm extended to the case of *weighted
29
+ partial* formulas.
30
+
31
+ .. [1] Zhaohui Fu, Sharad Malik. *On Solving the Partial MAX-SAT Problem*.
32
+ SAT 2006. pp. 252-265
33
+
34
+ .. [2] Joao Marques-Silva, Jordi Planes. *On Using Unsatisfiability for
35
+ Solving Maximum Satisfiability*. CoRR abs/0712.1097. 2007
36
+
37
+ .. [3] Joao Marques-Silva, Vasco M. Manquinho. *Towards More Effective
38
+ Unsatisfiability-Based Maximum Satisfiability Algorithms*. SAT 2008.
39
+ pp. 225-230
40
+
41
+ .. [4] Carlos Ansótegui, Maria Luisa Bonet, Jordi Levy. *Solving
42
+ (Weighted) Partial MaxSAT through Satisfiability Testing*. SAT 2009.
43
+ pp. 427-440
44
+
45
+ .. [5] Vasco M. Manquinho, Joao Marques Silva, Jordi Planes. *Algorithms
46
+ for Weighted Boolean Optimization*. SAT 2009. pp. 495-508
47
+
48
+ The implementation can be used as an executable (the list of available
49
+ command-line options can be shown using ``fm.py -h``) in the following way:
50
+
51
+ ::
52
+
53
+ $ xzcat formula.wcnf.xz
54
+ p wcnf 3 6 4
55
+ 1 1 0
56
+ 1 2 0
57
+ 1 3 0
58
+ 4 -1 -2 0
59
+ 4 -1 -3 0
60
+ 4 -2 -3 0
61
+
62
+ $ fm.py -c cardn -s glucose3 -vv formula.wcnf.xz
63
+ c cost: 1; core sz: 2
64
+ c cost: 2; core sz: 3
65
+ s OPTIMUM FOUND
66
+ o 2
67
+ v -1 -2 3 0
68
+ c oracle time: 0.0001
69
+
70
+ Alternatively, the algorithm can be accessed and invoked through the
71
+ standard ``import`` interface of Python, e.g.
72
+
73
+ .. code-block:: python
74
+
75
+ >>> from pysat.examples.fm import FM
76
+ >>> from pysat.formula import WCNF
77
+ >>>
78
+ >>> wcnf = WCNF(from_file='formula.wcnf.xz')
79
+ >>>
80
+ >>> fm = FM(wcnf, verbose=0)
81
+ >>> fm.compute() # set of hard clauses should be satisfiable
82
+ True
83
+ >>> print(fm.cost) # cost of MaxSAT solution should be 2
84
+ >>> 2
85
+ >>> print(fm.model)
86
+ [-1, -2, 3]
87
+
88
+ ==============
89
+ Module details
90
+ ==============
91
+ """
92
+
93
+ #
94
+ #==============================================================================
95
+ from __future__ import print_function
96
+ import copy
97
+ import getopt
98
+ import gzip
99
+ import os
100
+ from pysat.formula import CNFPlus, WCNFPlus
101
+ from pysat.card import CardEnc, EncType
102
+ from pysat.solvers import Solver, SolverNames
103
+ import re
104
+ from six.moves import range
105
+ import sys
106
+
107
+
108
+ # cardinality encodings
109
+ #==============================================================================
110
+ encmap = {
111
+ 'pw': EncType.pairwise,
112
+ 'bw': EncType.bitwise,
113
+ 'seqc': EncType.seqcounter,
114
+ 'cardn': EncType.cardnetwrk,
115
+ 'sortn': EncType.sortnetwrk,
116
+ 'ladder': EncType.ladder,
117
+ 'tot': EncType.totalizer,
118
+ 'mtot': EncType.mtotalizer,
119
+ 'kmtot': EncType.kmtotalizer,
120
+ 'native': EncType.native
121
+ }
122
+
123
+
124
+ #
125
+ #==============================================================================
126
+ class FM(object):
127
+ """
128
+ A non-incremental implementation of the FM (Fu&Malik, or WMSU1)
129
+ algorithm. The algorithm (see details in [5]_) is *core-guided*, i.e.
130
+ it solves maximum satisfiability with a series of unsatisfiability
131
+ oracle calls, each producing an unsatisfiable core. The clauses
132
+ involved in an unsatisfiable core are *relaxed* and a new
133
+ :math:`\\textsf{AtMost1}` constraint on the corresponding *relaxation
134
+ variables* is added to the formula. The process gets a bit more
135
+ sophisticated in the case of weighted formulas because of the *clause
136
+ weight splitting* technique.
137
+
138
+ The constructor of :class:`FM` objects receives a target :class:`.WCNF`
139
+ MaxSAT formula, an identifier of the cardinality encoding to use, a SAT
140
+ solver name, and a verbosity level. Note that the algorithm uses the
141
+ ``pairwise`` (see :class:`.card.EncType`) cardinality encoding by
142
+ default, while the default SAT solver is MiniSat22 (referred to as
143
+ ``'m22'``, see :class:`.SolverNames` for details). The default
144
+ verbosity level is ``1``.
145
+
146
+ :param formula: input MaxSAT formula
147
+ :param enc: cardinality encoding to use
148
+ :param solver: name of SAT solver
149
+ :param verbose: verbosity level
150
+
151
+ :type formula: :class:`.WCNF`
152
+ :type enc: int
153
+ :type solver: str
154
+ :type verbose: int
155
+ """
156
+
157
+ def __init__(self, formula, enc=EncType.pairwise, solver='m22', verbose=1):
158
+ """
159
+ Constructor.
160
+ """
161
+
162
+ # saving verbosity level
163
+ self.verbose = verbose
164
+ self.solver = solver
165
+ self.time = 0.0
166
+
167
+ # MaxSAT related stuff
168
+ self.topv = self.orig_nv = formula.nv
169
+ self.hard = copy.deepcopy(formula.hard)
170
+ self.soft = copy.deepcopy(formula.soft)
171
+ self.wght = formula.wght[:]
172
+ self.cenc = enc
173
+ self.cost = 0
174
+
175
+ if isinstance(formula, WCNFPlus) and formula.atms:
176
+ self.atm1 = copy.deepcopy(formula.atms)
177
+ else:
178
+ self.atm1 = None
179
+
180
+ # initialize SAT oracle with hard clauses only
181
+ self.init(with_soft=False)
182
+
183
+ def __enter__(self):
184
+ """
185
+ 'with' constructor.
186
+ """
187
+
188
+ return self
189
+
190
+ def __exit__(self, exc_type, exc_value, traceback):
191
+ """
192
+ 'with' destructor.
193
+ """
194
+
195
+ self.delete()
196
+
197
+ def init(self, with_soft=True):
198
+ """
199
+ The method for the SAT oracle initialization. Since the oracle is
200
+ is used non-incrementally, it is reinitialized at every iteration
201
+ of the MaxSAT algorithm (see :func:`reinit`). An input parameter
202
+ ``with_soft`` (``False`` by default) regulates whether or not the
203
+ formula's soft clauses are copied to the oracle.
204
+
205
+ :param with_soft: copy formula's soft clauses to the oracle or not
206
+ :type with_soft: bool
207
+ """
208
+
209
+ self.oracle = Solver(name=self.solver, bootstrap_with=self.hard, use_timer=True)
210
+
211
+ if self.atm1: # this check is needed at the beggining (before iteration 1)
212
+ # we are using CaDiCaL195 and it can use external linear engine
213
+ if self.solver in SolverNames.cadical195:
214
+ self.oracle.activate_atmost()
215
+
216
+ assert self.oracle.supports_atmost(), \
217
+ '{0} does not support native cardinality constraints. Make sure you use the right type of formula.'.format(solver_name)
218
+
219
+ # self.atm1 is not empty only in case of minicard
220
+ for am in self.atm1:
221
+ self.oracle.add_atmost(*am)
222
+
223
+ if with_soft:
224
+ for cl, cpy in zip(self.soft, self.scpy):
225
+ if cpy:
226
+ self.oracle.add_clause(cl)
227
+
228
+ def delete(self):
229
+ """
230
+ Explicit destructor of the internal SAT oracle.
231
+ """
232
+
233
+ if self.oracle:
234
+ self.time += self.oracle.time_accum() # keep SAT solving time
235
+
236
+ self.oracle.delete()
237
+ self.oracle = None
238
+
239
+ def reinit(self):
240
+ """
241
+ This method calls :func:`delete` and :func:`init` to reinitialize
242
+ the internal SAT oracle. This is done at every iteration of the
243
+ MaxSAT algorithm.
244
+ """
245
+
246
+ self.delete()
247
+ self.init();
248
+
249
+ def compute(self):
250
+ """
251
+ Compute a MaxSAT solution. First, the method checks whether or
252
+ not the set of hard clauses is satisfiable. If not, the method
253
+ returns ``False``. Otherwise, add soft clauses to the oracle and
254
+ call the MaxSAT algorithm (see :func:`_compute`).
255
+
256
+ Note that the soft clauses are added to the oracles after being
257
+ augmented with additional *selector* literals. The selectors
258
+ literals are then used as *assumptions* when calling the SAT oracle
259
+ and are needed for extracting unsatisfiable cores.
260
+ """
261
+
262
+ if self.oracle.solve():
263
+ # hard part is satisfiable
264
+ # create selectors and a mapping from selectors to clause ids
265
+ self.sels, self.vmap = [], {}
266
+ self.scpy = [True for cl in self.soft]
267
+
268
+ # adding soft clauses to oracle
269
+ for i in range(len(self.soft)):
270
+ self.topv += 1
271
+
272
+ self.soft[i].append(-self.topv)
273
+ self.sels.append(self.topv)
274
+ self.oracle.add_clause(self.soft[i])
275
+
276
+ self.vmap[self.topv] = i
277
+
278
+ self._compute()
279
+ return True
280
+ else:
281
+ return False
282
+
283
+ def _compute(self):
284
+ """
285
+ This method implements WMSU1 algorithm. The method is essentially a
286
+ loop, which at each iteration calls the SAT oracle to decide
287
+ whether the working formula is satisfiable. If it is, the method
288
+ derives a model (stored in variable ``self.model``) and returns.
289
+ Otherwise, a new unsatisfiable core of the formula is extracted
290
+ and processed (see :func:`treat_core`), and the algorithm proceeds.
291
+ """
292
+
293
+ while True:
294
+ if self.oracle.solve(assumptions=self.sels):
295
+ self.model = [l for l in self.oracle.get_model() if abs(l) <= self.orig_nv]
296
+ return
297
+ else:
298
+ self.treat_core()
299
+
300
+ if self.verbose > 1:
301
+ print('c cost: {0}; core sz: {1}'.format(self.cost, len(self.core)))
302
+
303
+ self.reinit()
304
+
305
+ def treat_core(self):
306
+ """
307
+ Now that the previous SAT call returned UNSAT, a new unsatisfiable
308
+ core should be extracted and relaxed. Core extraction is done
309
+ through a call to the :func:`pysat.solvers.Solver.get_core` method,
310
+ which returns a subset of the selector literals deemed responsible
311
+ for unsatisfiability.
312
+
313
+ After the core is extracted, its *minimum weight* ``minw`` is
314
+ computed, i.e. it is the minimum weight among the weights of all
315
+ soft clauses involved in the core (see [5]_). Note that the cost of
316
+ the MaxSAT solution is incremented by ``minw``.
317
+
318
+ Clauses that have weight larger than ``minw`` are split (see
319
+ :func:`split_core`). Afterwards, all clauses of the unsatisfiable
320
+ core are relaxed (see :func:`relax_core`).
321
+ """
322
+
323
+ # extracting the core
324
+ self.core = [self.vmap[sel] for sel in self.oracle.get_core()]
325
+ minw = min(map(lambda i: self.wght[i], self.core))
326
+
327
+ # updating the cost
328
+ self.cost += minw
329
+
330
+ # splitting clauses in the core if necessary
331
+ self.split_core(minw)
332
+
333
+ # relaxing clauses in the core and adding a new atmost1 constraint
334
+ self.relax_core()
335
+
336
+ def split_core(self, minw):
337
+ """
338
+ Split clauses in the core whenever necessary.
339
+
340
+ Given a list of soft clauses in an unsatisfiable core, the method
341
+ is used for splitting clauses whose weights are greater than the
342
+ minimum weight of the core, i.e. the ``minw`` value computed in
343
+ :func:`treat_core`. Each clause :math:`(c\\vee\\neg{s},w)`, s.t.
344
+ :math:`w>minw` and :math:`s` is its selector literal, is split into
345
+ clauses (1) clause :math:`(c\\vee\\neg{s}, minw)` and (2) a
346
+ residual clause :math:`(c\\vee\\neg{s}',w-minw)`. Note that the
347
+ residual clause has a fresh selector literal :math:`s'` different
348
+ from :math:`s`.
349
+
350
+ :param minw: minimum weight of the core
351
+ :type minw: int
352
+ """
353
+
354
+ for clid in self.core:
355
+ sel = self.sels[clid]
356
+
357
+ if self.wght[clid] > minw:
358
+ self.topv += 1
359
+
360
+ cl_new = []
361
+ for l in self.soft[clid]:
362
+ if l != -sel:
363
+ cl_new.append(l)
364
+ else:
365
+ cl_new.append(-self.topv)
366
+
367
+ self.sels.append(self.topv)
368
+ self.vmap[self.topv] = len(self.soft)
369
+
370
+ self.soft.append(cl_new)
371
+ self.wght.append(self.wght[clid] - minw)
372
+ self.wght[clid] = minw
373
+
374
+ self.scpy.append(True)
375
+
376
+ def relax_core(self):
377
+ """
378
+ Relax and bound the core.
379
+
380
+ After unsatisfiable core splitting, this method is called. If the
381
+ core contains only one clause, i.e. this clause cannot be satisfied
382
+ together with the hard clauses of the formula, the formula gets
383
+ augmented with the negation of the clause (see
384
+ :func:`remove_unit_core`).
385
+
386
+ Otherwise (if the core contains more than one clause), every clause
387
+ :math:`c` of the core is *relaxed*. This means a new *relaxation
388
+ literal* is added to the clause, i.e. :math:`c\\gets c\\vee r`,
389
+ where :math:`r` is a fresh (unused) relaxation variable. After the
390
+ clauses get relaxed, a new cardinality encoding is added to the
391
+ formula enforcing the sum of the new relaxation variables to be not
392
+ greater than 1, :math:`\\sum_{c\\in\\phi}{r\\leq 1}`, where
393
+ :math:`\\phi` denotes the unsatisfiable core.
394
+ """
395
+
396
+ if len(self.core) > 1:
397
+ # relaxing
398
+ rels = []
399
+
400
+ for clid in self.core:
401
+ self.topv += 1
402
+ rels.append(self.topv)
403
+ self.soft[clid].append(self.topv)
404
+
405
+ # creating a new cardinality constraint
406
+ am1 = CardEnc.atmost(lits=rels, top_id=self.topv, encoding=self.cenc)
407
+
408
+ for cl in am1.clauses:
409
+ self.hard.append(cl)
410
+
411
+ # only if minicard
412
+ # (for other solvers am1.atmosts should be empty)
413
+ for am in am1.atmosts:
414
+ self.atm1.append(am)
415
+
416
+ self.topv = am1.nv
417
+
418
+ elif len(self.core) == 1: # unit core => simply negate the clause
419
+ self.remove_unit_core()
420
+
421
+ def remove_unit_core(self):
422
+ """
423
+ If an unsatisfiable core contains only one clause :math:`c`, this
424
+ method is invoked to add a bunch of new unit size hard clauses. As
425
+ a result, the SAT oracle gets unit clauses :math:`(\\neg{l})` for
426
+ all literals :math:`l` in clause :math:`c`.
427
+ """
428
+
429
+ self.scpy[self.core[0]] = False
430
+
431
+ for l in self.soft[self.core[0]]:
432
+ self.hard.append([-l])
433
+
434
+ def oracle_time(self):
435
+ """
436
+ Method for calculating and reporting the total SAT solving time.
437
+ """
438
+
439
+ self.time += self.oracle.time_accum() # include time of the last SAT call
440
+ return self.time
441
+
442
+
443
+ #
444
+ #==============================================================================
445
+ def parse_options():
446
+ """
447
+ Parses command-line option
448
+ """
449
+
450
+ try:
451
+ opts, args = getopt.getopt(sys.argv[1:], 'hs:c:v', ['help','solver=','cardenc=','verbose'])
452
+ except getopt.GetoptError as err:
453
+ sys.stderr.write(str(err).capitalize())
454
+ usage()
455
+ sys.exit(1)
456
+
457
+ solver = 'm22'
458
+ cardenc = 'seqc'
459
+ verbose = 1
460
+
461
+ for opt, arg in opts:
462
+ if opt in ('-c', '--cardenc'):
463
+ cardenc = str(arg)
464
+ elif opt in ('-h', '--help'):
465
+ usage()
466
+ sys.exit(0)
467
+ elif opt in ('-s', '--solver'):
468
+ solver = str(arg)
469
+ elif opt in ('-v', '--verbose'):
470
+ verbose += 1
471
+ else:
472
+ assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
473
+
474
+ cardenc = encmap[cardenc]
475
+
476
+ # using minicard's native implementation of AtMost1 constraints
477
+ if solver in SolverNames.minicard + SolverNames.gluecard3 + \
478
+ SolverNames.gluecard4 + SolverNames.cadical195:
479
+ cardenc = encmap['native']
480
+ else:
481
+ assert cardenc != encmap['native'], 'Only Minicard can handle cardinality constraints natively'
482
+
483
+ return solver, cardenc, verbose, args
484
+
485
+
486
+ #==============================================================================
487
+ def usage():
488
+ """
489
+ Prints usage message.
490
+ """
491
+
492
+ print('Usage:', os.path.basename(sys.argv[0]), '[options] dimacs-file')
493
+ print('Options:')
494
+ print(' -c, --cardenc Cardinality encoding to use:')
495
+ print(' Available values: bw, cardn, kmtot, ladder, mtot, pw, seqc, sortn, tot (default = seqc)')
496
+ print(' -h, --help')
497
+ print(' -s, --solver SAT solver to use')
498
+ print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default = m22)')
499
+ print(' -v, --verbose Be verbose')
500
+
501
+
502
+ #
503
+ #==============================================================================
504
+ if __name__ == '__main__':
505
+ solver, cardenc, verbose, files = parse_options()
506
+
507
+ if files:
508
+ # parsing the input formula
509
+ if re.search(r'\.wcnf[p|+]?(\.(gz|bz2|lzma|xz|zst))?$', files[0]):
510
+ formula = WCNFPlus(from_file=files[0])
511
+ else: # expecting '*.cnf[,p,+].*'
512
+ formula = CNFPlus(from_file=files[0]).weighted()
513
+
514
+ with FM(formula, solver=solver, enc=cardenc, verbose=verbose) as fm:
515
+ res = fm.compute()
516
+
517
+ if res:
518
+ print('s OPTIMUM FOUND')
519
+ print('o {0}'.format(fm.cost))
520
+
521
+ if verbose > 2:
522
+ print('v', ' '.join([str(l) for l in fm.model]), '0')
523
+ else:
524
+ print('s UNSATISFIABLE')
525
+
526
+ if verbose > 1:
527
+ print('c oracle time: {0:.4f}'.format(fm.oracle_time()))