python-sat 1.8.dev21__tar.gz → 1.8.dev23__tar.gz
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.
- {python_sat-1.8.dev21/python_sat.egg-info → python_sat-1.8.dev23}/PKG-INFO +1 -1
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/README.rst +25 -0
- python_sat-1.8.dev23/examples/bbscan.py +669 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/examples/bica.py +2 -3
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/examples/fm.py +1 -1
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/examples/lbx.py +5 -5
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/examples/lsu.py +5 -3
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/examples/mcsls.py +5 -5
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/examples/musx.py +1 -1
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/examples/optux.py +6 -6
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/examples/primer.py +2 -2
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/examples/rc2.py +1 -1
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/pysat/__init__.py +1 -1
- {python_sat-1.8.dev21 → python_sat-1.8.dev23/python_sat.egg-info}/PKG-INFO +1 -1
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/python_sat.egg-info/SOURCES.txt +1 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/setup.py +2 -2
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/LICENSE.txt +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/MANIFEST.in +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/allies/__init__.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/allies/approxmc.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/allies/unigen.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/cardenc/bitwise.hh +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/cardenc/card.hh +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/cardenc/clset.hh +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/cardenc/common.hh +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/cardenc/itot.hh +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/cardenc/ladder.hh +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/cardenc/mto.hh +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/cardenc/pairwise.hh +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/cardenc/ptypes.hh +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/cardenc/pycard.cc +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/cardenc/seqcounter.hh +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/cardenc/sortcard.hh +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/cardenc/utils.hh +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/examples/__init__.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/examples/genhard.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/examples/hitman.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/examples/models.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/examples/usage.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/pysat/_fileio.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/pysat/_utils.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/pysat/card.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/pysat/engines.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/pysat/formula.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/pysat/pb.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/pysat/process.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/pysat/solvers.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/python_sat.egg-info/dependency_links.txt +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/python_sat.egg-info/requires.txt +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/python_sat.egg-info/top_level.txt +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/requirements.txt +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/setup.cfg +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/cadical103.tar.gz +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/cadical153.tar.gz +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/cadical195.tar.gz +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/glucose30.tar.gz +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/glucose41.tar.gz +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/glucose421.tar.gz +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/lingeling.tar.gz +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/maplechrono.zip +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/maplecm.zip +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/maplesat.zip +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/mergesat3.tar.gz +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/minicard.tar.gz +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/minisat22.tar.gz +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/minisatgh.zip +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/patches/cadical103.patch +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/patches/cadical153.patch +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/patches/cadical195.patch +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/patches/glucose30.patch +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/patches/glucose41.patch +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/patches/glucose421.patch +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/patches/gluecard30.patch +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/patches/gluecard41.patch +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/patches/lingeling.patch +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/patches/maplechrono.patch +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/patches/maplecm.patch +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/patches/maplesat.patch +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/patches/mergesat3.patch +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/patches/minicard.patch +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/patches/minisat22.patch +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/patches/minisatgh.patch +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/prepare.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/solvers/pysolvers.cc +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/tests/test_accum_stats.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/tests/test_atmost.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/tests/test_atmost1.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/tests/test_atmostk.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/tests/test_boolengine.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/tests/test_clausification.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/tests/test_cnf.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/tests/test_equals1.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/tests/test_formula_unique.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/tests/test_process.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/tests/test_propagate.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/tests/test_unique_model.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/tests/test_unique_mus.py +0 -0
- {python_sat-1.8.dev21 → python_sat-1.8.dev23}/tests/test_warmstart.py +0 -0
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
PySAT: SAT technology in Python
|
|
2
2
|
===============================
|
|
3
3
|
|
|
4
|
+
|version|
|
|
5
|
+
|tests|
|
|
6
|
+
|downloads|
|
|
7
|
+
|license|
|
|
8
|
+
|sat18|
|
|
9
|
+
|sat24|
|
|
10
|
+
|
|
11
|
+
.. |version| image:: https://img.shields.io/pypi/v/python-sat
|
|
12
|
+
:target: https://pysathq.github.io/
|
|
13
|
+
|
|
14
|
+
.. |tests| image:: https://img.shields.io/github/actions/workflow/status/pysathq/pysat/test.yml?label=tests
|
|
15
|
+
:target: https://github.com/pysathq/pysat
|
|
16
|
+
|
|
17
|
+
.. |downloads| image:: https://img.shields.io/pypi/dm/python-sat
|
|
18
|
+
:target: https://pypi.org/project/python-sat/
|
|
19
|
+
|
|
20
|
+
.. |license| image:: https://img.shields.io/github/license/pysathq/pysat
|
|
21
|
+
:target: https://github.com/pysathq/pysat/blob/master/LICENSE.txt
|
|
22
|
+
|
|
23
|
+
.. |sat18| image:: https://img.shields.io/badge/doi-10.1007%2F978--3--319--94144--8_26-blue
|
|
24
|
+
:target: https://doi.org/10.1007/978-3-319-94144-8_26
|
|
25
|
+
|
|
26
|
+
.. |sat24| image:: https://img.shields.io/badge/doi-10.4230%2FLIPICS.SAT.2024.16-blue
|
|
27
|
+
:target: https://doi.org/10.4230/LIPIcs.SAT.2024.16
|
|
28
|
+
|
|
4
29
|
PySAT is a Python (2.7, 3.4+) toolkit, which aims at providing a simple and
|
|
5
30
|
unified interface to a number of state-of-art `Boolean satisfiability (SAT)
|
|
6
31
|
<https://en.wikipedia.org/wiki/Boolean_satisfiability_problem>`__ solvers as
|
|
@@ -0,0 +1,669 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
#-*- coding:utf-8 -*-
|
|
3
|
+
##
|
|
4
|
+
## bbscan.py
|
|
5
|
+
##
|
|
6
|
+
## Created on: Sep 21, 2025
|
|
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
|
+
BBScan
|
|
20
|
+
|
|
21
|
+
==================
|
|
22
|
+
Module description
|
|
23
|
+
==================
|
|
24
|
+
|
|
25
|
+
A simple implementation of a few backbone extraction algorithms described
|
|
26
|
+
in [1]_. Given a Boolean formula :math:`\\varphi`, a backbone literal is a
|
|
27
|
+
literal that holds true in every satisfying assignment of
|
|
28
|
+
:math:`\\varphi`. The backbone of formula :math:`\\varphi` is the set of
|
|
29
|
+
all backbone literals. This implementation includes the following
|
|
30
|
+
algorithms from [1]_:
|
|
31
|
+
|
|
32
|
+
* implicant enumeration algorithm,
|
|
33
|
+
* iterative algorithm (with one test per variable),
|
|
34
|
+
* complement of backbone estimate algorithm,
|
|
35
|
+
* chunking algorithm,
|
|
36
|
+
* core-based algorithm,
|
|
37
|
+
* core-based algorithm with chunking.
|
|
38
|
+
|
|
39
|
+
.. [1] Mikolás Janota, Inês Lynce, Joao Marques-Silva. *Algorithms for
|
|
40
|
+
computing backbones of propositional formulae*. AI Commun. 28(2).
|
|
41
|
+
2015. pp. 161-177
|
|
42
|
+
|
|
43
|
+
The implementation can be used as an executable (the list of
|
|
44
|
+
available command-line options can be shown using ``bbscan.py -h``)
|
|
45
|
+
in the following way:
|
|
46
|
+
|
|
47
|
+
::
|
|
48
|
+
|
|
49
|
+
$ xzcat formula.cnf.xz
|
|
50
|
+
p cnf 3 4
|
|
51
|
+
1 2 0
|
|
52
|
+
1 -2 0
|
|
53
|
+
2 -3 0
|
|
54
|
+
-2 -3 0
|
|
55
|
+
|
|
56
|
+
$ bbscan.py -v formula.cnf.xz
|
|
57
|
+
c formula: 3 vars, 4 clauses
|
|
58
|
+
c using iterative algorithm
|
|
59
|
+
v +1 -3 0
|
|
60
|
+
c backbone size: 2 (66.67% of all variables)
|
|
61
|
+
c oracle time: 0.0000
|
|
62
|
+
c oracle calls: 4
|
|
63
|
+
|
|
64
|
+
Alternatively, the algorithms can be accessed and invoked through the
|
|
65
|
+
standard ``import`` interface of Python, e.g.
|
|
66
|
+
|
|
67
|
+
.. code-block:: python
|
|
68
|
+
|
|
69
|
+
>>> from pysat.examples.bbscan import BBScan
|
|
70
|
+
>>> from pysat.formula import CNF
|
|
71
|
+
>>>
|
|
72
|
+
>>> # reading a formula from file
|
|
73
|
+
>>> cnf = CNF(from_file='formula.wcnf.xz')
|
|
74
|
+
>>>
|
|
75
|
+
>>> # creating BBScan and running it
|
|
76
|
+
>>> with BBScan(cnf, solver='g3', lift=False, rotate=True) as bbscan:
|
|
77
|
+
... bbone = bbscan.compute(algorithm='core')
|
|
78
|
+
...
|
|
79
|
+
... # outputting the results
|
|
80
|
+
... if bbone:
|
|
81
|
+
... print(bbone)
|
|
82
|
+
[1, -3]
|
|
83
|
+
|
|
84
|
+
Each of the available algorithms can be augmented with two simple
|
|
85
|
+
heuristics aiming at reducing satisfying assignments and, thus, filtering
|
|
86
|
+
out unnecessary literals: literal lifting and filtering rotatable
|
|
87
|
+
literals. Both are described in the aforementioned paper.
|
|
88
|
+
|
|
89
|
+
Note that most of the methods of the class :class:`BBScan` are made
|
|
90
|
+
private. Therefore, the tool provides a minimalistic interface via which a
|
|
91
|
+
user can specify all the necessary parameters.
|
|
92
|
+
|
|
93
|
+
==============
|
|
94
|
+
Module details
|
|
95
|
+
==============
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
#
|
|
99
|
+
#==============================================================================
|
|
100
|
+
import getopt
|
|
101
|
+
import itertools
|
|
102
|
+
import os
|
|
103
|
+
from pysat.formula import CNF, IDPool
|
|
104
|
+
from pysat.solvers import Solver, SolverNames
|
|
105
|
+
import sys
|
|
106
|
+
import re
|
|
107
|
+
import time
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
#
|
|
111
|
+
#==============================================================================
|
|
112
|
+
class BBScan:
|
|
113
|
+
"""
|
|
114
|
+
A solver for computing all backbone literals of a given Boolean
|
|
115
|
+
formula. It implements 6 algorithms for backbone computation described
|
|
116
|
+
in [1]_ augmented with two simple heuristics that can be speed up the
|
|
117
|
+
process.
|
|
118
|
+
|
|
119
|
+
Note that the input formula can be a :class:`.CNF` object but also any
|
|
120
|
+
object of class :class:`.Formula`, thus the tool can used for
|
|
121
|
+
computing backbone literals of non-clausal formulas.
|
|
122
|
+
|
|
123
|
+
A user can select one of the SAT solvers available at hand
|
|
124
|
+
(``'glucose3'`` is used by default). The optional two heuristics can
|
|
125
|
+
be also specified as Boolean input arguments ``lift``, and ``rotate``
|
|
126
|
+
(turned off by default).
|
|
127
|
+
|
|
128
|
+
The list of initialiser's arguments is as follows:
|
|
129
|
+
|
|
130
|
+
:param formula: input formula whose backbone is sought
|
|
131
|
+
:param solver: SAT oracle name
|
|
132
|
+
:param lift: apply literal lifting heuristic
|
|
133
|
+
:param rotate: apply rotatable literal filtering
|
|
134
|
+
:param verbose: verbosity level
|
|
135
|
+
|
|
136
|
+
:type formula: :class:`.Formula` or :class:`.CNF`
|
|
137
|
+
:type solver: str
|
|
138
|
+
:type lift: bool
|
|
139
|
+
:type rotate: bool
|
|
140
|
+
:type verbose: int
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
def __init__(self, formula, solver='g3', lift=False, rotate=False, verbose=0):
|
|
144
|
+
"""
|
|
145
|
+
Constructor.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
self.formula = formula
|
|
149
|
+
self.verbose = verbose
|
|
150
|
+
self.oracle = None
|
|
151
|
+
|
|
152
|
+
# implicant reduction heuristics
|
|
153
|
+
self.lift, self.rotate = lift, rotate
|
|
154
|
+
|
|
155
|
+
# basic stats
|
|
156
|
+
self.calls, self.filtered = 0, 0
|
|
157
|
+
|
|
158
|
+
# this is going to be our workhorse
|
|
159
|
+
self.oracle = Solver(name=solver, bootstrap_with=formula, use_timer=True)
|
|
160
|
+
|
|
161
|
+
def __del__(self):
|
|
162
|
+
"""
|
|
163
|
+
Destructor.
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
self.delete()
|
|
167
|
+
|
|
168
|
+
def __enter__(self):
|
|
169
|
+
"""
|
|
170
|
+
'with' constructor.
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
return self
|
|
174
|
+
|
|
175
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
176
|
+
"""
|
|
177
|
+
'with' destructor.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
self.delete()
|
|
181
|
+
|
|
182
|
+
def delete(self):
|
|
183
|
+
"""
|
|
184
|
+
Explicit destructor of the internal SAT oracle.
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
if self.oracle:
|
|
188
|
+
self.oracle.delete()
|
|
189
|
+
self.oracle = None
|
|
190
|
+
|
|
191
|
+
def compute(self, algorithm='iter', chunk_size=100, focus_on=None):
|
|
192
|
+
"""
|
|
193
|
+
Main solving method. A user is supposed to specify which backbone
|
|
194
|
+
computation algorithm they want to use:
|
|
195
|
+
|
|
196
|
+
* ``'enum'`` (implicant enumeration algorithm),
|
|
197
|
+
* ``'iter'`` (iterative algorithm, with one test per variable),
|
|
198
|
+
* ``'compl'`` (complement of backbone estimate algorithm),
|
|
199
|
+
* ``'chunk'`` (chunking algorithm),
|
|
200
|
+
* ``'core'`` (core-based algorithm),
|
|
201
|
+
* ``'corechunk'`` (core-based algorithm with chunking).
|
|
202
|
+
|
|
203
|
+
By default, :class:`.BBScan` opts for ``'iter'``.
|
|
204
|
+
|
|
205
|
+
If the chunking algorithm is selected (either ``'chunk'`` or
|
|
206
|
+
``'corechunk'``), the user may specify the size of the chunk,
|
|
207
|
+
which is set to 100 by default. Note that the chunk_size can be a
|
|
208
|
+
floating-point number in the interval (0, 1], which will set the
|
|
209
|
+
actual chunk_size relative to the total number of literals of
|
|
210
|
+
interest.
|
|
211
|
+
|
|
212
|
+
Finally, one may opt for computing backbone literals out of a
|
|
213
|
+
particular subset of literals, which may run faster than computing
|
|
214
|
+
the entire formula's backbone. To focus on a particular set of
|
|
215
|
+
literals, the user should use the parameter ``focus_on``, which is
|
|
216
|
+
set to ``None`` by default.
|
|
217
|
+
|
|
218
|
+
.. note::
|
|
219
|
+
|
|
220
|
+
The method raises ``ValueError`` if the input formula is
|
|
221
|
+
unsatisfiable.
|
|
222
|
+
|
|
223
|
+
:param algorithm: backbone computation algorithm
|
|
224
|
+
:param chunk_size: number of literals in the chunk (for chunking algorithms)
|
|
225
|
+
:param focus_on: a list of literals to focus on
|
|
226
|
+
|
|
227
|
+
:type algorithm: str
|
|
228
|
+
:type chunk_size: int or float
|
|
229
|
+
:type focus_on: iterable(int)
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
self.calls += 1
|
|
233
|
+
|
|
234
|
+
if self.oracle.solve():
|
|
235
|
+
self.model = self.oracle.get_model()
|
|
236
|
+
if focus_on is not None:
|
|
237
|
+
model = set(self.model)
|
|
238
|
+
self.model = [l for l in focus_on if l in model]
|
|
239
|
+
else:
|
|
240
|
+
raise ValueError('Unsatisfiable formula')
|
|
241
|
+
|
|
242
|
+
if isinstance(chunk_size, float):
|
|
243
|
+
assert 0 < chunk_size <= 1, f'Wrong chunk proportion {chunk_size}'
|
|
244
|
+
chunk_size = int(min(self.formula.nv, len(self.model)) * chunk_size)
|
|
245
|
+
|
|
246
|
+
if algorithm == 'enum':
|
|
247
|
+
result = self._compute_enum()
|
|
248
|
+
elif algorithm == 'iter':
|
|
249
|
+
result = self._compute_iter()
|
|
250
|
+
elif algorithm == 'compl':
|
|
251
|
+
result = self._compute_compl()
|
|
252
|
+
elif algorithm == 'chunk':
|
|
253
|
+
result = self._compute_chunking(chunk_size)
|
|
254
|
+
elif algorithm == 'core':
|
|
255
|
+
result = self._compute_core_based()
|
|
256
|
+
elif algorithm == 'corechunk':
|
|
257
|
+
result = self._compute_core_chunking(chunk_size)
|
|
258
|
+
else:
|
|
259
|
+
assert 0, f'Unknown algorithm: {algorithm}'
|
|
260
|
+
|
|
261
|
+
return sorted(result, key=lambda l: abs(l))
|
|
262
|
+
|
|
263
|
+
def _get_implicant(self, model):
|
|
264
|
+
"""
|
|
265
|
+
Simple literal lifting used to reduce a given model.
|
|
266
|
+
"""
|
|
267
|
+
|
|
268
|
+
res, model = set(), set(model)
|
|
269
|
+
|
|
270
|
+
# traversing the clauses and collecting all literals
|
|
271
|
+
# that satisfy at least one clause of the formula
|
|
272
|
+
for cl in self.formula:
|
|
273
|
+
res |= set([l for l in cl if l in model])
|
|
274
|
+
|
|
275
|
+
return list(res)
|
|
276
|
+
|
|
277
|
+
def _filter_rotatable(self, model):
|
|
278
|
+
"""
|
|
279
|
+
Filter out rotatable literals.
|
|
280
|
+
"""
|
|
281
|
+
|
|
282
|
+
units, model = set([]), set(model)
|
|
283
|
+
|
|
284
|
+
# determining unit literals
|
|
285
|
+
for cl in self.formula:
|
|
286
|
+
satisfied = [l for l in cl if l in model]
|
|
287
|
+
if len(satisfied) == 1:
|
|
288
|
+
units.add(satisfied[0])
|
|
289
|
+
|
|
290
|
+
self.filtered += len(model) - len(units)
|
|
291
|
+
return list(units)
|
|
292
|
+
|
|
293
|
+
def _process_model(self, model):
|
|
294
|
+
"""
|
|
295
|
+
Heuristically reduce a model.
|
|
296
|
+
"""
|
|
297
|
+
|
|
298
|
+
if self.lift:
|
|
299
|
+
model = self._get_implicant(model)
|
|
300
|
+
|
|
301
|
+
if self.rotate:
|
|
302
|
+
model = self._filter_rotatable(model)
|
|
303
|
+
|
|
304
|
+
return model
|
|
305
|
+
|
|
306
|
+
def _compute_enum(self, focus_on=None):
|
|
307
|
+
"""
|
|
308
|
+
Enumeration-based backbone computation.
|
|
309
|
+
"""
|
|
310
|
+
|
|
311
|
+
if self.verbose:
|
|
312
|
+
print('c using enumeration-based algorithm')
|
|
313
|
+
|
|
314
|
+
# initial backbone estimate contains all literals
|
|
315
|
+
bbone = set(self.model) if focus_on is None else set(focus_on)
|
|
316
|
+
|
|
317
|
+
while bbone:
|
|
318
|
+
# counting the number of calls
|
|
319
|
+
self.calls += 1
|
|
320
|
+
|
|
321
|
+
# stop if there are no more models
|
|
322
|
+
if not self.oracle.solve():
|
|
323
|
+
break
|
|
324
|
+
|
|
325
|
+
coex = set(self.oracle.get_model())
|
|
326
|
+
self.model = [l for l in bbone if l in coex]
|
|
327
|
+
self.model = self._process_model(self.model)
|
|
328
|
+
|
|
329
|
+
# updating backbone estimate - intersection
|
|
330
|
+
bbone &= set(self.model)
|
|
331
|
+
|
|
332
|
+
# blocking the previously found implicant
|
|
333
|
+
self.oracle.add_clause([-l for l in self.model])
|
|
334
|
+
|
|
335
|
+
return list(bbone)
|
|
336
|
+
|
|
337
|
+
def _compute_iter(self, focus_on=None):
|
|
338
|
+
"""
|
|
339
|
+
Iterative algorithm with one test per variable.
|
|
340
|
+
"""
|
|
341
|
+
|
|
342
|
+
if self.verbose:
|
|
343
|
+
print('c using iterative algorithm')
|
|
344
|
+
|
|
345
|
+
bbone, model = [], self.model if focus_on is None else focus_on
|
|
346
|
+
|
|
347
|
+
while model:
|
|
348
|
+
# counting the number of oracle calls
|
|
349
|
+
self.calls += 1
|
|
350
|
+
|
|
351
|
+
# checking this literal
|
|
352
|
+
lit = model.pop()
|
|
353
|
+
|
|
354
|
+
if self.oracle.solve(assumptions=[-lit]) == False:
|
|
355
|
+
# it is a backbone literal
|
|
356
|
+
bbone.append(lit)
|
|
357
|
+
else:
|
|
358
|
+
# it isn't and we've got a counterexample
|
|
359
|
+
# => using it to filter out more literals
|
|
360
|
+
coex = set(self.oracle.get_model())
|
|
361
|
+
model = [l for l in model if l in coex]
|
|
362
|
+
model = self._process_model(model)
|
|
363
|
+
|
|
364
|
+
return bbone
|
|
365
|
+
|
|
366
|
+
def _compute_compl(self, focus_on=None):
|
|
367
|
+
"""
|
|
368
|
+
Iterative algorithm with complement of backbone estimate.
|
|
369
|
+
"""
|
|
370
|
+
|
|
371
|
+
if self.verbose:
|
|
372
|
+
print('c using complement of backbone estimate algorithm')
|
|
373
|
+
|
|
374
|
+
# initial estimate
|
|
375
|
+
bbone = set(self.model) if focus_on is None else set(focus_on)
|
|
376
|
+
|
|
377
|
+
# iterating until we find the backbone or determine there is none
|
|
378
|
+
while bbone:
|
|
379
|
+
self.calls += 1
|
|
380
|
+
|
|
381
|
+
# first, adding a new D-clause
|
|
382
|
+
self.oracle.add_clause([-l for l in bbone])
|
|
383
|
+
|
|
384
|
+
# testing for unsatisfiability
|
|
385
|
+
if self.oracle.solve() == False:
|
|
386
|
+
break
|
|
387
|
+
else:
|
|
388
|
+
coex = set(self.oracle.get_model())
|
|
389
|
+
model = [l for l in bbone if l in coex]
|
|
390
|
+
model = self._process_model(model)
|
|
391
|
+
bbone &= set(model)
|
|
392
|
+
|
|
393
|
+
return list(bbone)
|
|
394
|
+
|
|
395
|
+
def _compute_chunking(self, chunk_size=100, focus_on=None):
|
|
396
|
+
"""
|
|
397
|
+
Algorithm 5: Chunking algorithm.
|
|
398
|
+
"""
|
|
399
|
+
|
|
400
|
+
if self.verbose:
|
|
401
|
+
print('c using chunking algorithm, with chunk size:', chunk_size)
|
|
402
|
+
|
|
403
|
+
# initial estimate
|
|
404
|
+
bbone, model = [], self.model if focus_on is None else focus_on
|
|
405
|
+
|
|
406
|
+
# we are going to use clause selectors
|
|
407
|
+
vpool = IDPool(start_from=self.formula.nv + 1)
|
|
408
|
+
|
|
409
|
+
# iterating until we find the backbone or determine there is none
|
|
410
|
+
while model:
|
|
411
|
+
self.calls += 1
|
|
412
|
+
|
|
413
|
+
# preparing the call
|
|
414
|
+
size = min(chunk_size, len(model))
|
|
415
|
+
selv = vpool.id()
|
|
416
|
+
|
|
417
|
+
# first, adding a new D-clause for the selected chunk
|
|
418
|
+
self.oracle.add_clause([-model[-i] for i in range(1, size + 1)] + [-selv])
|
|
419
|
+
|
|
420
|
+
# testing for unsatisfiability
|
|
421
|
+
if self.oracle.solve(assumptions=[selv]) == False:
|
|
422
|
+
# all literals in the chunk are in the backbone
|
|
423
|
+
for _ in range(size):
|
|
424
|
+
lit = model.pop()
|
|
425
|
+
bbone.append(lit)
|
|
426
|
+
self.oracle.add_clause([lit])
|
|
427
|
+
else:
|
|
428
|
+
coex = set(self.oracle.get_model())
|
|
429
|
+
model = [l for l in model if l in coex]
|
|
430
|
+
model = self._process_model(model)
|
|
431
|
+
|
|
432
|
+
return bbone
|
|
433
|
+
|
|
434
|
+
def _compute_core_based(self, focus_on=None):
|
|
435
|
+
"""
|
|
436
|
+
Core-based algorithm.
|
|
437
|
+
"""
|
|
438
|
+
|
|
439
|
+
if self.verbose:
|
|
440
|
+
print('c using core-based algorithm')
|
|
441
|
+
|
|
442
|
+
# initial estimate
|
|
443
|
+
bbone, model = [], self.model if focus_on is None else focus_on
|
|
444
|
+
|
|
445
|
+
# iterating until we find the backbone or determine there is none
|
|
446
|
+
while model:
|
|
447
|
+
# flipping all the literals
|
|
448
|
+
assumps = [-l for l in model]
|
|
449
|
+
|
|
450
|
+
# getting unsatisfiable cores with them
|
|
451
|
+
while True:
|
|
452
|
+
self.calls += 1
|
|
453
|
+
|
|
454
|
+
if self.oracle.solve(assumptions=assumps):
|
|
455
|
+
coex = set(self.oracle.get_model())
|
|
456
|
+
model = [l for l in model if l in coex]
|
|
457
|
+
model = self._process_model(model)
|
|
458
|
+
break
|
|
459
|
+
|
|
460
|
+
else:
|
|
461
|
+
core = self.oracle.get_core()
|
|
462
|
+
if len(core) == 1:
|
|
463
|
+
bbone.append(-core[0])
|
|
464
|
+
self.oracle.add_clause([-core[0]])
|
|
465
|
+
|
|
466
|
+
# remove from the working model
|
|
467
|
+
indx = model.index(-core[0]) # may be slow
|
|
468
|
+
if indx < len(model) - 1:
|
|
469
|
+
model[indx] = model.pop()
|
|
470
|
+
else:
|
|
471
|
+
model.pop()
|
|
472
|
+
|
|
473
|
+
# filtering out unnecessary flipped literals
|
|
474
|
+
core = set(core)
|
|
475
|
+
assumps = [l for l in assumps if l not in core]
|
|
476
|
+
|
|
477
|
+
if not assumps:
|
|
478
|
+
# resorting to the iterative traversal algorithm
|
|
479
|
+
self.model = model
|
|
480
|
+
return bbone + self._compute_iter()
|
|
481
|
+
|
|
482
|
+
return bbone
|
|
483
|
+
|
|
484
|
+
def _compute_core_chunking(self, chunk_size=100, focus_on=None):
|
|
485
|
+
"""
|
|
486
|
+
Core-based algorithm with chunking.
|
|
487
|
+
"""
|
|
488
|
+
|
|
489
|
+
if self.verbose:
|
|
490
|
+
print('c using core-based chunking, with chunk size:', chunk_size)
|
|
491
|
+
|
|
492
|
+
# initial estimate
|
|
493
|
+
bbone, model = [], self.model if focus_on is None else focus_on
|
|
494
|
+
|
|
495
|
+
# we are going to use clause selectors
|
|
496
|
+
vpool = IDPool(start_from=self.formula.nv + 1)
|
|
497
|
+
|
|
498
|
+
# iterating until we find the backbone or determine there is none
|
|
499
|
+
while model:
|
|
500
|
+
# preparing the chunking
|
|
501
|
+
size = min(chunk_size, len(model))
|
|
502
|
+
|
|
503
|
+
# flipping all the literals
|
|
504
|
+
assumps, skipped = [-model.pop() for i in range(size)], []
|
|
505
|
+
|
|
506
|
+
# getting unsatisfiable cores with them
|
|
507
|
+
while True:
|
|
508
|
+
self.calls += 1
|
|
509
|
+
|
|
510
|
+
if self.oracle.solve(assumptions=assumps):
|
|
511
|
+
coex = set(self.oracle.get_model())
|
|
512
|
+
model = [l for l in model if l in coex]
|
|
513
|
+
model = self._process_model(model)
|
|
514
|
+
|
|
515
|
+
if skipped:
|
|
516
|
+
bbone += self._compute_iter(focus_on=skipped)
|
|
517
|
+
|
|
518
|
+
break
|
|
519
|
+
|
|
520
|
+
else:
|
|
521
|
+
core = self.oracle.get_core()
|
|
522
|
+
|
|
523
|
+
if len(core) == 1:
|
|
524
|
+
# a unit-size core must contain a backbone literal
|
|
525
|
+
bbone.append(-core[0])
|
|
526
|
+
self.oracle.add_clause([-core[0]])
|
|
527
|
+
else:
|
|
528
|
+
# all removed literals are going to be tested later
|
|
529
|
+
skipped += [-l for l in core]
|
|
530
|
+
|
|
531
|
+
# filtering out unnecessary flipped literals
|
|
532
|
+
core = set(core)
|
|
533
|
+
assumps = [l for l in assumps if l not in core]
|
|
534
|
+
|
|
535
|
+
if not assumps:
|
|
536
|
+
# resorting to the iterative traversal algorithm
|
|
537
|
+
# in order to test all the removed literals
|
|
538
|
+
bbone += self._compute_iter(focus_on=skipped)
|
|
539
|
+
break
|
|
540
|
+
|
|
541
|
+
return bbone
|
|
542
|
+
|
|
543
|
+
def oracle_time(self):
|
|
544
|
+
"""
|
|
545
|
+
This method computes and returns the total SAT solving time
|
|
546
|
+
involved, including the time spent by the hitting set enumerator
|
|
547
|
+
and the two SAT oracles.
|
|
548
|
+
|
|
549
|
+
:rtype: float
|
|
550
|
+
"""
|
|
551
|
+
|
|
552
|
+
return self.oracle.time_accum()
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
#
|
|
556
|
+
#==============================================================================
|
|
557
|
+
def parse_options():
|
|
558
|
+
"""
|
|
559
|
+
Parses command-line options.
|
|
560
|
+
"""
|
|
561
|
+
|
|
562
|
+
try:
|
|
563
|
+
opts, args = getopt.getopt(sys.argv[1:],
|
|
564
|
+
'a:c:hlrs:v',
|
|
565
|
+
['algo=',
|
|
566
|
+
'chunk=',
|
|
567
|
+
'help',
|
|
568
|
+
'lift',
|
|
569
|
+
'rotate',
|
|
570
|
+
'solver=',
|
|
571
|
+
'verbose'])
|
|
572
|
+
except getopt.GetoptError as err:
|
|
573
|
+
sys.stderr.write(str(err).capitalize() + '\n')
|
|
574
|
+
usage()
|
|
575
|
+
sys.exit(1)
|
|
576
|
+
|
|
577
|
+
algo = 'iter'
|
|
578
|
+
chunk = 100
|
|
579
|
+
lift = False
|
|
580
|
+
rotate = False
|
|
581
|
+
solver = 'g3'
|
|
582
|
+
verbose = 0
|
|
583
|
+
|
|
584
|
+
for opt, arg in opts:
|
|
585
|
+
if opt in ('-a', '--algo'):
|
|
586
|
+
algo = str(arg)
|
|
587
|
+
assert algo in ('enum', 'iter', 'compl', 'chunk', 'core', 'corechunk'), 'Unknown algorithm'
|
|
588
|
+
elif opt in ('-c', '--chunk'):
|
|
589
|
+
chunk = float(arg)
|
|
590
|
+
if chunk.is_integer():
|
|
591
|
+
chunk = int(chunk)
|
|
592
|
+
else:
|
|
593
|
+
assert 0 < chunk <= 1, f'Wrong chunk proportion {chunk_size}'
|
|
594
|
+
elif opt in ('-h', '--help'):
|
|
595
|
+
usage()
|
|
596
|
+
sys.exit(0)
|
|
597
|
+
elif opt in ('-l', '--lift'):
|
|
598
|
+
lift = True
|
|
599
|
+
elif opt in ('-r', '--rotate'):
|
|
600
|
+
rotate = True
|
|
601
|
+
elif opt in ('-s', '--solver'):
|
|
602
|
+
solver = str(arg)
|
|
603
|
+
elif opt in ('-v', '--verbose'):
|
|
604
|
+
verbose += 1
|
|
605
|
+
else:
|
|
606
|
+
assert False, 'Unhandled option: {0} {1}'.format(opt, arg)
|
|
607
|
+
|
|
608
|
+
return algo, chunk, lift, rotate, solver, verbose, args
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
#
|
|
612
|
+
#==============================================================================
|
|
613
|
+
def usage():
|
|
614
|
+
"""
|
|
615
|
+
Prints usage message.
|
|
616
|
+
"""
|
|
617
|
+
|
|
618
|
+
print('Usage:', os.path.basename(sys.argv[0]), '[options] file')
|
|
619
|
+
print('Options:')
|
|
620
|
+
print(' -a, --algo=<string> Algorithm to use')
|
|
621
|
+
print(' Available values: enum, iter, compl, chunk, core, corechunk (default: iter)')
|
|
622
|
+
print(' -c, --chunk=<int,float> Chunk size for chunking algorithms')
|
|
623
|
+
print(' Available values: [1 .. INT_MAX] or (0 .. 1] (default: 100)')
|
|
624
|
+
print(' -h, --help Show this message')
|
|
625
|
+
print(' -l, --lift Apply literal lifting for heuristic model reduction')
|
|
626
|
+
print(' -r, --rotate Heuristically filter out rotatable literals')
|
|
627
|
+
print(' -s, --solver=<string> SAT solver to use')
|
|
628
|
+
print(' Available values: cd15, cd19, g3, g4, lgl, mcb, mcm, mpl, m22, mc, mgh (default: g3)')
|
|
629
|
+
print(' -v, --verbose Be verbose (can be used multiple times)')
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
#
|
|
633
|
+
#==============================================================================
|
|
634
|
+
if __name__ == '__main__':
|
|
635
|
+
algo, chunk, lift, rotate, solver, verbose, files = parse_options()
|
|
636
|
+
|
|
637
|
+
if files:
|
|
638
|
+
# read CNF from file
|
|
639
|
+
assert re.search(r'cnf(\.(gz|bz2|lzma|xz|zst))?$', files[0]), \
|
|
640
|
+
'Unknown input file extension'
|
|
641
|
+
formula = CNF(from_file=files[0])
|
|
642
|
+
|
|
643
|
+
if verbose:
|
|
644
|
+
print('c formula: {0} vars, {1} clauses'.format(formula.nv,
|
|
645
|
+
len(formula.clauses)))
|
|
646
|
+
|
|
647
|
+
# computing the backbone
|
|
648
|
+
with BBScan(formula, solver=solver, lift=lift, rotate=rotate,
|
|
649
|
+
verbose=verbose) as bbscan:
|
|
650
|
+
try:
|
|
651
|
+
bbone = bbscan.compute(algorithm=algo, chunk_size=chunk)
|
|
652
|
+
|
|
653
|
+
except ValueError as e:
|
|
654
|
+
print('s UNSATISFIABLE')
|
|
655
|
+
print('c', str(e))
|
|
656
|
+
sys.exit(1)
|
|
657
|
+
|
|
658
|
+
# outputting the results
|
|
659
|
+
if bbone:
|
|
660
|
+
print('v {0} 0'.format(' '.join(['{0}{1}'.format('+' if v > 0 else '', v) for v in bbone])))
|
|
661
|
+
print('c backbone size: {0} ({1:.2f}% of all variables)'.format(len(bbone), 100. * len(bbone) / formula.nv))
|
|
662
|
+
else:
|
|
663
|
+
print('v 0')
|
|
664
|
+
|
|
665
|
+
if verbose > 1:
|
|
666
|
+
print('c filtered: {0} ({1:.2f})'.format(bbscan.filtered, 100. * bbscan.filtered / formula.nv))
|
|
667
|
+
|
|
668
|
+
print('c oracle time: {0:.4f}'.format(bbscan.oracle_time()))
|
|
669
|
+
print('c oracle calls: {0}'.format(bbscan.calls))
|
|
@@ -259,7 +259,6 @@ class Bica:
|
|
|
259
259
|
:type otrim: int
|
|
260
260
|
:type weighted: bool
|
|
261
261
|
:type verbose: int
|
|
262
|
-
|
|
263
262
|
"""
|
|
264
263
|
|
|
265
264
|
def __init__(self, formula, negated=None, target='cnf', psolver='cd19',
|
|
@@ -662,8 +661,8 @@ if __name__ == '__main__':
|
|
|
662
661
|
|
|
663
662
|
if files:
|
|
664
663
|
# read CNF from file
|
|
665
|
-
|
|
666
|
-
|
|
664
|
+
assert re.search(r'cnf(\.(gz|bz2|lzma|xz|zst))?$', files[0]), 'Unknown input file extension'
|
|
665
|
+
formula = CNF(from_file=files[0])
|
|
667
666
|
|
|
668
667
|
# creating an object of Primer
|
|
669
668
|
with Bica(formula, negated=None, target=mode, psolver=psolver,
|
|
@@ -506,7 +506,7 @@ if __name__ == '__main__':
|
|
|
506
506
|
|
|
507
507
|
if files:
|
|
508
508
|
# parsing the input formula
|
|
509
|
-
if re.search(r'\.wcnf[p|+]?(\.(gz|bz2|lzma|xz))?$', files[0]):
|
|
509
|
+
if re.search(r'\.wcnf[p|+]?(\.(gz|bz2|lzma|xz|zst))?$', files[0]):
|
|
510
510
|
formula = WCNFPlus(from_file=files[0])
|
|
511
511
|
else: # expecting '*.cnf[,p,+].*'
|
|
512
512
|
formula = CNFPlus(from_file=files[0]).weighted()
|
|
@@ -614,11 +614,11 @@ if __name__ == '__main__':
|
|
|
614
614
|
|
|
615
615
|
if files:
|
|
616
616
|
# reading standard CNF, WCNF, or (W)CNF+
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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
622
|
|
|
623
623
|
with LBX(formula, use_cld=dcalls, solver_name=solver, process=process,
|
|
624
624
|
use_timer=True) as mcsls:
|
|
@@ -458,8 +458,8 @@ if __name__ == '__main__':
|
|
|
458
458
|
|
|
459
459
|
if files:
|
|
460
460
|
# reading standard CNF or WCNF
|
|
461
|
-
if re.search(r'cnf(\.(gz|bz2|lzma|xz))?$', files[0]):
|
|
462
|
-
if re.search(r'\.wcnf(\.(gz|bz2|lzma|xz))?$', files[0]):
|
|
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
463
|
formula = WCNF(from_file=files[0])
|
|
464
464
|
else: # expecting '*.cnf'
|
|
465
465
|
formula = CNF(from_file=files[0]).weighted()
|
|
@@ -468,10 +468,12 @@ if __name__ == '__main__':
|
|
|
468
468
|
expect_interrupt=(timeout != None), verbose=verbose)
|
|
469
469
|
|
|
470
470
|
# reading WCNF+
|
|
471
|
-
elif re.search(r'\.wcnf[p,+](\.(gz|bz2|lzma|xz))?$', files[0]):
|
|
471
|
+
elif re.search(r'\.wcnf[p,+](\.(gz|bz2|lzma|xz|zst))?$', files[0]):
|
|
472
472
|
formula = WCNFPlus(from_file=files[0])
|
|
473
473
|
lsu = LSUPlus(formula, solver=solver, incr=incr,
|
|
474
474
|
expect_interrupt=(timeout != None), verbose=verbose)
|
|
475
|
+
else:
|
|
476
|
+
assert False, 'Unknown input file extension'
|
|
475
477
|
|
|
476
478
|
# setting a timer if necessary
|
|
477
479
|
if timeout is not None:
|
|
@@ -586,11 +586,11 @@ if __name__ == '__main__':
|
|
|
586
586
|
|
|
587
587
|
if files:
|
|
588
588
|
# reading standard CNF, WCNF, or (W)CNF+
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
589
|
+
assert re.search(r'cnf[p|+]?(\.(gz|bz2|lzma|xz|zst))?$', files[0]), 'Unknown input file extension'
|
|
590
|
+
if re.search(r'\.wcnf[p|+]?(\.(gz|bz2|lzma|xz|zst))?$', files[0]):
|
|
591
|
+
formula = WCNFPlus(from_file=files[0])
|
|
592
|
+
else: # expecting '*.cnf[,p,+].*'
|
|
593
|
+
formula = CNFPlus(from_file=files[0]).weighted()
|
|
594
594
|
|
|
595
595
|
with MCSls(formula, use_cld=dcalls, solver_name=solver,
|
|
596
596
|
process=process, use_timer=True) as mcsls:
|
|
@@ -327,7 +327,7 @@ if __name__ == '__main__':
|
|
|
327
327
|
|
|
328
328
|
if files:
|
|
329
329
|
# parsing the input formula
|
|
330
|
-
if re.search(r'\.wcnf[p|+]?(\.(gz|bz2|lzma|xz))?$', files[0]):
|
|
330
|
+
if re.search(r'\.wcnf[p|+]?(\.(gz|bz2|lzma|xz|zst))?$', files[0]):
|
|
331
331
|
formula = WCNFPlus(from_file=files[0])
|
|
332
332
|
else: # expecting '*.cnf[,p,+].*'
|
|
333
333
|
formula = CNFPlus(from_file=files[0]).weighted()
|
|
@@ -676,14 +676,14 @@ if __name__ == '__main__':
|
|
|
676
676
|
|
|
677
677
|
if files:
|
|
678
678
|
# reading standard CNF, WCNF, or (W)CNF+
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
679
|
+
assert re.search(r'cnf[p|+]?(\.(gz|bz2|lzma|xz|zst))?$', files[0]), 'Unknown input file extension'
|
|
680
|
+
if re.search(r'\.wcnf[p|+]?(\.(gz|bz2|lzma|xz|zst))?$', files[0]):
|
|
681
|
+
formula = WCNFPlus(from_file=files[0])
|
|
682
|
+
else: # expecting '*.cnf[,p,+].*'
|
|
683
|
+
formula = CNFPlus(from_file=files[0]).weighted()
|
|
684
684
|
|
|
685
685
|
if cover: # expecting '*.cnf[,p,+].*' only!
|
|
686
|
-
assert re.search(r'cnf[p|+]?(\.(gz|bz2|lzma|xz))?$', cover), '
|
|
686
|
+
assert re.search(r'cnf[p|+]?(\.(gz|bz2|lzma|xz|zst))?$', cover), 'Wrong file for formula to cover'
|
|
687
687
|
cover = CNFPlus(from_file=cover)
|
|
688
688
|
|
|
689
689
|
# creating an object of OptUx
|
|
@@ -594,8 +594,8 @@ if __name__ == '__main__':
|
|
|
594
594
|
|
|
595
595
|
if files:
|
|
596
596
|
# read CNF from file
|
|
597
|
-
|
|
598
|
-
|
|
597
|
+
assert re.search(r'cnf(\.(gz|bz2|lzma|xz|zst))?$', files[0]), 'Unknown input file extension'
|
|
598
|
+
formula = CNF(from_file=files[0])
|
|
599
599
|
|
|
600
600
|
# creating an object of Primer
|
|
601
601
|
with Primer(formula, negated=None, solver=solver, implicates=mode,
|
|
@@ -1788,7 +1788,7 @@ if __name__ == '__main__':
|
|
|
1788
1788
|
|
|
1789
1789
|
if files:
|
|
1790
1790
|
# parsing the input formula
|
|
1791
|
-
if re.search(r'\.wcnf[p|+]?(\.(gz|bz2|lzma|xz))?$', files[0]):
|
|
1791
|
+
if re.search(r'\.wcnf[p|+]?(\.(gz|bz2|lzma|xz|zst))?$', files[0]):
|
|
1792
1792
|
formula = WCNFPlus(from_file=files[0])
|
|
1793
1793
|
else: # expecting '*.cnf[,p,+].*'
|
|
1794
1794
|
formula = CNFPlus(from_file=files[0]).weighted()
|
|
@@ -75,8 +75,8 @@ to_install = ['cadical103', 'cadical153', 'cadical195', 'gluecard30',
|
|
|
75
75
|
|
|
76
76
|
# example and allies scripts to install as standalone executables
|
|
77
77
|
#==============================================================================
|
|
78
|
-
example_scripts = ['bica', 'fm', 'genhard', 'lbx', 'lsu', 'mcsls',
|
|
79
|
-
'musx', 'optux', 'primer', 'rc2']
|
|
78
|
+
example_scripts = ['bbscan', 'bica', 'fm', 'genhard', 'lbx', 'lsu', 'mcsls',
|
|
79
|
+
'models', 'musx', 'optux', 'primer', 'rc2']
|
|
80
80
|
allies_scripts = ['approxmc', 'unigen']
|
|
81
81
|
|
|
82
82
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|