python-sat 1.8.dev15__cp313-cp313-win_amd64.whl

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

Potentially problematic release.


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

Files changed (42) hide show
  1. pycard.cp313-win_amd64.pyd +0 -0
  2. pysat/__init__.py +24 -0
  3. pysat/_fileio.py +193 -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/fm.py +528 -0
  12. pysat/examples/genhard.py +516 -0
  13. pysat/examples/hitman.py +643 -0
  14. pysat/examples/lbx.py +601 -0
  15. pysat/examples/lsu.py +494 -0
  16. pysat/examples/mcsls.py +576 -0
  17. pysat/examples/models.py +189 -0
  18. pysat/examples/musx.py +344 -0
  19. pysat/examples/optux.py +675 -0
  20. pysat/examples/rc2.py +1804 -0
  21. pysat/examples/usage.py +63 -0
  22. pysat/formula.py +5417 -0
  23. pysat/pb.py +442 -0
  24. pysat/process.py +349 -0
  25. pysat/solvers.py +7589 -0
  26. pysolvers.cp313-win_amd64.pyd +0 -0
  27. python_sat-1.8.dev15.data/scripts/approxmc.py +385 -0
  28. python_sat-1.8.dev15.data/scripts/fm.py +528 -0
  29. python_sat-1.8.dev15.data/scripts/genhard.py +516 -0
  30. python_sat-1.8.dev15.data/scripts/lbx.py +601 -0
  31. python_sat-1.8.dev15.data/scripts/lsu.py +494 -0
  32. python_sat-1.8.dev15.data/scripts/mcsls.py +576 -0
  33. python_sat-1.8.dev15.data/scripts/models.py +189 -0
  34. python_sat-1.8.dev15.data/scripts/musx.py +344 -0
  35. python_sat-1.8.dev15.data/scripts/optux.py +675 -0
  36. python_sat-1.8.dev15.data/scripts/rc2.py +1804 -0
  37. python_sat-1.8.dev15.data/scripts/unigen.py +435 -0
  38. python_sat-1.8.dev15.dist-info/LICENSE.txt +21 -0
  39. python_sat-1.8.dev15.dist-info/METADATA +44 -0
  40. python_sat-1.8.dev15.dist-info/RECORD +42 -0
  41. python_sat-1.8.dev15.dist-info/WHEEL +5 -0
  42. python_sat-1.8.dev15.dist-info/top_level.txt +3 -0
