python-sat 1.9.dev3__cp313-cp313-win_amd64.whl → 1.9.dev12__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.
- pycard.cp313-win_amd64.pyd +0 -0
- pyformula.cp313-win_amd64.pyd +0 -0
- pysat/__init__.py +1 -1
- pysat/engines.py +35 -9
- pysat/examples/rc2.py +63 -27
- pysat/formula.py +226 -106
- pysat/solvers.py +3 -3
- pysolvers.cp313-win_amd64.pyd +0 -0
- {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/rc2.py +63 -27
- {python_sat-1.9.dev3.dist-info → python_sat-1.9.dev12.dist-info}/METADATA +1 -1
- python_sat-1.9.dev12.dist-info/RECORD +50 -0
- {python_sat-1.9.dev3.dist-info → python_sat-1.9.dev12.dist-info}/WHEEL +1 -1
- python_sat-1.9.dev3.dist-info/RECORD +0 -50
- {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/approxmc.py +0 -0
- {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/bbscan.py +0 -0
- {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/bica.py +0 -0
- {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/fm.py +0 -0
- {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/genhard.py +0 -0
- {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/lbx.py +0 -0
- {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/lsu.py +0 -0
- {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/mcsls.py +0 -0
- {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/models.py +0 -0
- {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/musx.py +0 -0
- {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/optux.py +0 -0
- {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/primer.py +0 -0
- {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/unigen.py +0 -0
- {python_sat-1.9.dev3.dist-info → python_sat-1.9.dev12.dist-info}/licenses/LICENSE.txt +0 -0
- {python_sat-1.9.dev3.dist-info → python_sat-1.9.dev12.dist-info}/top_level.txt +0 -0
pycard.cp313-win_amd64.pyd
CHANGED
|
Binary file
|
pyformula.cp313-win_amd64.pyd
CHANGED
|
Binary file
|
pysat/__init__.py
CHANGED
pysat/engines.py
CHANGED
|
@@ -564,8 +564,11 @@ class Propagator(object):
|
|
|
564
564
|
def provide_reason(self, lit: int) -> List[int]:
|
|
565
565
|
pass # explain why a given literal was propagated
|
|
566
566
|
|
|
567
|
+
def has_clause(self) -> bool:
|
|
568
|
+
return False # optional: report whether an external clause exists
|
|
569
|
+
|
|
567
570
|
def add_clause(self) -> List[int]:
|
|
568
|
-
return [] #
|
|
571
|
+
return [] # if has_clause() is used, may also be the empty clause
|
|
569
572
|
"""
|
|
570
573
|
|
|
571
574
|
def __init__(self):
|
|
@@ -687,10 +690,9 @@ class Propagator(object):
|
|
|
687
690
|
|
|
688
691
|
def add_clause(self) -> List[int]:
|
|
689
692
|
"""
|
|
690
|
-
The method is called by the solver to
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
learning it.
|
|
693
|
+
The method is called by the solver to obtain an external clause.
|
|
694
|
+
The clause can be arbitrary but if it is root-satisfied or
|
|
695
|
+
tautological, the solver will ignore it without learning it.
|
|
694
696
|
|
|
695
697
|
Root-falsified literals are eagerly removed from the clause.
|
|
696
698
|
Falsified clauses trigger conflict analysis, propagating clauses
|
|
@@ -700,6 +702,19 @@ class Propagator(object):
|
|
|
700
702
|
An empty clause (or root falsified clause, see above) makes the
|
|
701
703
|
formula unsatisfiable and stops the search immediately.
|
|
702
704
|
|
|
705
|
+
.. note::
|
|
706
|
+
|
|
707
|
+
The exact meaning of returning ``[]`` depends on whether the
|
|
708
|
+
propagator defines method ``has_clause()``.
|
|
709
|
+
|
|
710
|
+
- If ``has_clause()`` is defined, the solver first queries it.
|
|
711
|
+
Whenever it returns ``True``, the follow-up call of
|
|
712
|
+
:meth:`add_clause` is expected to return the clause itself,
|
|
713
|
+
and ``[]`` denotes the actual empty clause.
|
|
714
|
+
- Otherwise, if ``has_clause()`` is undefined, returning
|
|
715
|
+
``[]`` from :meth:`add_clause` is interpreted as saying that
|
|
716
|
+
there is no clause to add.
|
|
717
|
+
|
|
703
718
|
:rtype: iterable(int)
|
|
704
719
|
"""
|
|
705
720
|
|
|
@@ -1283,14 +1298,25 @@ class BooleanEngine(Propagator):
|
|
|
1283
1298
|
|
|
1284
1299
|
return [lit] + self.origin[lit].justify(lit)
|
|
1285
1300
|
|
|
1301
|
+
def has_clause(self):
|
|
1302
|
+
"""
|
|
1303
|
+
Check whether the engine currently has an external clause to add.
|
|
1304
|
+
When queried separately by the solver, this lets
|
|
1305
|
+
:meth:`add_clause` return ``[]`` as an actual empty clause.
|
|
1306
|
+
"""
|
|
1307
|
+
|
|
1308
|
+
return self.falsified is not None
|
|
1309
|
+
|
|
1286
1310
|
def add_clause(self):
|
|
1287
1311
|
"""
|
|
1288
|
-
Extract
|
|
1289
|
-
|
|
1312
|
+
Extract the external clause previously announced by
|
|
1313
|
+
:meth:`has_clause`.
|
|
1290
1314
|
"""
|
|
1291
1315
|
|
|
1292
|
-
if
|
|
1293
|
-
|
|
1316
|
+
# # old behaviour checking if we have a clause to add;
|
|
1317
|
+
# # commenting this out because we have has_clause() now
|
|
1318
|
+
# if self.falsified is None:
|
|
1319
|
+
# return []
|
|
1294
1320
|
|
|
1295
1321
|
to_add = self.falsified.explain_failure()
|
|
1296
1322
|
|
pysat/examples/rc2.py
CHANGED
|
@@ -131,9 +131,9 @@
|
|
|
131
131
|
#
|
|
132
132
|
#==============================================================================
|
|
133
133
|
from __future__ import print_function
|
|
134
|
+
import bisect
|
|
134
135
|
import collections
|
|
135
136
|
import getopt
|
|
136
|
-
import itertools
|
|
137
137
|
from math import copysign
|
|
138
138
|
import os
|
|
139
139
|
from pysat.formula import CNFPlus, WCNFPlus, IDPool
|
|
@@ -408,6 +408,9 @@ class RC2(object):
|
|
|
408
408
|
:type clause: iterable(int)
|
|
409
409
|
:type weight: int
|
|
410
410
|
|
|
411
|
+
:return: selector literal for a soft clause and ``None`` for a hard one
|
|
412
|
+
:rtype: int or None
|
|
413
|
+
|
|
411
414
|
.. code-block:: python
|
|
412
415
|
|
|
413
416
|
>>> from pysat.examples.rc2 import RC2
|
|
@@ -448,6 +451,8 @@ class RC2(object):
|
|
|
448
451
|
'{0} does not support native cardinality constraints. Make sure you use the right type of formula.'.format(self.solver)
|
|
449
452
|
|
|
450
453
|
self.oracle.add_atmost(cl, clause[1], weights=clause[2] if len(clause) == 3 else [])
|
|
454
|
+
|
|
455
|
+
return None
|
|
451
456
|
else:
|
|
452
457
|
# soft clauses should be augmented with a selector
|
|
453
458
|
selv = cl[0] # for a unit clause, no selector is needed
|
|
@@ -471,6 +476,8 @@ class RC2(object):
|
|
|
471
476
|
self.sall.append(selv)
|
|
472
477
|
self.sels_set.add(selv)
|
|
473
478
|
|
|
479
|
+
return selv
|
|
480
|
+
|
|
474
481
|
def delete(self):
|
|
475
482
|
"""
|
|
476
483
|
Explicit destructor of the internal SAT oracle and all the
|
|
@@ -732,9 +739,12 @@ class RC2(object):
|
|
|
732
739
|
self.minw = min(map(lambda l: self.wght[l], self.core))
|
|
733
740
|
|
|
734
741
|
# dividing the core into two parts
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
742
|
+
self.core_sels, self.core_sums = [], []
|
|
743
|
+
for l in self.core:
|
|
744
|
+
if l in self.sels_set:
|
|
745
|
+
self.core_sels.append(l)
|
|
746
|
+
else:
|
|
747
|
+
self.core_sums.append(l)
|
|
738
748
|
|
|
739
749
|
def process_core(self):
|
|
740
750
|
"""
|
|
@@ -810,8 +820,8 @@ class RC2(object):
|
|
|
810
820
|
handled by calling :func:`process_am1`.
|
|
811
821
|
"""
|
|
812
822
|
|
|
813
|
-
# literal connections
|
|
814
|
-
conns = collections.defaultdict(
|
|
823
|
+
# literal connections and selectors conflicting on their own
|
|
824
|
+
conns = collections.defaultdict(list)
|
|
815
825
|
confl = []
|
|
816
826
|
|
|
817
827
|
# prepare connections
|
|
@@ -819,26 +829,29 @@ class RC2(object):
|
|
|
819
829
|
st, props = self.oracle.propagate(assumptions=[l1], phase_saving=2)
|
|
820
830
|
if st:
|
|
821
831
|
for l2 in props:
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
conns[
|
|
832
|
+
l2 = -l2
|
|
833
|
+
if l2 in self.sels_set:
|
|
834
|
+
conns[l1].append(l2)
|
|
835
|
+
conns[l2].append(l1)
|
|
825
836
|
else:
|
|
826
837
|
# propagating this literal results in a conflict
|
|
827
838
|
confl.append(l1)
|
|
828
839
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
840
|
+
# sort (in-place) and deduplicate adjacency lists
|
|
841
|
+
for l in list(conns):
|
|
842
|
+
neigh = conns[l]
|
|
843
|
+
if neigh:
|
|
844
|
+
neigh.sort()
|
|
832
845
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
846
|
+
j = 1
|
|
847
|
+
for i in range(1, len(neigh)):
|
|
848
|
+
if neigh[i] != neigh[j - 1]:
|
|
849
|
+
neigh[j] = neigh[i]
|
|
850
|
+
j += 1
|
|
838
851
|
|
|
839
|
-
|
|
840
|
-
confl = list(confl)
|
|
852
|
+
del neigh[j:]
|
|
841
853
|
|
|
854
|
+
if confl:
|
|
842
855
|
# processing unit size cores
|
|
843
856
|
for l in confl:
|
|
844
857
|
self.core, self.minw = [l], self.wght[l]
|
|
@@ -849,24 +862,47 @@ class RC2(object):
|
|
|
849
862
|
print('c unit cores found: {0}; cost: {1}'.format(len(confl),
|
|
850
863
|
self.cost))
|
|
851
864
|
|
|
865
|
+
# the following graph manipulation requires
|
|
866
|
+
# fast membership checks for confl; making it a set
|
|
867
|
+
confl = set(confl)
|
|
868
|
+
|
|
869
|
+
# current degrees in the remaining graph;
|
|
870
|
+
# literals disconnected after removing conflicts are ignored
|
|
871
|
+
degree, lits = {}, set()
|
|
872
|
+
for l in conns:
|
|
873
|
+
if l not in confl:
|
|
874
|
+
d = sum(1 for l2 in conns[l] if l2 not in confl)
|
|
875
|
+
if d:
|
|
876
|
+
degree[l] = d
|
|
877
|
+
lits.add(l)
|
|
878
|
+
|
|
879
|
+
# detect AM1s on the original formula with conflicting selectors
|
|
880
|
+
# filtered out, similarly to the previous implementation
|
|
852
881
|
nof_am1 = 0
|
|
853
882
|
len_am1 = []
|
|
854
|
-
|
|
883
|
+
|
|
855
884
|
while lits:
|
|
856
|
-
am1 = [min(lits, key=lambda l:
|
|
885
|
+
am1 = [min(lits, key=lambda l: degree[l])]
|
|
857
886
|
|
|
858
|
-
for l in sorted(conns[am1[0]], key=lambda l:
|
|
887
|
+
for l in sorted(conns[am1[0]], key=lambda l: degree.get(l, 0)):
|
|
859
888
|
if l in lits:
|
|
860
889
|
for l_added in am1[1:]:
|
|
861
|
-
|
|
890
|
+
# adjacency lists are sorted, so we use
|
|
891
|
+
# binary search for clique membership tests
|
|
892
|
+
i = bisect.bisect_left(conns[l], l_added)
|
|
893
|
+
if i == len(conns[l]) or conns[l][i] != l_added:
|
|
862
894
|
break
|
|
863
895
|
else:
|
|
864
896
|
am1.append(l)
|
|
865
897
|
|
|
866
|
-
# updating remaining
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
898
|
+
# updating remaining literals and their degrees
|
|
899
|
+
am1_set = set(am1)
|
|
900
|
+
lits.difference_update(am1_set)
|
|
901
|
+
|
|
902
|
+
for l in am1:
|
|
903
|
+
for l2 in conns.get(l, []):
|
|
904
|
+
if l2 in lits:
|
|
905
|
+
degree[l2] -= 1
|
|
870
906
|
|
|
871
907
|
if len(am1) > 1:
|
|
872
908
|
# treat the new atmost1 relation
|
pysat/formula.py
CHANGED
|
@@ -810,6 +810,13 @@ class Formula(object):
|
|
|
810
810
|
:type type: :class:`FormulaType`
|
|
811
811
|
"""
|
|
812
812
|
|
|
813
|
+
# Formula.__new__ may return an existing structurally identical
|
|
814
|
+
# formula; by default, Python still invokes __init__ in that case,
|
|
815
|
+
# which is harmful; if an identical object exists, we should
|
|
816
|
+
# immediately return
|
|
817
|
+
if hasattr(self, 'type'):
|
|
818
|
+
return
|
|
819
|
+
|
|
813
820
|
assert 'type' in kwargs, 'No \'type\' of the formula is provided'
|
|
814
821
|
self.type = kwargs['type']
|
|
815
822
|
|
|
@@ -883,11 +890,11 @@ class Formula(object):
|
|
|
883
890
|
@staticmethod
|
|
884
891
|
def _get_key(*args, **kwargs):
|
|
885
892
|
"""
|
|
886
|
-
The method is used to extract and return
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
893
|
+
The method is used to extract and return an immutable structural
|
|
894
|
+
key associated with the formula requested by a user. The key is
|
|
895
|
+
composed of tuples, frozensets, and scalar values rather than
|
|
896
|
+
string representations. The method is meant to be internal and
|
|
897
|
+
not to be used by formula users.
|
|
891
898
|
"""
|
|
892
899
|
|
|
893
900
|
def _hashable(entity):
|
|
@@ -918,7 +925,6 @@ class Formula(object):
|
|
|
918
925
|
if isinstance(value, Iterable) and value:
|
|
919
926
|
items.extend(_flatten(value, prefix=new_key, sep=sep))
|
|
920
927
|
else:
|
|
921
|
-
|
|
922
928
|
items.append((new_key, value))
|
|
923
929
|
|
|
924
930
|
# finally, it is a simple non-empty object
|
|
@@ -945,58 +951,136 @@ class Formula(object):
|
|
|
945
951
|
elif item[0] != 'merge':
|
|
946
952
|
subfs.append(item)
|
|
947
953
|
else:
|
|
948
|
-
extra = [('merge',
|
|
954
|
+
extra = [('merge', item[1])]
|
|
955
|
+
|
|
956
|
+
def _object_key(obj):
|
|
957
|
+
"""
|
|
958
|
+
Get a hashable key for an atom object.
|
|
959
|
+
"""
|
|
960
|
+
|
|
961
|
+
if type(obj) in (bool, int, float, str, bytes, type(None)):
|
|
962
|
+
return ('val', type(obj), obj)
|
|
963
|
+
if type(obj) in (tuple, list):
|
|
964
|
+
return ('seq', tuple(_object_key(item) for item in obj))
|
|
965
|
+
if type(obj) in (set, frozenset):
|
|
966
|
+
return ('set', frozenset(_object_key(item) for item in obj))
|
|
967
|
+
if isinstance(obj, dict):
|
|
968
|
+
return ('map', frozenset((_object_key(key), _object_key(val)) for key, val in obj.items()))
|
|
969
|
+
try:
|
|
970
|
+
hash(obj)
|
|
971
|
+
except TypeError:
|
|
972
|
+
return ('identity', type(obj), id(obj))
|
|
973
|
+
return ('obj', type(obj), obj)
|
|
974
|
+
|
|
975
|
+
def _formula_key_uncached(form):
|
|
976
|
+
"""
|
|
977
|
+
Get a structural key without a formula's merge flag.
|
|
978
|
+
"""
|
|
979
|
+
|
|
980
|
+
if not isinstance(form, Formula):
|
|
981
|
+
return ('identity', type(form), id(form))
|
|
982
|
+
|
|
983
|
+
if form.type == FormulaType.CNF:
|
|
984
|
+
# CNF and CNFPlus are mutable containers and deliberately
|
|
985
|
+
# bypass Formula's unique-object constructor; using their
|
|
986
|
+
# object identity as it looks like the cheapest option
|
|
987
|
+
return ('cnf-object', type(form), id(form))
|
|
988
|
+
|
|
989
|
+
if form.type == FormulaType.ATOM:
|
|
990
|
+
return (FormulaType.ATOM, _object_key(form.object))
|
|
991
|
+
if form.type == FormulaType.NEG:
|
|
992
|
+
return (FormulaType.NEG, _formula_key(form.subformula))
|
|
993
|
+
if form.type == FormulaType.IMPL:
|
|
994
|
+
return (FormulaType.IMPL, _formula_key(form.left),
|
|
995
|
+
_formula_key(form.right))
|
|
996
|
+
if form.type == FormulaType.ITE:
|
|
997
|
+
return (FormulaType.ITE, _formula_key(form.cond),
|
|
998
|
+
_formula_key(form.cons1),
|
|
999
|
+
_formula_key(form.cons2))
|
|
1000
|
+
|
|
1001
|
+
counts = collections.Counter(_formula_key(subform) for subform in form.subformulas)
|
|
1002
|
+
return (form.type, frozenset(counts.items()))
|
|
1003
|
+
|
|
1004
|
+
def _formula_key(form):
|
|
1005
|
+
"""
|
|
1006
|
+
Get a cached structural key for an immutable formula.
|
|
1007
|
+
"""
|
|
1008
|
+
|
|
1009
|
+
if isinstance(form, Formula):
|
|
1010
|
+
key = getattr(form, '_key_cache', None)
|
|
1011
|
+
if key is None:
|
|
1012
|
+
key = _formula_key_uncached(form)
|
|
1013
|
+
form._key_cache = key
|
|
1014
|
+
|
|
1015
|
+
return key
|
|
1016
|
+
|
|
1017
|
+
return _formula_key_uncached(form)
|
|
1018
|
+
|
|
1019
|
+
def _merged_operands(operands, formula_type):
|
|
1020
|
+
"""Flatten same-type operands for a merged connective."""
|
|
1021
|
+
result = []
|
|
1022
|
+
for operand in operands:
|
|
1023
|
+
if isinstance(operand, Formula) and operand.type == formula_type:
|
|
1024
|
+
result.extend(_merged_operands(operand.subformulas,
|
|
1025
|
+
formula_type))
|
|
1026
|
+
else:
|
|
1027
|
+
result.append(_formula_key(operand))
|
|
1028
|
+
return result
|
|
949
1029
|
|
|
950
|
-
# the
|
|
951
|
-
|
|
952
|
-
if
|
|
1030
|
+
# Reconstruct a structural key depending on the Formula type.
|
|
1031
|
+
formula_type = ftype[1]
|
|
1032
|
+
if formula_type == FormulaType.ATOM:
|
|
953
1033
|
assert len(subfs) == 1, 'A single object is required for an Atom formula'
|
|
954
|
-
|
|
955
|
-
|
|
1034
|
+
return (formula_type, _object_key(subfs[0][1]))
|
|
1035
|
+
|
|
1036
|
+
if formula_type == FormulaType.NEG:
|
|
956
1037
|
assert len(subfs) == 1, 'A single subformula is required for a Neg formula'
|
|
957
|
-
|
|
958
|
-
|
|
1038
|
+
return (formula_type, _formula_key(subfs[0][1]))
|
|
1039
|
+
|
|
1040
|
+
if formula_type == FormulaType.IMPL:
|
|
959
1041
|
assert len(subfs) == 2, 'Two subformulas are required for an Implies formula'
|
|
960
1042
|
if subfs[0][0] == '' and subfs[1][0] == '':
|
|
961
|
-
|
|
962
|
-
subfs[1] = ('right', id(subfs[1][1]))
|
|
1043
|
+
operands = [('left', subfs[0][1]), ('right', subfs[1][1])]
|
|
963
1044
|
elif subfs[1][0] == 'left':
|
|
964
|
-
|
|
1045
|
+
operands = [('right', subfs[0][1]), subfs[1]]
|
|
965
1046
|
elif subfs[1][0] == 'right':
|
|
966
|
-
|
|
967
|
-
|
|
1047
|
+
operands = [('left', subfs[0][1]), subfs[1]]
|
|
1048
|
+
else:
|
|
1049
|
+
operands = subfs
|
|
1050
|
+
role_order = {'left': 0, 'right': 1}
|
|
1051
|
+
return (formula_type, tuple( (label, _formula_key(value)) for \
|
|
1052
|
+
label, value in sorted(operands,
|
|
1053
|
+
key=lambda pair: role_order[pair[0]])))
|
|
1054
|
+
|
|
1055
|
+
if formula_type == FormulaType.ITE:
|
|
968
1056
|
assert len(subfs) == 3, 'Three subformulas are required for an ITE formula'
|
|
969
|
-
if
|
|
970
|
-
subfs
|
|
971
|
-
subfs[1] = ('cons1', id(subfs[1][1]))
|
|
972
|
-
subfs[2] = ('cons2', id(subfs[2][1]))
|
|
1057
|
+
if all(item[0] == '' for item in subfs):
|
|
1058
|
+
subfs = [('cond', subfs[0][1]), ('cons1', subfs[1][1]), ('cons2', subfs[2][1])]
|
|
973
1059
|
elif subfs[0][0] == subfs[1][0] == '':
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1060
|
+
role = subfs[2][0]
|
|
1061
|
+
missing = [r for r in ('cond', 'cons1', 'cons2') if r != role]
|
|
1062
|
+
subfs = [(missing[0], subfs[0][1]), (missing[1], subfs[1][1]), subfs[2]]
|
|
1063
|
+
elif subfs[0][0] == '':
|
|
1064
|
+
roles = {item[0] for item in subfs[1:]}
|
|
1065
|
+
missing = next(r for r in ('cond', 'cons1', 'cons2') if r not in roles)
|
|
1066
|
+
subfs[0] = (missing, subfs[0][1])
|
|
1067
|
+
|
|
1068
|
+
role_order = {'cond': 0, 'cons1': 1, 'cons2': 2}
|
|
1069
|
+
return (formula_type, tuple((label, _formula_key(value)) for \
|
|
1070
|
+
label, value in sorted(subfs,
|
|
1071
|
+
key=lambda pair: role_order[pair[0]])))
|
|
1072
|
+
|
|
1073
|
+
# these are commutative connectives; representing operands as a
|
|
1074
|
+
# multiset so that argument order is irrelevant while duplicate
|
|
1075
|
+
# operands remain significant; a merged connective also flattens nested operands.
|
|
1076
|
+
assert len(subfs) >= 1, 'At least one subformula is required for an And/Equals/Or/XOr formula'
|
|
1077
|
+
merge = dict(extra).get('merge', False)
|
|
1078
|
+
operands = [value for _, value in subfs]
|
|
1079
|
+
if merge:
|
|
1080
|
+
operands = _merged_operands(operands, formula_type)
|
|
990
1081
|
else:
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
subfs = sorted(map(lambda p: (p[0], repr(id(p[1]))), subfs))
|
|
994
|
-
|
|
995
|
-
if not extra:
|
|
996
|
-
extra = [('merge', repr(False))]
|
|
997
|
-
|
|
998
|
-
# the key is a string combining all the parts
|
|
999
|
-
return ' '.join([f'{repr(p[0])}:{repr(p[1])}' for p in [ftype] + subfs + extra])
|
|
1082
|
+
operands = [_formula_key(operand) for operand in operands]
|
|
1083
|
+
return (formula_type, frozenset(collections.Counter(operands).items()), merge)
|
|
1000
1084
|
|
|
1001
1085
|
def __hash__(self):
|
|
1002
1086
|
"""
|
|
@@ -1708,50 +1792,83 @@ class And(Formula):
|
|
|
1708
1792
|
|
|
1709
1793
|
def _clausify(self, name_required=True):
|
|
1710
1794
|
"""
|
|
1711
|
-
|
|
1795
|
+
Conjunction clausification.
|
|
1712
1796
|
|
|
1713
1797
|
If ``name_required`` is ``False``, the method recursively encodes
|
|
1714
|
-
the subformulas
|
|
1715
|
-
|
|
1716
|
-
subformula, thus
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1798
|
+
the subformulas expanding self's list of clauses and also
|
|
1799
|
+
populates self's clauses with unit clauses, each containing the
|
|
1800
|
+
auxiliary "name" of the corresponding subformula, thus
|
|
1801
|
+
representing their conjunction. However, :class:`CNF` formulas
|
|
1802
|
+
are expanded without introducing an intermediate Boolean name -
|
|
1803
|
+
instead, they contribute their clausal representations to the
|
|
1804
|
+
list of self's clauses unchanged.
|
|
1805
|
+
|
|
1806
|
+
If ``name_required`` is set to ``True``, the method introduces one
|
|
1807
|
+
name for the complete conjunction and encodes the equivalence
|
|
1808
|
+
between that name and the collected clauses.
|
|
1723
1809
|
|
|
1724
1810
|
:param name_required: whether or not a Tseitin variable is needed
|
|
1725
|
-
:
|
|
1811
|
+
:type name_required: bool
|
|
1726
1812
|
"""
|
|
1727
1813
|
|
|
1728
1814
|
save_clauses = bool(self.clauses)
|
|
1729
1815
|
|
|
1730
1816
|
if not self.clauses:
|
|
1731
|
-
#
|
|
1817
|
+
# recursively collect a clausal representation; CNF objects are
|
|
1818
|
+
# already clausal, so they do not need a Boolean name at this
|
|
1819
|
+
# level
|
|
1732
1820
|
for sub in self.subformulas:
|
|
1733
|
-
sub
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1821
|
+
if type(sub) is CNF:
|
|
1822
|
+
sub._clausify(name_required=False)
|
|
1823
|
+
self.clauses.extend(cl.copy() for cl in sub.clauses)
|
|
1824
|
+
else:
|
|
1825
|
+
sub._clausify(name_required=True)
|
|
1826
|
+
self.clauses.append([sub.name])
|
|
1737
1827
|
|
|
1738
|
-
# introducing a
|
|
1828
|
+
# introducing a name is needed only when this conjunction is itself
|
|
1829
|
+
# used by another connective; encode each non-unit clause with its own
|
|
1830
|
+
# selector, then encode the conjunction of those selectors with the
|
|
1831
|
+
# single name for this `And` formula
|
|
1739
1832
|
if name_required and not self.name:
|
|
1740
|
-
|
|
1741
|
-
self.encoded = [clause.copy() for clause in self.clauses]
|
|
1742
|
-
else:
|
|
1743
|
-
self.encoded = self.clauses
|
|
1744
|
-
self.clauses = []
|
|
1833
|
+
self.encoded, enclits = [], []
|
|
1745
1834
|
|
|
1746
|
-
|
|
1835
|
+
for cl in self.clauses:
|
|
1836
|
+
auxv = cl[0]
|
|
1837
|
+
if len(cl) > 1:
|
|
1838
|
+
auxv = Formula._vpool[Formula._context].id()
|
|
1839
|
+
|
|
1840
|
+
# direct implication
|
|
1841
|
+
for l in cl:
|
|
1842
|
+
self.encoded.append([-l, auxv])
|
|
1843
|
+
|
|
1844
|
+
# opposite implication
|
|
1845
|
+
self.encoded.append(cl + [-auxv])
|
|
1747
1846
|
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
cl.append(-self.encoded[i][0]) # updating final clause
|
|
1751
|
-
self.encoded[i].append(-self.name) # updating direct implication
|
|
1847
|
+
# literals representing the clauses
|
|
1848
|
+
enclits.append(auxv)
|
|
1752
1849
|
|
|
1753
|
-
#
|
|
1754
|
-
|
|
1850
|
+
# encoding the conjunction
|
|
1851
|
+
if len(enclits) > 1:
|
|
1852
|
+
self.name = Formula._vpool[Formula._context].id(self)
|
|
1853
|
+
|
|
1854
|
+
for lit in enclits:
|
|
1855
|
+
self.encoded.append([lit, -self.name])
|
|
1856
|
+
self.encoded.append([self.name] + [-lit for lit in enclits])
|
|
1857
|
+
elif len(enclits) == 1: # single clause - nothing left to encode
|
|
1858
|
+
self.name = enclits[0] # existing variable
|
|
1859
|
+
|
|
1860
|
+
# connecting it to the `And` object as its name
|
|
1861
|
+
Formula._vpool[Formula._context].obj2id[self] = self.name
|
|
1862
|
+
if self.name not in Formula._vpool[Formula._context].id2obj:
|
|
1863
|
+
Formula._vpool[Formula._context].id2obj[self.name] = self
|
|
1864
|
+
else:
|
|
1865
|
+
self.name = PYSAT_TRUE.name
|
|
1866
|
+
|
|
1867
|
+
# preserve clauses that existed before this call;
|
|
1868
|
+
# they are still needed if the same formula is later iterated as
|
|
1869
|
+
# an outermost formula
|
|
1870
|
+
if not save_clauses:
|
|
1871
|
+
self.clauses = []
|
|
1755
1872
|
|
|
1756
1873
|
def _atoms(self, dest):
|
|
1757
1874
|
"""
|
|
@@ -1947,18 +2064,18 @@ class Or(Formula):
|
|
|
1947
2064
|
Disjunction clausification.
|
|
1948
2065
|
|
|
1949
2066
|
If ``name_required`` is ``False``, the method recursively encodes
|
|
1950
|
-
the subformulas and populates self's clauses with unit
|
|
1951
|
-
|
|
1952
|
-
|
|
2067
|
+
the subformulas and populates self's clauses with a unit clause
|
|
2068
|
+
containing the auxiliary "name" of each corresponding subformula,
|
|
2069
|
+
thus representing their disjunction.
|
|
1953
2070
|
|
|
1954
2071
|
If ``name_required`` is set to ``True``, the method encodes the
|
|
1955
|
-
|
|
2072
|
+
disjunction using the standard logic: :math:`x \\equiv
|
|
1956
2073
|
\\bigvee{y_i}`, if :math:`x` is the new auxiliary variable
|
|
1957
2074
|
encoding ``self`` and :math:`y_i` is the auxiliary variable
|
|
1958
2075
|
representing :math:`i`'s subformula.
|
|
1959
2076
|
|
|
1960
2077
|
:param name_required: whether or not a Tseitin variable is needed
|
|
1961
|
-
:
|
|
2078
|
+
:type name_required: bool
|
|
1962
2079
|
"""
|
|
1963
2080
|
|
|
1964
2081
|
save_clauses = bool(self.clauses)
|
|
@@ -2136,7 +2253,7 @@ class Neg(Formula):
|
|
|
2136
2253
|
is equal to the negation of subformula's name.
|
|
2137
2254
|
|
|
2138
2255
|
:param name_required: whether or not a Tseitin variable is needed
|
|
2139
|
-
:
|
|
2256
|
+
:type name_required: bool
|
|
2140
2257
|
"""
|
|
2141
2258
|
|
|
2142
2259
|
if not self.clauses:
|
|
@@ -2307,7 +2424,7 @@ class Implies(Formula):
|
|
|
2307
2424
|
|
|
2308
2425
|
If ``name_required`` is ``False``, the method recursively encodes
|
|
2309
2426
|
the left and right subformulas giving them names, say, :math:`x`
|
|
2310
|
-
and :math:`y` respectively and
|
|
2427
|
+
and :math:`y` respectively, and populates self's clauses with a
|
|
2311
2428
|
single binary clause :math:`(\\neg{x}\\lor y)`.
|
|
2312
2429
|
|
|
2313
2430
|
If ``name_required`` is set to ``True``, the method removes this
|
|
@@ -2315,7 +2432,7 @@ class Implies(Formula):
|
|
|
2315
2432
|
Tseitin-encoding it, i.e. :math:`n \\equiv (\\neg{x}\\lor y)`.
|
|
2316
2433
|
|
|
2317
2434
|
:param name_required: whether or not a Tseitin variable is needed
|
|
2318
|
-
:
|
|
2435
|
+
:type name_required: bool
|
|
2319
2436
|
"""
|
|
2320
2437
|
|
|
2321
2438
|
save_clauses = bool(self.clauses)
|
|
@@ -2407,7 +2524,7 @@ class Equals(Formula):
|
|
|
2407
2524
|
>>> from pysat.formula import *
|
|
2408
2525
|
>>> a1 = Equals(Equals(Atom('x'), Atom('y')), Atom('z'))
|
|
2409
2526
|
>>> a2 = Equals(Equals(Atom('x'), Atom('y')), Atom('z'), merge=True)
|
|
2410
|
-
>>> a3 = Atom('x')
|
|
2527
|
+
>>> a3 = Atom('x') @ Atom('y') @ Atom('z')
|
|
2411
2528
|
>>>
|
|
2412
2529
|
>>> print(a1)
|
|
2413
2530
|
(x @ y) @ z
|
|
@@ -2571,7 +2688,7 @@ class Equals(Formula):
|
|
|
2571
2688
|
relating it with the names of subformulas.
|
|
2572
2689
|
|
|
2573
2690
|
:param name_required: whether or not a Tseitin variable is needed
|
|
2574
|
-
:
|
|
2691
|
+
:type name_required: bool
|
|
2575
2692
|
"""
|
|
2576
2693
|
|
|
2577
2694
|
save_clauses = bool(self.clauses)
|
|
@@ -2652,13 +2769,12 @@ class XOr(Formula):
|
|
|
2652
2769
|
|
|
2653
2770
|
If an additional Boolean keyword argument ``merge`` is provided set to
|
|
2654
2771
|
``True``, the toolkit will try to flatten the current :class:`XOr`
|
|
2655
|
-
formula merging its *
|
|
2656
|
-
operands. For example, if ``XOr(XOr(x, y), z, merge=True)`` is
|
|
2657
|
-
a new Formula object will be created with two operands:
|
|
2658
|
-
and ``z``, followed by merging ``x`` and ``y`` into the
|
|
2659
|
-
root-level ``XOr``. This will result in a formula
|
|
2660
|
-
|
|
2661
|
-
used to create ``XOr`` formulas.
|
|
2772
|
+
formula merging its *exclusive-disjunction* sub-operands into the
|
|
2773
|
+
list of operands. For example, if ``XOr(XOr(x, y), z, merge=True)`` is
|
|
2774
|
+
called, a new Formula object will be created with two operands:
|
|
2775
|
+
``XOr(x, y)`` and ``z``, followed by merging ``x`` and ``y`` into the
|
|
2776
|
+
list of root-level ``XOr``. This will result in a formula
|
|
2777
|
+
``XOr(x, y, z)``. Bitwise ``^`` operations also enable merging.
|
|
2662
2778
|
|
|
2663
2779
|
Example:
|
|
2664
2780
|
|
|
@@ -2679,8 +2795,8 @@ class XOr(Formula):
|
|
|
2679
2795
|
>>> id(a1) == id(a2)
|
|
2680
2796
|
False
|
|
2681
2797
|
>>>
|
|
2682
|
-
>>> id(
|
|
2683
|
-
True # formulas
|
|
2798
|
+
>>> id(a2) == id(a3)
|
|
2799
|
+
True # formulas a2 and a3 refer to the same object
|
|
2684
2800
|
|
|
2685
2801
|
.. note::
|
|
2686
2802
|
|
|
@@ -2766,7 +2882,7 @@ class XOr(Formula):
|
|
|
2766
2882
|
>>> a = x ^ y ^ z
|
|
2767
2883
|
>>>
|
|
2768
2884
|
>>> print(a.simplified(assumptions=[y]))
|
|
2769
|
-
~x ^ z
|
|
2885
|
+
~(x ^ z)
|
|
2770
2886
|
>>> print(a.simplified(assumptions=[~y]))
|
|
2771
2887
|
x ^ z
|
|
2772
2888
|
"""
|
|
@@ -2817,7 +2933,7 @@ class XOr(Formula):
|
|
|
2817
2933
|
variable for each pair of operands.
|
|
2818
2934
|
|
|
2819
2935
|
:param name_required: whether or not a Tseitin variable is needed
|
|
2820
|
-
:
|
|
2936
|
+
:type name_required: bool
|
|
2821
2937
|
"""
|
|
2822
2938
|
|
|
2823
2939
|
save_clauses = bool(self.clauses)
|
|
@@ -2943,7 +3059,7 @@ class ITE(Formula):
|
|
|
2943
3059
|
>>> ite = ITE(x, y, z)
|
|
2944
3060
|
>>>
|
|
2945
3061
|
>>> print(ite)
|
|
2946
|
-
|
|
3062
|
+
(x >> y) & (~x >> z)
|
|
2947
3063
|
"""
|
|
2948
3064
|
|
|
2949
3065
|
def __new__(cls, *args, **kwargs):
|
|
@@ -3071,17 +3187,17 @@ class ITE(Formula):
|
|
|
3071
3187
|
|
|
3072
3188
|
If ``name_required`` is ``False``, the method recursively encodes
|
|
3073
3189
|
the ``cond``, ``cons1``, and ``cons2`` subformulas giving them
|
|
3074
|
-
names, say, :math:`x`, :math:`y`, and :math:`z`,
|
|
3075
|
-
|
|
3076
|
-
:math:`(\\neg{x}\\lor y)` and :math:`(x \\lor
|
|
3190
|
+
names, say, :math:`x`, :math:`y`, and :math:`z`, respectively, and
|
|
3191
|
+
populates self's clauses with two binary clauses
|
|
3192
|
+
:math:`(\\neg{x}\\lor y)` and :math:`(x \\lor z)`.
|
|
3077
3193
|
|
|
3078
3194
|
If ``name_required`` is set to ``True``, the method removes these
|
|
3079
3195
|
clauses and instead gives a name to the ITE by Tseitin-encoding
|
|
3080
3196
|
it, i.e. encoding :math:`n \\equiv \\left[(\\neg{x}\\lor
|
|
3081
|
-
y)\\land(x\\lor
|
|
3197
|
+
y)\\land(x\\lor z)\\right]`.
|
|
3082
3198
|
|
|
3083
3199
|
:param name_required: whether or not a Tseitin variable is needed
|
|
3084
|
-
:
|
|
3200
|
+
:type name_required: bool
|
|
3085
3201
|
"""
|
|
3086
3202
|
|
|
3087
3203
|
save_clauses = bool(self.clauses)
|
|
@@ -3136,7 +3252,7 @@ class ITE(Formula):
|
|
|
3136
3252
|
"""
|
|
3137
3253
|
Informal representation of the ``ITE`` formula. Returns a string
|
|
3138
3254
|
representation of the subformulas as two implications connected
|
|
3139
|
-
with the
|
|
3255
|
+
with the conjunction symbol ``' & '``
|
|
3140
3256
|
"""
|
|
3141
3257
|
|
|
3142
3258
|
cons1 = '{0} >> {1}'.format(f'{str(self.cond)}' if self.cond.type in (FormulaType.ATOM, FormulaType.NEG) else f'({str(self.cond)})',
|
|
@@ -3898,7 +4014,7 @@ class CNF(Formula, object):
|
|
|
3898
4014
|
for lit in enclits:
|
|
3899
4015
|
clauses.append([-self.name, lit])
|
|
3900
4016
|
clauses.append([self.name] + [-lit for lit in enclits])
|
|
3901
|
-
|
|
4017
|
+
elif len(enclits) == 1: # single clause - nothing left to encode
|
|
3902
4018
|
self.name = enclits[0] # existing variable
|
|
3903
4019
|
|
|
3904
4020
|
# connecting it to the CNF object as its name
|
|
@@ -3907,6 +4023,8 @@ class CNF(Formula, object):
|
|
|
3907
4023
|
|
|
3908
4024
|
# just in case, marking all ids below self.name as occupied
|
|
3909
4025
|
Formula._vpool[Formula._context].occupy(1, self.name)
|
|
4026
|
+
else:
|
|
4027
|
+
self.name = PYSAT_TRUE.name
|
|
3910
4028
|
|
|
3911
4029
|
self.encoded = clauses
|
|
3912
4030
|
self.auxvars = auxvars
|
|
@@ -4129,6 +4247,7 @@ class WCNF(object):
|
|
|
4129
4247
|
# normalize it, i.e. transform into a set of clauses
|
|
4130
4248
|
# with a positive weight
|
|
4131
4249
|
if negs:
|
|
4250
|
+
self.nv = max(self.nv, max(map(lambda cl: max(map(abs, cl[0])), negs)))
|
|
4132
4251
|
self.normalize_negatives(negs)
|
|
4133
4252
|
|
|
4134
4253
|
# if topw was unspecified and assigned to +infinity,
|
|
@@ -5380,6 +5499,7 @@ class WCNFPlus(WCNF, object):
|
|
|
5380
5499
|
# normalize it, i.e. transform into a set of clauses
|
|
5381
5500
|
# with a positive weight
|
|
5382
5501
|
if negs:
|
|
5502
|
+
self.nv = max(self.nv, max(map(lambda cl: max(map(abs, cl[0])), negs)))
|
|
5383
5503
|
self.normalize_negatives(negs)
|
|
5384
5504
|
|
|
5385
5505
|
# if topw was unspecified and assigned to +infinity,
|
pysat/solvers.py
CHANGED
|
@@ -184,9 +184,9 @@
|
|
|
184
184
|
settings. For that reason, :class:`Cadical300`'s BVA is disabled by
|
|
185
185
|
default whenever it is created without a synchronized
|
|
186
186
|
:class:`.pysat.formula.IDPool`. Users who want BVA can either enable it
|
|
187
|
-
manually with :meth:`.configure` or attach a shared pool with
|
|
188
|
-
:meth:`.attach_vpool` and keep it synchronized explicitly with
|
|
189
|
-
:meth:`.sync_vpool`.
|
|
187
|
+
manually with :meth:`.Solver.configure` or attach a shared pool with
|
|
188
|
+
:meth:`.Solver.attach_vpool` and keep it synchronized explicitly with
|
|
189
|
+
:meth:`.Solver.sync_vpool`.
|
|
190
190
|
|
|
191
191
|
==============
|
|
192
192
|
Module details
|
pysolvers.cp313-win_amd64.pyd
CHANGED
|
Binary file
|
|
@@ -131,9 +131,9 @@
|
|
|
131
131
|
#
|
|
132
132
|
#==============================================================================
|
|
133
133
|
from __future__ import print_function
|
|
134
|
+
import bisect
|
|
134
135
|
import collections
|
|
135
136
|
import getopt
|
|
136
|
-
import itertools
|
|
137
137
|
from math import copysign
|
|
138
138
|
import os
|
|
139
139
|
from pysat.formula import CNFPlus, WCNFPlus, IDPool
|
|
@@ -408,6 +408,9 @@ class RC2(object):
|
|
|
408
408
|
:type clause: iterable(int)
|
|
409
409
|
:type weight: int
|
|
410
410
|
|
|
411
|
+
:return: selector literal for a soft clause and ``None`` for a hard one
|
|
412
|
+
:rtype: int or None
|
|
413
|
+
|
|
411
414
|
.. code-block:: python
|
|
412
415
|
|
|
413
416
|
>>> from pysat.examples.rc2 import RC2
|
|
@@ -448,6 +451,8 @@ class RC2(object):
|
|
|
448
451
|
'{0} does not support native cardinality constraints. Make sure you use the right type of formula.'.format(self.solver)
|
|
449
452
|
|
|
450
453
|
self.oracle.add_atmost(cl, clause[1], weights=clause[2] if len(clause) == 3 else [])
|
|
454
|
+
|
|
455
|
+
return None
|
|
451
456
|
else:
|
|
452
457
|
# soft clauses should be augmented with a selector
|
|
453
458
|
selv = cl[0] # for a unit clause, no selector is needed
|
|
@@ -471,6 +476,8 @@ class RC2(object):
|
|
|
471
476
|
self.sall.append(selv)
|
|
472
477
|
self.sels_set.add(selv)
|
|
473
478
|
|
|
479
|
+
return selv
|
|
480
|
+
|
|
474
481
|
def delete(self):
|
|
475
482
|
"""
|
|
476
483
|
Explicit destructor of the internal SAT oracle and all the
|
|
@@ -732,9 +739,12 @@ class RC2(object):
|
|
|
732
739
|
self.minw = min(map(lambda l: self.wght[l], self.core))
|
|
733
740
|
|
|
734
741
|
# dividing the core into two parts
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
742
|
+
self.core_sels, self.core_sums = [], []
|
|
743
|
+
for l in self.core:
|
|
744
|
+
if l in self.sels_set:
|
|
745
|
+
self.core_sels.append(l)
|
|
746
|
+
else:
|
|
747
|
+
self.core_sums.append(l)
|
|
738
748
|
|
|
739
749
|
def process_core(self):
|
|
740
750
|
"""
|
|
@@ -810,8 +820,8 @@ class RC2(object):
|
|
|
810
820
|
handled by calling :func:`process_am1`.
|
|
811
821
|
"""
|
|
812
822
|
|
|
813
|
-
# literal connections
|
|
814
|
-
conns = collections.defaultdict(
|
|
823
|
+
# literal connections and selectors conflicting on their own
|
|
824
|
+
conns = collections.defaultdict(list)
|
|
815
825
|
confl = []
|
|
816
826
|
|
|
817
827
|
# prepare connections
|
|
@@ -819,26 +829,29 @@ class RC2(object):
|
|
|
819
829
|
st, props = self.oracle.propagate(assumptions=[l1], phase_saving=2)
|
|
820
830
|
if st:
|
|
821
831
|
for l2 in props:
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
conns[
|
|
832
|
+
l2 = -l2
|
|
833
|
+
if l2 in self.sels_set:
|
|
834
|
+
conns[l1].append(l2)
|
|
835
|
+
conns[l2].append(l1)
|
|
825
836
|
else:
|
|
826
837
|
# propagating this literal results in a conflict
|
|
827
838
|
confl.append(l1)
|
|
828
839
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
840
|
+
# sort (in-place) and deduplicate adjacency lists
|
|
841
|
+
for l in list(conns):
|
|
842
|
+
neigh = conns[l]
|
|
843
|
+
if neigh:
|
|
844
|
+
neigh.sort()
|
|
832
845
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
846
|
+
j = 1
|
|
847
|
+
for i in range(1, len(neigh)):
|
|
848
|
+
if neigh[i] != neigh[j - 1]:
|
|
849
|
+
neigh[j] = neigh[i]
|
|
850
|
+
j += 1
|
|
838
851
|
|
|
839
|
-
|
|
840
|
-
confl = list(confl)
|
|
852
|
+
del neigh[j:]
|
|
841
853
|
|
|
854
|
+
if confl:
|
|
842
855
|
# processing unit size cores
|
|
843
856
|
for l in confl:
|
|
844
857
|
self.core, self.minw = [l], self.wght[l]
|
|
@@ -849,24 +862,47 @@ class RC2(object):
|
|
|
849
862
|
print('c unit cores found: {0}; cost: {1}'.format(len(confl),
|
|
850
863
|
self.cost))
|
|
851
864
|
|
|
865
|
+
# the following graph manipulation requires
|
|
866
|
+
# fast membership checks for confl; making it a set
|
|
867
|
+
confl = set(confl)
|
|
868
|
+
|
|
869
|
+
# current degrees in the remaining graph;
|
|
870
|
+
# literals disconnected after removing conflicts are ignored
|
|
871
|
+
degree, lits = {}, set()
|
|
872
|
+
for l in conns:
|
|
873
|
+
if l not in confl:
|
|
874
|
+
d = sum(1 for l2 in conns[l] if l2 not in confl)
|
|
875
|
+
if d:
|
|
876
|
+
degree[l] = d
|
|
877
|
+
lits.add(l)
|
|
878
|
+
|
|
879
|
+
# detect AM1s on the original formula with conflicting selectors
|
|
880
|
+
# filtered out, similarly to the previous implementation
|
|
852
881
|
nof_am1 = 0
|
|
853
882
|
len_am1 = []
|
|
854
|
-
|
|
883
|
+
|
|
855
884
|
while lits:
|
|
856
|
-
am1 = [min(lits, key=lambda l:
|
|
885
|
+
am1 = [min(lits, key=lambda l: degree[l])]
|
|
857
886
|
|
|
858
|
-
for l in sorted(conns[am1[0]], key=lambda l:
|
|
887
|
+
for l in sorted(conns[am1[0]], key=lambda l: degree.get(l, 0)):
|
|
859
888
|
if l in lits:
|
|
860
889
|
for l_added in am1[1:]:
|
|
861
|
-
|
|
890
|
+
# adjacency lists are sorted, so we use
|
|
891
|
+
# binary search for clique membership tests
|
|
892
|
+
i = bisect.bisect_left(conns[l], l_added)
|
|
893
|
+
if i == len(conns[l]) or conns[l][i] != l_added:
|
|
862
894
|
break
|
|
863
895
|
else:
|
|
864
896
|
am1.append(l)
|
|
865
897
|
|
|
866
|
-
# updating remaining
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
898
|
+
# updating remaining literals and their degrees
|
|
899
|
+
am1_set = set(am1)
|
|
900
|
+
lits.difference_update(am1_set)
|
|
901
|
+
|
|
902
|
+
for l in am1:
|
|
903
|
+
for l2 in conns.get(l, []):
|
|
904
|
+
if l2 in lits:
|
|
905
|
+
degree[l2] -= 1
|
|
870
906
|
|
|
871
907
|
if len(am1) > 1:
|
|
872
908
|
# treat the new atmost1 relation
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
pycard.cp313-win_amd64.pyd,sha256=D53SVLi3JhMdEfqTkJO1bgTsDVhh_lfdJoB8-XKEIHA,71680
|
|
2
|
+
pyformula.cp313-win_amd64.pyd,sha256=J7eXIRP8RrOdwbynbwkc4oEnMQIP0HsnBDFN3BzaAfI,31232
|
|
3
|
+
pysolvers.cp313-win_amd64.pyd,sha256=sjflLpYHydwda4Y6amRmn0WI66Ui8ZdWe4S_ECq4OYE,3340288
|
|
4
|
+
pysat/__init__.py,sha256=NUV138GbmWkxc-cwJx_fsLTx9ps9VuNoI2fx0C9Pf5Q,681
|
|
5
|
+
pysat/_fileio.py,sha256=pXRGGAaP7U8r3bpn5ShseCSHROXqi7ygaYe6T6QORto,6700
|
|
6
|
+
pysat/_utils.py,sha256=4cv40p70FKvjCTKq8GqzS_jlxF74cp_lojTZSim_qJE,1340
|
|
7
|
+
pysat/card.py,sha256=rav3_t_mCzbLw8SAdXcOh-PNbFtaATVSAJzpbygqiQ4,30066
|
|
8
|
+
pysat/engines.py,sha256=yAYA_PzIGr_uRF85M7kL2bipjpLbY7jdfFKTdY-yW8I,46364
|
|
9
|
+
pysat/formula.py,sha256=zmAceFu7gpvopLWfpXSZVH9jaQY_5FOGt03lrse3X1Y,215477
|
|
10
|
+
pysat/integer.py,sha256=um6FXUzmBHUFlWXNKs0xLh9Jss3jiEin2Er_U41KtkA,90203
|
|
11
|
+
pysat/pb.py,sha256=DMy8UfbbpfJo5cTZul9W1o_mlr-B0lCIEseZjrQYd8A,17816
|
|
12
|
+
pysat/process.py,sha256=8lNbpEKipFZjbQ9DCJPfdL8eZHx-ltNuu7h4geWWdJY,12739
|
|
13
|
+
pysat/solvers.py,sha256=JTZU2O-rrmUKDRNq0vLm0wtXIGsftwhGxxQZdGiij34,277199
|
|
14
|
+
pysat/allies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
pysat/allies/approxmc.py,sha256=LtUss6PiqmgvFwLTe4KUHmhavdzlotRii2UUOSb9gTU,13586
|
|
16
|
+
pysat/allies/unigen.py,sha256=qOGc_WwcIi-AgFSJDBhhY2n6Q6UeYVMpMPuem4-Vf6c,16718
|
|
17
|
+
pysat/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
pysat/examples/bbscan.py,sha256=E4Y5j3Llr0-jIn-RDZIk-f02Sbujg4wX7uysoiFRnLg,22046
|
|
19
|
+
pysat/examples/bica.py,sha256=OGcGQGQnMepCCurwRbygokFgoDkI7eBhjmsYvr3FM1M,27098
|
|
20
|
+
pysat/examples/fm.py,sha256=e_s6wNBstEeoMw9zYyyDRgkE-Hqa902Iy_CxCS961CE,18136
|
|
21
|
+
pysat/examples/genhard.py,sha256=DFXtSVQ4qn78y0-ZMr2D6gBCLizZSzgkvcbDsjqjnKI,19017
|
|
22
|
+
pysat/examples/hitman.py,sha256=vHJEeSfoMQlDD3D3Poofaryv-k_dMH37XoqVXJUUuUg,26360
|
|
23
|
+
pysat/examples/lbx.py,sha256=InMjqY-9UvWKznIXOEwfBsM4spfkduDld6gUkr1jCqo,23249
|
|
24
|
+
pysat/examples/lsu.py,sha256=qyN2TvXG61EtMy-IrX5EYmLPM_iNOhtOkGTY5nHO_zo,16198
|
|
25
|
+
pysat/examples/mcsls.py,sha256=bP47P56OOP_uy0MuFA7TGxMdkAis7gX4E9AEEI0tSec,22093
|
|
26
|
+
pysat/examples/models.py,sha256=cdIR8iOrYu2ibqfcb_kEKXQMCfmFiz7cvZ3y1dur3Qw,5741
|
|
27
|
+
pysat/examples/musx.py,sha256=hswVGkjm95lhOyrtl1e4ouDyNSy-hWvQ05084Xnck9I,10946
|
|
28
|
+
pysat/examples/optux.py,sha256=8eYHO_uhL_4ugb-hRv2s6cnOwTurfqkpbdnK9T4iDtw,27697
|
|
29
|
+
pysat/examples/primer.py,sha256=0OeV9R6mWyIr2BKIitkrfgY3q2XE-sV1ZLSiybGRFXw,23375
|
|
30
|
+
pysat/examples/rc2.py,sha256=7-FuygxAbgNYb9QmKsNhkK5EG5OW8mpHOpoA8GnDs28,76998
|
|
31
|
+
pysat/examples/usage.py,sha256=x9luw6pyKAAndlxAeUWFhS4aLSVmD2t_j_5uA9YddHg,2183
|
|
32
|
+
python_sat-1.9.dev12.data/scripts/approxmc.py,sha256=h6aMnSpIaYXkTMq-tCSzNWwKb0AG-vTH4vuLWGn5Q4s,13958
|
|
33
|
+
python_sat-1.9.dev12.data/scripts/bbscan.py,sha256=xGxklG6Gc_UBaM8QvEJDOtQhEamxAncfsd65E-0JcAc,22696
|
|
34
|
+
python_sat-1.9.dev12.data/scripts/bica.py,sha256=q6fQGsFp1EaaYRZGBsDFOIJPVScr5k42XFskBzW69mA,27776
|
|
35
|
+
python_sat-1.9.dev12.data/scripts/fm.py,sha256=ek1QDsuxiq1zm1PBLbrSXXEE89RP9_DYwVNnyB3eO4s,18650
|
|
36
|
+
python_sat-1.9.dev12.data/scripts/genhard.py,sha256=YswufT272W4xTJrSBlOC2ucJx4RK4ntfRv73rASrx30,19520
|
|
37
|
+
python_sat-1.9.dev12.data/scripts/lbx.py,sha256=rCQ6zH_DXoVjBrzhZHSDJMfP92-hz2JVwh6e4m44AUc,23874
|
|
38
|
+
python_sat-1.9.dev12.data/scripts/lsu.py,sha256=cAATg2x0Cu1LC8KszMctHqywjq7pygkb9vWNBrbUEZs,16681
|
|
39
|
+
python_sat-1.9.dev12.data/scripts/mcsls.py,sha256=cTJQMIO-4iho412wG0JhvhqXWy81U0RQV8wwOgkri3Q,22690
|
|
40
|
+
python_sat-1.9.dev12.data/scripts/models.py,sha256=UmMmklExzyXAIhh-0i038nQb7hTK6CbdV1M6m2GxxSI,5917
|
|
41
|
+
python_sat-1.9.dev12.data/scripts/musx.py,sha256=LwK10YuQ8pIf1TJwp0HKp54O0hdk8zBy3TIfGsF1yW0,11277
|
|
42
|
+
python_sat-1.9.dev12.data/scripts/optux.py,sha256=t20YBZH4_axTkRMapq-DL-fq-9tBybPWo0pkhIyGhDw,28394
|
|
43
|
+
python_sat-1.9.dev12.data/scripts/primer.py,sha256=iE9Vy0XyeySIY-7-L_VMwRahsIVCEmm9k04OhLwDvuw,23982
|
|
44
|
+
python_sat-1.9.dev12.data/scripts/rc2.py,sha256=vvB7GlzssovGtnyk-COK-1bxmPh4zpj8Y7GqQa9dC9U,79020
|
|
45
|
+
python_sat-1.9.dev12.data/scripts/unigen.py,sha256=e-HC2UxWA8MTN90VslkHX1ZlTPjdhI4etzkn6G8YAX4,17140
|
|
46
|
+
python_sat-1.9.dev12.dist-info/licenses/LICENSE.txt,sha256=6QMvEzxqdPXEoiAZUBaZLeLF4hW2S81fjKz_THER0uQ,1109
|
|
47
|
+
python_sat-1.9.dev12.dist-info/METADATA,sha256=sJK6NIR39c2zlQYICi7dM1ZNAHAc91t0iQTGNIBJCUI,1784
|
|
48
|
+
python_sat-1.9.dev12.dist-info/WHEEL,sha256=0LUNoHxLvcpw9db_x0XozE5jW73-_GOAO7fDDJ4Xt4Q,101
|
|
49
|
+
python_sat-1.9.dev12.dist-info/top_level.txt,sha256=0SLg3yVqNpeiqOwFcAo3OxXhqAnflCmb5Fjl6gsl-Do,33
|
|
50
|
+
python_sat-1.9.dev12.dist-info/RECORD,,
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
pycard.cp313-win_amd64.pyd,sha256=imahNXZGs_FQFbCmOasfz7D2m4dZ8rCXTaP-oGeM_Wk,71680
|
|
2
|
-
pyformula.cp313-win_amd64.pyd,sha256=T6n_TZi4gxd80YRi9nv0wcRL0v23633jdwjvthNmw7M,30208
|
|
3
|
-
pysolvers.cp313-win_amd64.pyd,sha256=xf58mzY8XyIsBQcbqI8yRz7L-W0FniikTXCqfCFXmDU,3332608
|
|
4
|
-
pysat/__init__.py,sha256=G7mBSiK_F_B3A_VWDDB1c8RXtNIGmgHsZ5-JV8Md9Ko,680
|
|
5
|
-
pysat/_fileio.py,sha256=pXRGGAaP7U8r3bpn5ShseCSHROXqi7ygaYe6T6QORto,6700
|
|
6
|
-
pysat/_utils.py,sha256=4cv40p70FKvjCTKq8GqzS_jlxF74cp_lojTZSim_qJE,1340
|
|
7
|
-
pysat/card.py,sha256=rav3_t_mCzbLw8SAdXcOh-PNbFtaATVSAJzpbygqiQ4,30066
|
|
8
|
-
pysat/engines.py,sha256=PzcZJ4zFXDHfcSSHF7iPadtrwSVEt4t9B_cO5epwTZk,45182
|
|
9
|
-
pysat/formula.py,sha256=3VdJYS6jZDRWSwn_KfofTmwD_x401O-3rQyfzgORL6o,210084
|
|
10
|
-
pysat/integer.py,sha256=um6FXUzmBHUFlWXNKs0xLh9Jss3jiEin2Er_U41KtkA,90203
|
|
11
|
-
pysat/pb.py,sha256=DMy8UfbbpfJo5cTZul9W1o_mlr-B0lCIEseZjrQYd8A,17816
|
|
12
|
-
pysat/process.py,sha256=8lNbpEKipFZjbQ9DCJPfdL8eZHx-ltNuu7h4geWWdJY,12739
|
|
13
|
-
pysat/solvers.py,sha256=puvS7W8TjBz5cIb65XgXv3KaSNjvJS8aju_AepJGyKA,277178
|
|
14
|
-
pysat/allies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
pysat/allies/approxmc.py,sha256=LtUss6PiqmgvFwLTe4KUHmhavdzlotRii2UUOSb9gTU,13586
|
|
16
|
-
pysat/allies/unigen.py,sha256=qOGc_WwcIi-AgFSJDBhhY2n6Q6UeYVMpMPuem4-Vf6c,16718
|
|
17
|
-
pysat/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
pysat/examples/bbscan.py,sha256=E4Y5j3Llr0-jIn-RDZIk-f02Sbujg4wX7uysoiFRnLg,22046
|
|
19
|
-
pysat/examples/bica.py,sha256=OGcGQGQnMepCCurwRbygokFgoDkI7eBhjmsYvr3FM1M,27098
|
|
20
|
-
pysat/examples/fm.py,sha256=e_s6wNBstEeoMw9zYyyDRgkE-Hqa902Iy_CxCS961CE,18136
|
|
21
|
-
pysat/examples/genhard.py,sha256=DFXtSVQ4qn78y0-ZMr2D6gBCLizZSzgkvcbDsjqjnKI,19017
|
|
22
|
-
pysat/examples/hitman.py,sha256=vHJEeSfoMQlDD3D3Poofaryv-k_dMH37XoqVXJUUuUg,26360
|
|
23
|
-
pysat/examples/lbx.py,sha256=InMjqY-9UvWKznIXOEwfBsM4spfkduDld6gUkr1jCqo,23249
|
|
24
|
-
pysat/examples/lsu.py,sha256=qyN2TvXG61EtMy-IrX5EYmLPM_iNOhtOkGTY5nHO_zo,16198
|
|
25
|
-
pysat/examples/mcsls.py,sha256=bP47P56OOP_uy0MuFA7TGxMdkAis7gX4E9AEEI0tSec,22093
|
|
26
|
-
pysat/examples/models.py,sha256=cdIR8iOrYu2ibqfcb_kEKXQMCfmFiz7cvZ3y1dur3Qw,5741
|
|
27
|
-
pysat/examples/musx.py,sha256=hswVGkjm95lhOyrtl1e4ouDyNSy-hWvQ05084Xnck9I,10946
|
|
28
|
-
pysat/examples/optux.py,sha256=8eYHO_uhL_4ugb-hRv2s6cnOwTurfqkpbdnK9T4iDtw,27697
|
|
29
|
-
pysat/examples/primer.py,sha256=0OeV9R6mWyIr2BKIitkrfgY3q2XE-sV1ZLSiybGRFXw,23375
|
|
30
|
-
pysat/examples/rc2.py,sha256=QjN-LbLWNnDwSyZKn10_INniPpIK8dv_0BGPZzbXxa8,75763
|
|
31
|
-
pysat/examples/usage.py,sha256=x9luw6pyKAAndlxAeUWFhS4aLSVmD2t_j_5uA9YddHg,2183
|
|
32
|
-
python_sat-1.9.dev3.data/scripts/approxmc.py,sha256=h6aMnSpIaYXkTMq-tCSzNWwKb0AG-vTH4vuLWGn5Q4s,13958
|
|
33
|
-
python_sat-1.9.dev3.data/scripts/bbscan.py,sha256=xGxklG6Gc_UBaM8QvEJDOtQhEamxAncfsd65E-0JcAc,22696
|
|
34
|
-
python_sat-1.9.dev3.data/scripts/bica.py,sha256=q6fQGsFp1EaaYRZGBsDFOIJPVScr5k42XFskBzW69mA,27776
|
|
35
|
-
python_sat-1.9.dev3.data/scripts/fm.py,sha256=ek1QDsuxiq1zm1PBLbrSXXEE89RP9_DYwVNnyB3eO4s,18650
|
|
36
|
-
python_sat-1.9.dev3.data/scripts/genhard.py,sha256=YswufT272W4xTJrSBlOC2ucJx4RK4ntfRv73rASrx30,19520
|
|
37
|
-
python_sat-1.9.dev3.data/scripts/lbx.py,sha256=rCQ6zH_DXoVjBrzhZHSDJMfP92-hz2JVwh6e4m44AUc,23874
|
|
38
|
-
python_sat-1.9.dev3.data/scripts/lsu.py,sha256=cAATg2x0Cu1LC8KszMctHqywjq7pygkb9vWNBrbUEZs,16681
|
|
39
|
-
python_sat-1.9.dev3.data/scripts/mcsls.py,sha256=cTJQMIO-4iho412wG0JhvhqXWy81U0RQV8wwOgkri3Q,22690
|
|
40
|
-
python_sat-1.9.dev3.data/scripts/models.py,sha256=UmMmklExzyXAIhh-0i038nQb7hTK6CbdV1M6m2GxxSI,5917
|
|
41
|
-
python_sat-1.9.dev3.data/scripts/musx.py,sha256=LwK10YuQ8pIf1TJwp0HKp54O0hdk8zBy3TIfGsF1yW0,11277
|
|
42
|
-
python_sat-1.9.dev3.data/scripts/optux.py,sha256=t20YBZH4_axTkRMapq-DL-fq-9tBybPWo0pkhIyGhDw,28394
|
|
43
|
-
python_sat-1.9.dev3.data/scripts/primer.py,sha256=iE9Vy0XyeySIY-7-L_VMwRahsIVCEmm9k04OhLwDvuw,23982
|
|
44
|
-
python_sat-1.9.dev3.data/scripts/rc2.py,sha256=yFieB6_5sYLLfIRo6Al9DXt3V5Y-qIUYH5E89xoaLN4,77749
|
|
45
|
-
python_sat-1.9.dev3.data/scripts/unigen.py,sha256=e-HC2UxWA8MTN90VslkHX1ZlTPjdhI4etzkn6G8YAX4,17140
|
|
46
|
-
python_sat-1.9.dev3.dist-info/licenses/LICENSE.txt,sha256=6QMvEzxqdPXEoiAZUBaZLeLF4hW2S81fjKz_THER0uQ,1109
|
|
47
|
-
python_sat-1.9.dev3.dist-info/METADATA,sha256=7JnxB6MFLeaqZ3M8n_ZUJGRtlFnrgEm25bwE9XJ__VE,1783
|
|
48
|
-
python_sat-1.9.dev3.dist-info/WHEEL,sha256=x5Wpw_tLx5PQKiWdxpqvs0e7Sg-SO0mTWdEADYDGPGA,101
|
|
49
|
-
python_sat-1.9.dev3.dist-info/top_level.txt,sha256=0SLg3yVqNpeiqOwFcAo3OxXhqAnflCmb5Fjl6gsl-Do,33
|
|
50
|
-
python_sat-1.9.dev3.dist-info/RECORD,,
|
|
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
|