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,638 @@
1
+ #!python
2
+ #-*- coding:utf-8 -*-
3
+ ##
4
+ ## lbx.py
5
+ ##
6
+ ## Created on: Jan 9, 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
+ LBX
20
+
21
+ ==================
22
+ Module description
23
+ ==================
24
+
25
+ This module implements a prototype of the LBX algorithm for the computation
26
+ of a *minimal correction subset* (MCS) and/or MCS enumeration. The LBX
27
+ abbreviation stands for *literal-based MCS extraction* algorithm, which was
28
+ proposed in [1]_. Note that this prototype does not follow the original
29
+ low-level implementation of the corresponding MCS extractor available
30
+ `online <https://reason.di.fc.ul.pt/wiki/doku.php?id=lbx>`_ (compared to
31
+ our prototype, the low-level implementation has a number of additional
32
+ heuristics used). However, it implements the LBX algorithm for partial
33
+ MaxSAT formulas, as described in [1]_.
34
+
35
+ .. [1] Carlos Mencia, Alessandro Previti, Joao Marques-Silva.
36
+ *Literal-Based MCS Extraction*. IJCAI 2015. pp. 1973-1979
37
+
38
+ The implementation can be used as an executable (the list of available
39
+ command-line options can be shown using ``lbx.py -h``) in the following
40
+ way:
41
+
42
+ ::
43
+
44
+ $ xzcat formula.wcnf.xz
45
+ p wcnf 3 6 4
46
+ 1 1 0
47
+ 1 2 0
48
+ 1 3 0
49
+ 4 -1 -2 0
50
+ 4 -1 -3 0
51
+ 4 -2 -3 0
52
+
53
+ $ lbx.py -d -e all -s glucose3 -vv formula.wcnf.xz
54
+ c MCS: 1 3 0
55
+ c cost: 2
56
+ c MCS: 2 3 0
57
+ c cost: 2
58
+ c MCS: 1 2 0
59
+ c cost: 2
60
+ c oracle time: 0.0002
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.lbx import LBX
68
+ >>> from pysat.formula import WCNF
69
+ >>>
70
+ >>> wcnf = WCNF(from_file='formula.wcnf.xz')
71
+ >>>
72
+ >>> lbx = LBX(wcnf, use_cld=True, solver_name='g3')
73
+ >>> for mcs in lbx.enumerate():
74
+ ... lbx.block(mcs)
75
+ ... print(mcs)
76
+ [1, 3]
77
+ [2, 3]
78
+ [1, 2]
79
+
80
+ ==============
81
+ Module details
82
+ ==============
83
+ """
84
+
85
+ #
86
+ #==============================================================================
87
+ from __future__ import print_function
88
+ import collections
89
+ import getopt
90
+ from math import copysign
91
+ import os
92
+ from pysat.formula import CNFPlus, WCNFPlus
93
+ from pysat.process import Processor
94
+ from pysat.solvers import Solver, SolverNames
95
+ import re
96
+ from six.moves import range
97
+ import sys
98
+
99
+
100
+ #
101
+ #==============================================================================
102
+ class LBX(object):
103
+ """
104
+ LBX-like algorithm for computing MCSes. Given an unsatisfiable partial
105
+ CNF formula, i.e. formula in the :class:`.WCNF` format, this class can
106
+ be used to compute a given number of MCSes of the formula. The
107
+ implementation follows the LBX algorithm description in [1]_. It can
108
+ use any SAT solver available in PySAT. Additionally, the "clause
109
+ :math:`D`" heuristic can be used when enumerating MCSes.
110
+
111
+ The default SAT solver to use is ``m22`` (see :class:`.SolverNames`).
112
+ The "clause :math:`D`" heuristic is disabled by default, i.e.
113
+ ``use_cld`` is set to ``False``. Internal SAT solver's timer is also
114
+ disabled by default, i.e. ``use_timer`` is ``False``.
115
+
116
+ Additionally, the input formula can be preprocessed before running MCS
117
+ enumeration. This is controlled by the input parameter ``process``
118
+ whose integer value signifies the number of processing rounds to be
119
+ applied. The number of rounds is set to 0 by default.
120
+
121
+ :param formula: unsatisfiable partial CNF formula
122
+ :param use_cld: whether or not to use "clause :math:`D`"
123
+ :param solver_name: SAT oracle name
124
+ :param process: apply formula preprocessing this many times
125
+ :param use_timer: whether or not to use SAT solver's timer
126
+
127
+ :type formula: :class:`.WCNF`
128
+ :type use_cld: bool
129
+ :type solver_name: str
130
+ :type process: int
131
+ :type use_timer: bool
132
+ """
133
+
134
+ def __init__(self, formula, use_cld=False, solver_name='m22', process=0,
135
+ use_timer=False):
136
+ """
137
+ Constructor.
138
+ """
139
+
140
+ # dealing with preprocessing, if required
141
+ hard = formula.hard if process > 0 else []
142
+
143
+ # bootstrapping the solver with hard clauses
144
+ self.oracle = Solver(name=solver_name,
145
+ bootstrap_with=formula.hard if process == 0 else [],
146
+ use_timer=use_timer)
147
+ self.solver = solver_name
148
+
149
+ # adding native cardinality constraints (if any) as hard clauses
150
+ # this can be done only if the Minicard solver is in use
151
+ if isinstance(formula, WCNFPlus) and formula.atms:
152
+ # we are using CaDiCaL195 and it can use external linear engine
153
+ if solver_name in SolverNames.cadical195:
154
+ self.oracle.activate_atmost()
155
+
156
+ assert self.oracle.supports_atmost(), \
157
+ '{0} does not support native cardinality constraints. Make sure you use the right type of formula.'.format(solver_name)
158
+
159
+ for atm in formula.atms:
160
+ self.oracle.add_atmost(*atm)
161
+
162
+ self.topv = formula.nv # top variable id
163
+ self.soft = formula.soft
164
+ self.sels = []
165
+ self.ucld = use_cld
166
+
167
+ # mappings between internal and external variables
168
+ VariableMap = collections.namedtuple('VariableMap', ['e2i', 'i2e'])
169
+ self.vmap = VariableMap(e2i={}, i2e={})
170
+
171
+ # at this point internal and external variables are the same
172
+ for v in range(1, formula.nv + 1):
173
+ self.vmap.e2i[v] = v
174
+ self.vmap.i2e[v] = v
175
+
176
+ for cl in self.soft:
177
+ sel = cl[0]
178
+ if len(cl) > 1 or cl[0] < 0:
179
+ self.topv += 1
180
+ sel = self.topv
181
+
182
+ if process == 0:
183
+ self.oracle.add_clause(cl + [-sel])
184
+ else:
185
+ # adding to formula's hard clauses
186
+ # if any preprocessing is required
187
+ hard.append(cl + [-sel])
188
+
189
+ self.sels.append(sel)
190
+
191
+ # finally, applying formula processing, if any
192
+ if process:
193
+ # the processor is immediately destroyed,
194
+ # as we do not need to restore the models
195
+ with Processor(bootstrap_with=hard) as processor:
196
+ proc = processor.process(rounds=process, freeze=self.sels)
197
+ self.oracle.append_formula(proc)
198
+
199
+ # we won't have access to the original soft
200
+ # clauses in the case of formula preprocessing
201
+ self.soft = [[l] for l in self.sels]
202
+
203
+ def __del__(self):
204
+ """
205
+ Destructor.
206
+ """
207
+
208
+ self.delete()
209
+
210
+ def __enter__(self):
211
+ """
212
+ 'with' constructor.
213
+ """
214
+
215
+ return self
216
+
217
+ def __exit__(self, exc_type, exc_value, traceback):
218
+ """
219
+ 'with' destructor.
220
+ """
221
+
222
+ self.delete()
223
+
224
+ def delete(self):
225
+ """
226
+ Explicit destructor of the internal SAT oracle.
227
+ """
228
+
229
+ if self.oracle:
230
+ self.oracle.delete()
231
+ self.oracle = None
232
+
233
+ def add_clause(self, clause, soft=False):
234
+ """
235
+ The method for adding a new hard of soft clause to the problem
236
+ formula. Although the input formula is to be specified as an
237
+ argument of the constructor of :class:`LBX`, adding clauses may be
238
+ helpful when *enumerating* MCSes of the formula. This way, the
239
+ clauses are added incrementally, i.e. *on the fly*.
240
+
241
+ The clause to add can be any iterable over integer literals. The
242
+ additional Boolean parameter ``soft`` can be set to ``True``
243
+ meaning the the clause being added is soft (note that parameter
244
+ ``soft`` is set to ``False`` by default).
245
+
246
+ Also note that besides pure clauses, the method can also expect
247
+ native cardinality constraints represented as a pair ``(lits,
248
+ bound)``. Only hard cardinality constraints can be added.
249
+
250
+ :param clause: a clause to add
251
+ :param soft: whether or not the clause is soft
252
+
253
+ :type clause: iterable(int)
254
+ :type soft: bool
255
+ """
256
+
257
+ # first, map external literals to internal literals
258
+ # introduce new variables if necessary
259
+ cl = list(map(lambda l: self._map_extlit(l), clause if not len(clause) == 2 or not type(clause[0]) in (list, tuple, set) else clause[0]))
260
+
261
+ if not soft:
262
+ if not len(clause) == 2 or not type(clause[0]) in (list, tuple, set):
263
+ # the clause is hard, and so we simply add it to the SAT oracle
264
+ self.oracle.add_clause(cl)
265
+ else:
266
+ # this should be a native cardinality constraint,
267
+ # which can be used only together with Minicard
268
+ assert self.oracle.supports_atmost(), \
269
+ '{0} does not support native cardinality constraints. Make sure you use the right type of formula.'.format(self.solver)
270
+
271
+ self.oracle.add_atmost(cl, clause[1])
272
+ else:
273
+ self.soft.append(cl)
274
+
275
+ # soft clauses should be augmented with a selector
276
+ sel = cl[0]
277
+ if len(cl) > 1 or cl[0] < 0:
278
+ self.topv += 1
279
+ sel = self.topv
280
+
281
+ self.oracle.add_clause(cl + [-sel])
282
+
283
+ self.sels.append(sel)
284
+
285
+ def compute(self, enable=[]):
286
+ """
287
+ Compute and return one solution. This method checks whether the
288
+ hard part of the formula is satisfiable, i.e. an MCS can be
289
+ extracted. If the formula is satisfiable, the model computed by the
290
+ SAT call is used as an *over-approximation* of the MCS in the
291
+ method :func:`_compute` invoked here, which implements the LBX
292
+ algorithm.
293
+
294
+ An MCS is reported as a list of integers, each representing a soft
295
+ clause index (the smallest index is ``1``).
296
+
297
+ An optional input parameter is ``enable``, which represents a
298
+ sequence (normally a list) of soft clause indices that a user
299
+ would prefer to enable/satisfy. Note that this may result in an
300
+ unsatisfiable oracle call, in which case ``None`` will be reported
301
+ as solution. Also, the smallest clause index is assumed to be
302
+ ``1``.
303
+
304
+ :param enable: a sequence of clause ids to enable
305
+ :type enable: iterable(int)
306
+
307
+ :rtype: list(int)
308
+ """
309
+
310
+ self.setd = []
311
+ self.satc = [False for cl in self.soft] # satisfied clauses
312
+ self.solution = None
313
+ self.bb_assumps = [] # backbone assumptions
314
+ self.ss_assumps = [] # satisfied soft clause assumptions
315
+
316
+ if self.oracle.solve(assumptions=[self.sels[cl_id - 1] for cl_id in enable]):
317
+ # hard part is satisfiable => there is a solution
318
+ self._filter_satisfied(update_setd=True)
319
+ self._compute()
320
+
321
+ self.solution = list(map(lambda i: i + 1, filter(lambda i: not self.satc[i], range(len(self.soft)))))
322
+
323
+ return self.solution
324
+
325
+ def enumerate(self):
326
+ """
327
+ This method iterates through MCSes enumerating them until the
328
+ formula has no more MCSes. The method iteratively invokes
329
+ :func:`compute`. Note that the method does not block the MCSes
330
+ computed - this should be explicitly done by a user.
331
+ """
332
+
333
+ done = False
334
+ while not done:
335
+ mcs = self.compute()
336
+
337
+ if mcs != None:
338
+ yield mcs
339
+ else:
340
+ done = True
341
+
342
+ def block(self, mcs):
343
+ """
344
+ Block a (previously computed) MCS. The MCS should be given as an
345
+ iterable of integers. Note that this method is not automatically
346
+ invoked from :func:`enumerate` because a user may want to block
347
+ some of the MCSes conditionally depending on the needs. For
348
+ example, one may want to compute disjoint MCSes only in which case
349
+ this standard blocking is not appropriate.
350
+
351
+ :param mcs: an MCS to block
352
+ :type mcs: iterable(int)
353
+ """
354
+
355
+ self.oracle.add_clause([self.sels[cl_id - 1] for cl_id in mcs])
356
+
357
+ def _satisfied(self, cl, model):
358
+ """
359
+ Given a clause (as an iterable of integers) and an assignment (as a
360
+ list of integers), this method checks whether or not the assignment
361
+ satisfies the clause. This is done by a simple clause traversal.
362
+ The method is invoked from :func:`_filter_satisfied`.
363
+
364
+ :param cl: a clause to check
365
+ :param model: an assignment
366
+
367
+ :type cl: iterable(int)
368
+ :type model: list(int)
369
+
370
+ :rtype: bool
371
+ """
372
+
373
+ for l in cl:
374
+ if len(model) < abs(l) or model[abs(l) - 1] == l:
375
+ # either literal is unassigned or satisfied by the model
376
+ return True
377
+
378
+ return False
379
+
380
+ def _filter_satisfied(self, update_setd=False):
381
+ """
382
+ This method extracts a model provided by the previous call to a SAT
383
+ oracle and iterates over all soft clauses checking if each of is
384
+ satisfied by the model. Satisfied clauses are marked accordingly
385
+ while the literals of the unsatisfied clauses are kept in a list
386
+ called ``setd``, which is then used to refine the correction set
387
+ (see :func:`_compute`, and :func:`do_cld_check`).
388
+
389
+ Optional Boolean parameter ``update_setd`` enforces the method to
390
+ update variable ``self.setd``. If this parameter is set to
391
+ ``False``, the method only updates the list of satisfied clauses,
392
+ which is an under-approximation of a *maximal satisfiable subset*
393
+ (MSS).
394
+
395
+ :param update_setd: whether or not to update setd
396
+ :type update_setd: bool
397
+ """
398
+
399
+ model = self.oracle.get_model()
400
+ setd = set()
401
+
402
+ for i, cl in enumerate(self.soft):
403
+ if not self.satc[i]:
404
+ if self._satisfied(cl, model):
405
+ self.satc[i] = True
406
+ self.ss_assumps.append(self.sels[i])
407
+ else:
408
+ setd = setd.union(set(cl))
409
+
410
+ if update_setd:
411
+ self.setd = sorted(setd)
412
+
413
+ def _compute(self):
414
+ """
415
+ The main method of the class, which computes an MCS given its
416
+ over-approximation. The over-approximation is defined by a model
417
+ for the hard part of the formula obtained in :func:`compute`.
418
+
419
+ The method is essentially a simple loop going over all literals
420
+ unsatisfied by the previous model, i.e. the literals of
421
+ ``self.setd`` and checking which literals can be satisfied. This
422
+ process can be seen a refinement of the over-approximation of the
423
+ MCS. The algorithm follows the pseudo-code of the LBX algorithm
424
+ presented in [1]_.
425
+
426
+ Additionally, if :class:`LBX` was constructed with the requirement
427
+ to make "clause :math:`D`" calls, the method calls
428
+ :func:`do_cld_check` at every iteration of the loop using the
429
+ literals of ``self.setd`` not yet checked, as the contents of
430
+ "clause :math:`D`".
431
+ """
432
+
433
+ # unless clause D checks are used, test one literal at a time
434
+ # and add it either to satisfied of backbone assumptions
435
+ i = 0
436
+ while i < len(self.setd):
437
+ if self.ucld:
438
+ self.do_cld_check(self.setd[i:])
439
+ i = 0
440
+
441
+ if self.setd: # if may be empty after the clause D check
442
+ if self.oracle.solve(assumptions=self.ss_assumps + self.bb_assumps + [self.setd[i]]):
443
+ # filtering satisfied clauses
444
+ self._filter_satisfied()
445
+ else:
446
+ # current literal is backbone
447
+ self.bb_assumps.append(-self.setd[i])
448
+
449
+ i += 1
450
+
451
+ def do_cld_check(self, cld):
452
+ """
453
+ Do the "clause :math:`D`" check. This method receives a list of
454
+ literals, which serves a "clause :math:`D`" [2]_, and checks
455
+ whether the formula conjoined with :math:`D` is satisfiable.
456
+
457
+ .. [2] Joao Marques-Silva, Federico Heras, Mikolas Janota,
458
+ Alessandro Previti, Anton Belov. *On Computing Minimal
459
+ Correction Subsets*. IJCAI 2013. pp. 615-622
460
+
461
+ If clause :math:`D` cannot be satisfied together with the formula,
462
+ then negations of all of its literals are backbones of the formula
463
+ and the LBX algorithm can stop. Otherwise, the literals satisfied
464
+ by the new model refine the MCS further.
465
+
466
+ Every time the method is called, a new fresh selector variable
467
+ :math:`s` is introduced, which augments the current clause
468
+ :math:`D`. The SAT oracle then checks if clause :math:`(D \\vee
469
+ \\neg{s})` can be satisfied together with the internal formula.
470
+ The :math:`D` clause is then disabled by adding a hard clause
471
+ :math:`(\\neg{s})`.
472
+
473
+ :param cld: clause :math:`D` to check
474
+ :type cld: list(int)
475
+ """
476
+
477
+ # adding a selector literal to clause D
478
+ # selector literals for clauses D currently
479
+ # cannot be reused, but this may change later
480
+ self.topv += 1
481
+ sel = self.topv
482
+ cld.append(-sel)
483
+
484
+ # adding clause D
485
+ self.oracle.add_clause(cld)
486
+
487
+ if self.oracle.solve(assumptions=self.ss_assumps + self.bb_assumps + [sel]):
488
+ # filtering satisfied
489
+ self._filter_satisfied(update_setd=True)
490
+ else:
491
+ # clause D is unsatisfiable => all literals are backbones
492
+ self.bb_assumps.extend([-l for l in cld[:-1]])
493
+ self.setd = []
494
+
495
+ # deactivating clause D
496
+ self.oracle.add_clause([-sel])
497
+
498
+ def _map_extlit(self, l):
499
+ """
500
+ Map an external variable to an internal one if necessary.
501
+
502
+ This method is used when new clauses are added to the formula
503
+ incrementally, which may result in introducing new variables
504
+ clashing with the previously used *clause selectors*. The method
505
+ makes sure no clash occurs, i.e. it maps the original variables
506
+ used in the new problem clauses to the newly introduced auxiliary
507
+ variables (see :func:`add_clause`).
508
+
509
+ Given an integer literal, a fresh literal is returned. The returned
510
+ integer has the same sign as the input literal.
511
+
512
+ :param l: literal to map
513
+ :type l: int
514
+
515
+ :rtype: int
516
+ """
517
+
518
+ v = abs(l)
519
+
520
+ if v in self.vmap.e2i:
521
+ return int(copysign(self.vmap.e2i[v], l))
522
+ else:
523
+ self.topv += 1
524
+
525
+ self.vmap.e2i[v] = self.topv
526
+ self.vmap.i2e[self.topv] = v
527
+
528
+ return int(copysign(self.topv, l))
529
+
530
+ def oracle_time(self):
531
+ """
532
+ Report the total SAT solving time.
533
+ """
534
+
535
+ return self.oracle.time_accum()
536
+
537
+
538
+ #
539
+ #==============================================================================
540
+ def parse_options():
541
+ """
542
+ Parses command-line options.
543
+ """
544
+
545
+ try:
546
+ opts, args = getopt.getopt(sys.argv[1:],
547
+ 'de:hp:p:s:v',
548
+ ['dcalls',
549
+ 'enum=',
550
+ 'help',
551
+ 'process=',
552
+ 'solver=',
553
+ 'verbose'])
554
+ except getopt.GetoptError as err:
555
+ sys.stderr.write(str(err).capitalize() + '\n')
556
+ usage()
557
+ sys.exit(1)
558
+
559
+ dcalls = False
560
+ to_enum = 1
561
+ process = 0
562
+ solver = 'm22'
563
+ verbose = 0
564
+
565
+ for opt, arg in opts:
566
+ if opt in ('-d', '--dcalls'):
567
+ dcalls = True
568
+ elif opt in ('-e', '--enum'):
569
+ to_enum = str(arg)
570
+ if to_enum != 'all':
571
+ to_enum = int(to_enum)
572
+ elif opt in ('-h', '--help'):
573
+ usage()
574
+ sys.exit(0)
575
+ elif opt in ('-p', '--process'):
576
+ process = int(arg)
577
+ elif opt in ('-s', '--solver'):
578
+ solver = str(arg)
579
+ elif opt in ('-v', '--verbose'):
580
+ verbose += 1
581
+ else:
582
+ assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
583
+
584
+ return dcalls, to_enum, solver, process, verbose, args
585
+
586
+
587
+ #
588
+ #==============================================================================
589
+ def usage():
590
+ """
591
+ Prints help message.
592
+ """
593
+
594
+ print('Usage:', os.path.basename(sys.argv[0]), '[options] file')
595
+ print('Options:')
596
+ print(' -d, --dcalls Apply clause D calls')
597
+ print(' -e, --enum=<string> How many solutions to compute')
598
+ print(' Available values: [1 .. all] (default: 1)')
599
+ print(' -h, --help')
600
+ print(' -p, --process=<int> Number of processing rounds')
601
+ print(' Available values: [0 .. INT_MAX] (default = 0)')
602
+ print(' -s, --solver SAT solver to use')
603
+ print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default = m22)')
604
+ print(' -v, --verbose Be verbose')
605
+
606
+
607
+ #
608
+ #==============================================================================
609
+ if __name__ == '__main__':
610
+ dcalls, to_enum, solver, process, verbose, files = parse_options()
611
+
612
+ if type(to_enum) == str:
613
+ to_enum = 0
614
+
615
+ if files:
616
+ # reading standard CNF, WCNF, or (W)CNF+
617
+ assert re.search(r'cnf[p|+]?(\.(gz|bz2|lzma|xz|zst))?$', files[0]), 'Unknown input file extension'
618
+ if re.search(r'\.wcnf[p|+]?(\.(gz|bz2|lzma|xz|zst))?$', files[0]):
619
+ formula = WCNFPlus(from_file=files[0])
620
+ else: # expecting '*.cnf[,p,+].*'
621
+ formula = CNFPlus(from_file=files[0]).weighted()
622
+
623
+ with LBX(formula, use_cld=dcalls, solver_name=solver, process=process,
624
+ use_timer=True) as mcsls:
625
+ for i, mcs in enumerate(mcsls.enumerate()):
626
+ if verbose:
627
+ print('c MCS:', ' '.join([str(cl_id) for cl_id in mcs]), '0')
628
+
629
+ if verbose > 1:
630
+ cost = sum([formula.wght[cl_id - 1] for cl_id in mcs])
631
+ print('c cost:', cost)
632
+
633
+ if to_enum and i + 1 == to_enum:
634
+ break
635
+
636
+ mcsls.block(mcs)
637
+
638
+ print('c oracle time: {0:.4f}'.format(mcsls.oracle_time()))