@@ -0,0 +1,675 @@
1
+ #!python
2
+ #-*- coding:utf-8 -*-
3
+ ##
4
+ ## optux.py
5
+ ##
6
+ ## Created on: Mar 22, 2021
7
+ ## Author: Alexey Ignatiev
8
+ ## E-mail: alexey.ignatiev@monash.edu
9
+ ##
10
+
11
+ """
12
+ ===============
13
+ List of classes
14
+ ===============
15
+
16
+ .. autosummary::
17
+ :nosignatures:
18
+
19
+ OptUx
20
+
21
+ ==================
22
+ Module description
23
+ ==================
24
+
25
+ An implementation of an extractor of a smallest size minimal unsatisfiable
26
+ subset (smallest MUS, or SMUS) [1]_ [2]_ [3]_ [4]_ and enumerator of
27
+ SMUSes based on *implicit hitting set enumeration* [1]_. This
28
+ implementation tries to replicate the well-known SMUS extractor Forqes
29
+ [1]_. In contrast to Forqes, this implementation supports not only plain
30
+ DIMACS :class:`.CNF` formulas but also weighted :class:`.WCNF` formulas.
31
+ As a result, the tool is able to compute and enumerate *optimal* MUSes in
32
+ case of weighted formulas. On the other hand, this prototype lacks a
33
+ number of command-line options used in Forqes and so it may be less
34
+ efficient compared to Forqes but the performance difference should not be
35
+ significant.
36
+
37
+ .. [1] Alexey Ignatiev, Alessandro Previti, Mark H. Liffiton, Joao
38
+ Marques-Silva. *Smallest MUS Extraction with Minimal Hitting Set
39
+ Dualization*. CP 2015. pp. 173-182
40
+
41
+ .. [2] Mark H. Liffiton, Maher N. Mneimneh, Ines Lynce, Zaher S. Andraus,
42
+ Joao Marques-Silva, Karem A. Sakallah. *A branch and bound algorithm
43
+ for extracting smallest minimal unsatisfiable subformulas*.
44
+ Constraints An Int. J. 14(4). 2009. pp. 415-442
45
+
46
+ .. [3] Alexey Ignatiev, Mikolas Janota, Joao Marques-Silva. *Quantified
47
+ Maximum Satisfiability: A Core-Guided Approach*. SAT 2013.
48
+ pp. 250-266
49
+
50
+ .. [4] Alexey Ignatiev, Mikolas Janota, Joao Marques-Silva. *Quantified
51
+ maximum satisfiability*. Constraints An Int. J. 21(2). 2016.
52
+ pp. 277-302
53
+
54
+ The file provides a class :class:`OptUx`, which is the basic
55
+ implementation of the algorithm. It can be applied to any formula in the
56
+ :class:`.CNF` or :class:`.WCNF` format.
57
+
58
+ The implementation can be used as an executable (the list of available
59
+ command-line options can be shown using ``optux.py -h``) in the following
60
+ way:
61
+
62
+ ::
63
+
64
+ $ xzcat formula.wcnf.xz
65
+ p wcnf 3 6 4
66
+ 1 1 0
67
+ 1 2 0
68
+ 1 3 0
69
+ 4 -1 -2 0
70
+ 4 -1 -3 0
71
+ 4 -2 -3 0
72
+
73
+ $ optux.py -vvv formula.wcnf.xz
74
+ c mcs: 1 2 0
75
+ c mcses: 0 unit, 1 disj
76
+ c mus: 1 2 0
77
+ c cost: 2
78
+ c oracle time: 0.0001
79
+
80
+ Alternatively, the algorithm can be accessed and invoked through the
81
+ standard ``import`` interface of Python, e.g.
82
+
83
+ .. code-block:: python
84
+
85
+ >>> from pysat.examples.optux import OptUx
86
+ >>> from pysat.formula import WCNF
87
+ >>>
88
+ >>> wcnf = WCNF(from_file='formula.wcnf.xz')
89
+ >>>
90
+ >>> with OptUx(wcnf) as optux:
91
+ ... for mus in optux.enumerate():
92
+ ... print('mus {0} has cost {1}'.format(mus, optux.cost))
93
+ mus [1, 2] has cost 2
94
+ mus [1, 3] has cost 2
95
+ mus [2, 3] has cost 2
96
+
97
+ As can be seen in the example above, the solver can be instructed either
98
+ to compute one optimal MUS of an input formula, or to enumerate a given
99
+ number (or *all*) of its top optimal MUSes.
100
+
101
+ ==============
102
+ Module details
103
+ ==============
104
+ """
105
+
106
+ #
107
+ #==============================================================================
108
+ from __future__ import print_function
109
+ import getopt
110
+ import os
111
+ from pysat.examples.hitman import Atom, Hitman
112
+ from pysat.examples.rc2 import RC2
113
+ from pysat.formula import CNFPlus, WCNFPlus
114
+ from pysat.solvers import Solver, SolverNames
115
+ import re
116
+ import sys
117
+
118
+
119
+ #
120
+ #==============================================================================
121
+ class OptUx(object):
122
+ """
123
+ A simple Python version of the implicit hitting set based optimal MUS
124
+ extractor and enumerator. Given a (weighted) (partial) CNF formula,
125
+ i.e. formula in the :class:`.WCNF` format, this class can be used to
126
+ compute a given number of optimal MUS (starting from the *best* one)
127
+ of the input formula. :class:`OptUx` roughly follows the
128
+ implementation of Forqes [1]_ but lacks a few additional heuristics,
129
+ which however aren't applied in Forqes by default.
130
+
131
+ As a result, OptUx applies exhaustive *disjoint* minimal correction
132
+ subset (MCS) enumeration [1]_, [2]_, [3]_, [4]_ with the incremental
133
+ use of RC2 [5]_ as an underlying MaxSAT solver. Once disjoint MCSes
134
+ are enumerated, they are used to bootstrap a hitting set solver. This
135
+ implementation uses :class:`.Hitman` as a hitting set solver, which is
136
+ again based on RC2.
137
+
138
+ Note that in the main implicit hitting enumeration loop of the
139
+ algorithm, OptUx follows Forqes in that it does not reduce correction
140
+ subsets detected to minimal correction subsets. As a result,
141
+ correction subsets computed in the main loop are added to
142
+ :class:`Hitman` *unreduced*.
143
+
144
+ :class:`OptUx` can use any SAT solver available in PySAT. The default
145
+ SAT solver to use is ``g3``, which stands for Glucose 3 [6]_ (see
146
+ :class:`.SolverNames`). Boolean parameters ``adapt``, ``exhaust``, and
147
+ ``minz`` control whether or not the underlying :class:`.RC2` oracles
148
+ should apply detection and adaptation of intrinsic AtMost1
149
+ constraints, core exhaustion, and core reduction. Also, unsatisfiable
150
+ cores can be trimmed if the ``trim`` parameter is set to a non-zero
151
+ integer. Finally, verbosity level can be set using the ``verbose``
152
+ parameter.
153
+
154
+ Two additional optional parameters ``unsorted`` and ``dcalls`` can be
155
+ used to instruct the tool to enumerate MUSes in the unsorted fashion,
156
+ i.e. optimal MUSes are not guaranteed to go first. For this,
157
+ :class:`OptUx` applies LBX-like MCS enumeration (it uses :class:`.LBX`
158
+ directly). Parameter ``dcalls`` can be applied to instruct the
159
+ underlying MCS enumerator to apply clause D oracle calls.
160
+
161
+ Another optional paramater ``puresat`` can be used to instruct OptUx
162
+ to run a purely SAT-based minimal hitting set enumerator, following
163
+ the ideas of [7]_. The value of ``puresat`` can be either ``False``,
164
+ meaning that no pure SAT enumeration is to be done or be equal to
165
+ ``'mgh'``, ``'cd15'``, or ``'lgl'`` - these are the solvers that
166
+ support *hard* phase setting, i.e. user preferences will not be
167
+ overwritten by the *phase saving* heuristic [8]_.
168
+
169
+ Finally, one more optional input parameter ``cover`` is to be used
170
+ when exhaustive enumeration of MUSes is not necessary and the tool can
171
+ stop as soon as a given formula is covered by the set of currently
172
+ computed MUSes. This can be made to work if the soft clauses of
173
+ ``formula`` are of size 1.
174
+
175
+ .. [5] Alexey Ignatiev, Antonio Morgado, Joao Marques-Silva. *RC2: an
176
+ Efficient MaxSAT Solver*. J. Satisf. Boolean Model. Comput. 11(1).
177
+ 2019. pp. 53-64
178
+
179
+ .. [6] Gilles Audemard, Jean-Marie Lagniez, Laurent Simon.
180
+ *Improving Glucose for Incremental SAT Solving with
181
+ Assumptions: Application to MUS Extraction*. SAT 2013.
182
+ pp. 309-317
183
+
184
+ .. [7] Enrico Giunchiglia, Marco Maratea. *Solving Optimization
185
+ Problems with DLL*. ECAI 2006. pp. 377-381
186
+
187
+ .. [8] Knot Pipatsrisawat, Adnan Darwiche. *A Lightweight Component
188
+ Caching Scheme for Satisfiability Solvers*. SAT 2007. pp. 294-299
189
+
190
+
191
+ :param formula: (weighted) (partial) CNFPlus formula
192
+ :param solver: SAT oracle name
193
+ :param adapt: detect and adapt intrinsic AtMost1 constraints
194
+ :param cover: CNFPlus formula to cover when doing MUS enumeration
195
+ :param dcalls: apply clause D oracle calls (for unsorted enumeration only)
196
+ :param exhaust: do core exhaustion
197
+ :param minz: do heuristic core reduction
198
+ :param puresat: use pure SAT-based hitting set enumeration
199
+ :param unsorted: apply unsorted MUS enumeration
200
+ :param trim: do core trimming at most this number of times
201
+ :param verbose: verbosity level
202
+
203
+ :type formula: :class:`.WCNFPlus`
204
+ :type solver: str
205
+ :type adapt: bool
206
+ :type cover: :class:`.CNFPlus`
207
+ :type dcalls: bool
208
+ :type exhaust: bool
209
+ :type minz: bool
210
+ :type puresat: str
211
+ :type unsorted: bool
212
+ :type trim: int
213
+ :type verbose: int
214
+ """
215
+
216
+ def __init__(self, formula, solver='g3', adapt=False, cover=None,
217
+ dcalls=False, exhaust=False, minz=False, puresat=False,
218
+ unsorted=False, trim=False, verbose=0):
219
+ """
220
+ Constructor.
221
+ """
222
+
223
+ assert not puresat or unsorted, '\'unsorted\' needs to be True for pure SAT mode'
224
+
225
+ # verbosity level
226
+ self.verbose = verbose
227
+
228
+ # constructing a local copy of the formula
229
+ self.formula = WCNFPlus()
230
+ self.formula.hard = formula.hard[:]
231
+ self.formula.wght = formula.wght[:]
232
+ self.formula.topw = formula.topw
233
+ self.formula.nv = formula.nv
234
+
235
+ # copying atmost constraints, if any
236
+ if isinstance(formula, WCNFPlus) and formula.atms:
237
+ self.formula.atms = formula.atms[:]
238
+
239
+ # top variable identifier
240
+ self.topv = formula.nv
241
+
242
+ # processing soft clauses
243
+ self._process_soft(formula)
244
+ self.formula.nv = self.topv
245
+
246
+ # creating an unweighted copy
247
+ unweighted = self.formula.copy()
248
+ unweighted.wght = [1 for w in unweighted.wght]
249
+
250
+ # enumerating disjoint MCSes (including unit-size MCSes)
251
+ to_hit, self.units = self._disjoint(unweighted, solver, adapt, exhaust,
252
+ minz, trim)
253
+
254
+ if self.verbose > 2:
255
+ print('c mcses: {0} unit, {1} disj'.format(len(self.units),
256
+ len(to_hit) + len(self.units)))
257
+
258
+ if not unsorted:
259
+ # MaxSAT-based hitting set enumerator
260
+ self.hitman = Hitman(bootstrap_with=to_hit, weights=self.weights,
261
+ solver=solver, htype='sorted', mxs_adapt=adapt,
262
+ mxs_exhaust=exhaust, mxs_minz=minz, mxs_trim=trim)
263
+ elif not puresat:
264
+ # MCS-based hitting set enumerator
265
+ self.hitman = Hitman(bootstrap_with=to_hit, weights=self.weights,
266
+ solver=solver, htype='lbx', mcs_usecld=dcalls)
267
+ else:
268
+ # pure SAT-based hitting set enumerator with preferred phases
269
+ self.hitman = Hitman(bootstrap_with=to_hit, weights=self.weights,
270
+ solver=puresat, htype='sat')
271
+
272
+ # adding the formula to cover to the hitting set enumerator
273
+ self.cover = cover is not None
274
+ if cover:
275
+ # mapping literals to Hitman's atoms
276
+ m = lambda l: Atom(l, sign=True) if -l not in self.weights else Atom(-l, sign=False)
277
+
278
+ for cl in cover:
279
+ if len(cl) != 2 or not type(cl[0]) in (list, tuple, set):
280
+ cl = [m(l) for l in cl]
281
+ else:
282
+ cl = [[m(l) for l in cl[0]], cl[1]]
283
+
284
+ self.hitman.add_hard(cl, weights=self.weights)
285
+
286
+ # SAT oracle bootstrapped with the hard clauses; note that
287
+ # clauses of the unit-size MCSes are enforced to be enabled
288
+ self.oracle = Solver(name=solver, bootstrap_with=unweighted.hard +
289
+ [[mcs] for mcs in self.units])
290
+
291
+ if unweighted.atms:
292
+ if solver in SolverNames.cadical195:
293
+ # we are using CaDiCaL195 and it can use external linear engine
294
+ self.oracle.activate_atmost()
295
+
296
+ assert self.oracle.supports_atmost(), \
297
+ '{0} does not support native cardinality constraints. Make sure you use the right type of formula.'.format(self.solver)
298
+
299
+ for atm in unweighted.atms:
300
+ self.oracle.add_atmost(*atm)
301
+
302
+ def __del__(self):
303
+ """
304
+ Destructor.
305
+ """
306
+
307
+ self.delete()
308
+
309
+ def __enter__(self):
310
+ """
311
+ 'with' constructor.
312
+ """
313
+
314
+ return self
315
+
316
+ def __exit__(self, exc_type, exc_value, traceback):
317
+ """
318
+ 'with' destructor.
319
+ """
320
+
321
+ self.delete()
322
+
323
+ def delete(self):
324
+ """
325
+ Explicit destructor of the internal hitting set and SAT oracles.
326
+ """
327
+
328
+ if self.hitman:
329
+ self.hitman.delete()
330
+ self.hitman = None
331
+
332
+ if self.oracle:
333
+ self.oracle.delete()
334
+ self.oracle = None
335
+
336
+ def _process_soft(self, formula):
337
+ """
338
+ The method is for processing the soft clauses of the input
339
+ formula. Concretely, it checks which soft clauses must be relaxed
340
+ by a unique selector literal and applies the relaxation.
341
+
342
+ :param formula: input formula
343
+ :type formula: :class:`.WCNF`
344
+ """
345
+
346
+ # list of selectors
347
+ self.sels = []
348
+
349
+ # mapping from selectors to clause ids
350
+ self.smap = {}
351
+
352
+ # duplicate unit clauses
353
+ processed_dups = set()
354
+
355
+ # processing the soft clauses
356
+ for cl in formula.soft:
357
+ # if the clause is unit-size, its sole literal acts a selector
358
+ selv = cl[0]
359
+
360
+ # if clause is not unit, we relax it
361
+ if len(cl) > 1:
362
+ self.topv += 1
363
+ selv = self.topv
364
+ self.formula.hard.append(cl + [-selv])
365
+ elif selv in self.smap:
366
+ # the clause is unit but a there is a previously seen
367
+ # duplicate of this clause; this means we have to
368
+ # reprocess the previous clause again and relax it
369
+ if selv not in processed_dups:
370
+ self.topv += 1
371
+ nsel = self.topv
372
+ self.sels[self.smap[selv] - 1] = nsel
373
+ self.formula.hard.append(self.formula.soft[self.smap[selv] - 1] + [-nsel])
374
+ self.formula.soft[self.smap[selv] - 1] = [nsel]
375
+ self.smap[nsel] = self.smap[selv]
376
+ processed_dups.add(selv)
377
+
378
+ # processing the current clause
379
+ self.topv += 1
380
+ selv = self.topv
381
+ self.formula.hard.append(cl + [-selv])
382
+
383
+ self.sels.append(selv)
384
+ self.formula.soft.append([selv])
385
+ self.smap[selv] = len(self.sels)
386
+
387
+ # garbage-collecting the duplicates
388
+ for selv in processed_dups:
389
+ del self.smap[selv]
390
+
391
+ # these numbers should be equal after the processing
392
+ assert len(self.sels) == len(self.smap) == len(self.formula.wght)
393
+
394
+ # creating a dictionary of weights
395
+ self.weights = {l: w for l, w in zip(self.sels, self.formula.wght)}
396
+
397
+ def _disjoint(self, formula, solver, adapt, exhaust, minz, trim):
398
+ """
399
+ This method constitutes the preliminary step of the implicit
400
+ hitting set paradigm of Forqes. Namely, it enumerates all the
401
+ disjoint *minimal correction subsets* (MCSes) of the formula,
402
+ which will be later used to bootstrap the hitting set solver.
403
+
404
+ Note that the MaxSAT solver in use is :class:`.RC2`. As a result,
405
+ all the input parameters of the method, namely, ``formula``,
406
+ ``solver``, ``adapt``, `exhaust``, ``minz``, and ``trim`` -
407
+ represent the input and the options for the RC2 solver.
408
+
409
+ :param formula: input formula
410
+ :param solver: SAT solver name
411
+ :param adapt: detect and adapt AtMost1 constraints
412
+ :param exhaust: exhaust unsatisfiable cores
413
+ :param minz: apply heuristic core minimization
414
+ :param trim: trim unsatisfiable cores at most this number of times
415
+
416
+ :type formula: :class:`.WCNF`
417
+ :type solver: str
418
+ :type adapt: bool
419
+ :type exhaust: bool
420
+ :type minz: bool
421
+ :type trim: int
422
+ """
423
+
424
+ # these will store disjoint MCSes
425
+ # (unit-size MCSes are stored separately)
426
+ to_hit, units = [], []
427
+
428
+ with RC2(formula, solver=solver, adapt=adapt, exhaust=exhaust,
429
+ minz=minz, trim=trim, verbose=0) as oracle:
430
+
431
+ # iterating over MaxSAT solutions
432
+ while True:
433
+ # a new MaxSAT model
434
+ model = oracle.compute()
435
+
436
+ if model is None:
437
+ # no model => no more disjoint MCSes
438
+ break
439
+
440
+ # extracting the MCS corresponding to the model
441
+ falsified = list(filter(lambda l: model[abs(l) - 1] == -l, self.sels))
442
+
443
+ # unit size or not?
444
+ if len(falsified) > 1:
445
+ to_hit.append(falsified)
446
+ else:
447
+ units.append(falsified[0])
448
+
449
+ # blocking the MCS;
450
+ # next time, all these clauses will be satisfied
451
+ for l in falsified:
452
+ oracle.add_clause([l])
453
+
454
+ # reporting the MCS
455
+ if self.verbose > 3:
456
+ print('c mcs: {0} 0'.format(' '.join([str(self.smap[s]) for s in falsified])))
457
+
458
+ # RC2 will be destroyed next; let's keep the oracle time
459
+ self.disj_time = oracle.oracle_time()
460
+
461
+ return to_hit, units
462
+
463
+ def compute(self):
464
+ """
465
+ This method implements the main look of the implicit hitting set
466
+ paradigm of Forqes to compute a best-cost MUS. The result MUS is
467
+ returned as a list of integers, each representing a soft clause
468
+ index.
469
+
470
+ :rtype: list(int)
471
+ """
472
+
473
+ # correctly computed cost of the unit-mcs component
474
+ units_cost = sum(map(lambda l: self.weights[l], self.units))
475
+
476
+ while True:
477
+ # computing a new optimal hitting set
478
+ hs = self.hitman.get()
479
+
480
+ if hs is None:
481
+ # no more hitting sets exist
482
+ break
483
+
484
+ # setting all the selector polarities to true
485
+ self.oracle.set_phases(self.sels)
486
+
487
+ # testing satisfiability of the {self.units + hs} subset
488
+ res = self.oracle.solve(assumptions=hs)
489
+
490
+ if res == False:
491
+ # the candidate subset of clauses is unsatisfiable,
492
+ # i.e. it is an optimal MUS we are searching for;
493
+ # therefore, blocking it and returning
494
+ self.hitman.block(hs)
495
+ self.cost = sum(map(lambda l: self.weights[l], hs)) + units_cost
496
+
497
+ if self.units and self.cover:
498
+ # due to additional constraints to cover and unit mcses,
499
+ # there may be duplicate clauses in self.units + hs
500
+ return sorted(map(lambda s: self.smap[s], sorted(set(self.units + hs))))
501
+ else:
502
+ return sorted(map(lambda s: self.smap[s], self.units + hs))
503
+ else:
504
+ # the candidate subset is satisfiable,
505
+ # thus extracting a correction subset
506
+ model = self.oracle.get_model()
507
+ cs = list(filter(lambda l: model[abs(l) - 1] == -l, self.sels))
508
+
509
+ # hitting the new correction subset
510
+ self.hitman.hit(cs, weights=self.weights)
511
+
512
+ def enumerate(self):
513
+ """
514
+ This is generator method iterating through MUSes and enumerating
515
+ them until the formula has no more MUSes, or a user decides to
516
+ stop the process.
517
+
518
+ :rtype: list(int)
519
+ """
520
+
521
+ done = False
522
+
523
+ while not done:
524
+ mus = self.compute()
525
+
526
+ if mus != None:
527
+ yield mus
528
+ else:
529
+ done = True
530
+
531
+ def oracle_time(self):
532
+ """
533
+ This method computes and returns the total SAT solving time
534
+ involved.
535
+
536
+ :rtype: float
537
+ """
538
+
539
+ return self.disj_time + self.hitman.oracle_time() + self.oracle.time_accum()
540
+
541
+
542
+ #
543
+ #==============================================================================
544
+ def parse_options():
545
+ """
546
+ Parses command-line options.
547
+ """
548
+
549
+ try:
550
+ opts, args = getopt.getopt(sys.argv[1:], 'ac:de:hmp:s:t:uvx',
551
+ ['adapt', 'cover=', 'dcalls', 'enum=', 'exhaust', 'help',
552
+ 'minimize', 'solver=', 'puresat=', 'unsorted', 'trim=',
553
+ 'verbose'])
554
+ except getopt.GetoptError as err:
555
+ sys.stderr.write(str(err).capitalize() + '\n')
556
+ usage()
557
+ sys.exit(1)
558
+
559
+ adapt = False
560
+ cover = None
561
+ dcalls = False
562
+ exhaust = False
563
+ minz = False
564
+ to_enum = 1
565
+ solver = 'g3'
566
+ puresat = False
567
+ unsorted = False
568
+ trim = 0
569
+ verbose = 1
570
+
571
+ for opt, arg in opts:
572
+ if opt in ('-a', '--adapt'):
573
+ adapt = True
574
+ elif opt in ('-c', '--cover'):
575
+ cover = str(arg)
576
+ if cover.lower() == 'none':
577
+ cover = None
578
+ elif opt in ('-d', '--dcalls'):
579
+ dcalls = True
580
+ elif opt in ('-e', '--enum'):
581
+ to_enum = str(arg)
582
+ if to_enum != 'all':
583
+ to_enum = int(to_enum)
584
+ elif opt in ('-h', '--help'):
585
+ usage()
586
+ sys.exit(0)
587
+ elif opt in ('-m', '--minimize'):
588
+ minz = True
589
+ elif opt in ('-p', '--puresat'):
590
+ puresat = str(arg)
591
+ elif opt in ('-s', '--solver'):
592
+ solver = str(arg)
593
+ elif opt in ('-u', '--unsorted'):
594
+ unsorted = True
595
+ elif opt in ('-t', '--trim'):
596
+ trim = int(arg)
597
+ elif opt in ('-v', '--verbose'):
598
+ verbose += 1
599
+ elif opt in ('-x', '--exhaust'):
600
+ exhaust = True
601
+ else:
602
+ assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
603
+
604
+ return adapt, cover, dcalls, exhaust, minz, trim, to_enum, solver, \
605
+ puresat, unsorted, verbose, args
606
+
607
+
608
+ #
609
+ #==============================================================================
610
+ def usage():
611
+ """
612
+ Prints help message.
613
+ """
614
+
615
+ print('Usage:', os.path.basename(sys.argv[0]), '[options] file')
616
+ print('Options:')
617
+ print(' -a, --adapt Try to adapt (simplify) input formula')
618
+ print(' -c, --cover=<string> Stop MUS enumeration as soon as this formula is covered')
619
+ print(' Available values: any valid file path, none (default = none)')
620
+ print(' -d, --dcalls Apply clause D calls (in unsorted enumeration only)')
621
+ print(' -e, --enum=<string> Enumerate top-k solutions')
622
+ print(' Available values: [1 .. INT_MAX], all (default: 1)')
623
+ print(' -h, --help Show this message')
624
+ print(' -m, --minimize Use a heuristic unsatisfiable core minimizer')
625
+ print(' -p, --puresat=<string> Use a pure SAT-based hitting set enumerator')
626
+ print(' Available values: cd15, cd19, lgl, mgh (default = mgh)')
627
+ print(' Requires: unsorted mode, i.e. \'-u\'')
628
+ print(' -s, --solver SAT solver to use')
629
+ print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default = g3)')
630
+ print(' -t, --trim=<int> How many times to trim unsatisfiable cores')
631
+ print(' Available values: [0 .. INT_MAX] (default = 0)')
632
+ print(' -u, --unsorted Enumerate MUSes in an unsorted way')
633
+ print(' -v, --verbose Be verbose')
634
+ print(' -x, --exhaust Exhaust new unsatisfiable cores')
635
+
636
+
637
+ #
638
+ #==============================================================================
639
+ if __name__ == '__main__':
640
+ adapt, cover, dcalls, exhaust, minz, trim, to_enum, solver, puresat, \
641
+ unsorted, verbose, files = parse_options()
642
+
643
+ if files:
644
+ # reading standard CNF, WCNF, or (W)CNF+
645
+ if re.search(r'cnf[p|+]?(\.(gz|bz2|lzma|xz))?$', files[0]):
646
+ if re.search(r'\.wcnf[p|+]?(\.(gz|bz2|lzma|xz))?$', files[0]):
647
+ formula = WCNFPlus(from_file=files[0])
648
+ else: # expecting '*.cnf[,p,+].*'
649
+ formula = CNFPlus(from_file=files[0]).weighted()
650
+
651
+ if cover: # expecting '*.cnf[,p,+].*' only!
652
+ assert re.search(r'cnf[p|+]?(\.(gz|bz2|lzma|xz))?$', cover), 'wrong file for formula to cover'
653
+ cover = CNFPlus(from_file=cover)
654
+
655
+ # creating an object of OptUx
656
+ with OptUx(formula, solver=solver, adapt=adapt, cover=cover,
657
+ dcalls=dcalls, exhaust=exhaust, minz=minz, puresat=puresat,
658
+ unsorted=unsorted, trim=trim, verbose=verbose) as optux:
659
+
660
+ # iterating over the necessary number of optimal MUSes
661
+ for i, mus in enumerate(optux.enumerate()):
662
+ # reporting the current solution
663
+ if verbose:
664
+ print('c mus:', ' '.join([str(cl_id) for cl_id in mus]), '0')
665
+
666
+ if verbose > 1:
667
+ print('c cost:', optux.cost)
668
+
669
+ # checking if we are done
670
+ if to_enum and i + 1 == to_enum:
671
+ break
672
+
673
+ # reporting the total oracle time
674
+ if verbose > 1:
675
+ print('c oracle time: {0:.4f}'.format(optux.oracle_time()))