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.

Files changed (28) hide show
  1. pycard.cp313-win_amd64.pyd +0 -0
  2. pyformula.cp313-win_amd64.pyd +0 -0
  3. pysat/__init__.py +1 -1
  4. pysat/engines.py +35 -9
  5. pysat/examples/rc2.py +63 -27
  6. pysat/formula.py +226 -106
  7. pysat/solvers.py +3 -3
  8. pysolvers.cp313-win_amd64.pyd +0 -0
  9. {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/rc2.py +63 -27
  10. {python_sat-1.9.dev3.dist-info → python_sat-1.9.dev12.dist-info}/METADATA +1 -1
  11. python_sat-1.9.dev12.dist-info/RECORD +50 -0
  12. {python_sat-1.9.dev3.dist-info → python_sat-1.9.dev12.dist-info}/WHEEL +1 -1
  13. python_sat-1.9.dev3.dist-info/RECORD +0 -50
  14. {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/approxmc.py +0 -0
  15. {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/bbscan.py +0 -0
  16. {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/bica.py +0 -0
  17. {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/fm.py +0 -0
  18. {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/genhard.py +0 -0
  19. {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/lbx.py +0 -0
  20. {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/lsu.py +0 -0
  21. {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/mcsls.py +0 -0
  22. {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/models.py +0 -0
  23. {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/musx.py +0 -0
  24. {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/optux.py +0 -0
  25. {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/primer.py +0 -0
  26. {python_sat-1.9.dev3.data → python_sat-1.9.dev12.data}/scripts/unigen.py +0 -0
  27. {python_sat-1.9.dev3.dist-info → python_sat-1.9.dev12.dist-info}/licenses/LICENSE.txt +0 -0
  28. {python_sat-1.9.dev3.dist-info → python_sat-1.9.dev12.dist-info}/top_level.txt +0 -0
Binary file
Binary file
pysat/__init__.py CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  # current version
12
12
  #==============================================================================
13
- VERSION = (1, 9, 'dev', 3)
13
+ VERSION = (1, 9, 'dev', 12)
14
14
 
15
15
 
16
16
  # PEP440 Format
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 [] # add an(y) external clause to the solver
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 add an external clause if
691
- there is any. The clause can be arbitrary but if it is
692
- root-satisfied or tautological, the solver will ignore it without
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 a new clause to add to the solver if one exists; return an
1289
- empty clause ``[]`` otherwise.
1312
+ Extract the external clause previously announced by
1313
+ :meth:`has_clause`.
1290
1314
  """
1291
1315
 
1292
- if self.falsified is None:
1293
- return []
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
- iter1, iter2 = itertools.tee(self.core)
736
- self.core_sels = list(l for l in iter1 if l in self.sels_set)
737
- self.core_sums = list(l for l in iter2 if l not in self.sels_set)
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(lambda: set([]))
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
- if -l2 in self.sels_set:
823
- conns[l1].add(-l2)
824
- conns[-l2].add(l1)
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
- if confl: # filtering out unnecessary connections
830
- ccopy = {}
831
- confl = set(confl)
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
- for l in conns:
834
- if l not in confl:
835
- cc = conns[l].difference(confl)
836
- if cc:
837
- ccopy[l] = cc
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
- conns = ccopy
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
- lits = set(conns.keys())
883
+
855
884
  while lits:
856
- am1 = [min(lits, key=lambda l: len(conns[l]))]
885
+ am1 = [min(lits, key=lambda l: degree[l])]
857
886
 
858
- for l in sorted(conns[am1[0]], key=lambda l: len(conns[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
- if l_added not in conns[l]:
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 lits and connections
867
- lits.difference_update(set(am1))
868
- for l in conns:
869
- conns[l] = conns[l].difference(set(am1))
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 a list of attributes to
887
- serve as a key associated with the formula requested by a user.
888
- The flattened result will contain string, i.e. ``repr()``,
889
- representation of all the attributes. The method is meant to be
890
- internal and not to be used by formula users.
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', repr(item[1]))]
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 ugly part:
951
- # reconstructing the key pairs, depending on the type of Formula
952
- if ftype[1] == FormulaType.ATOM:
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
- subfs[0] = ('object', repr(subfs[0][1]))
955
- elif ftype[1] == FormulaType.NEG:
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
- subfs[0] = ('subformula', id(subfs[0][1]))
958
- elif ftype[1] == FormulaType.IMPL:
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
- subfs[0] = ('left', id(subfs[0][1]))
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
- subfs[0] = ('right', id(subfs[0][1]))
1045
+ operands = [('right', subfs[0][1]), subfs[1]]
965
1046
  elif subfs[1][0] == 'right':
966
- subfs[0] = ('left', id(subfs[0][1]))
967
- elif ftype[1] == FormulaType.ITE:
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 subfs[0][0] == '' and subfs[1][0] == '' and subfs[2][0] == '':
970
- subfs[0] = ('cond', id(subfs[0][1]))
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
- if subfs[2][0] == 'cond':
975
- subfs[0] = ('cons1', id(subfs[0][1]))
976
- subfs[1] = ('cons2', id(subfs[1][1]))
977
- elif subfs[2][0] == 'cons1':
978
- subfs[0] = ('cond', id(subfs[0][1]))
979
- subfs[1] = ('cons2', id(subfs[1][1]))
980
- elif subfs[2][0] == 'cons2':
981
- subfs[0] = ('cond', id(subfs[0][1]))
982
- subfs[1] = ('cons1', id(subfs[1][1]))
983
- elif subfs[0] == '':
984
- if 'cond' not in (subfs[1][0], subfs[2][0]):
985
- subfs[0] = ('cond', id(subfs[0][0]))
986
- elif 'cons1' not in (subfs[1][0], subfs[2][0]):
987
- subfs[0] = ('cons1', id(subfs[0][0]))
988
- elif 'cons2' not in (subfs[1][0], subfs[2][0]):
989
- subfs[0] = ('cons2', id(subfs[0][0]))
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
- # these are commutative connectives; we need to sort the arguments
992
- assert len(subfs) >= 1, 'At least one subformula is required for an And/Equals/Or/XOr formula'
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
- Conjuction clausification.
1795
+ Conjunction clausification.
1712
1796
 
1713
1797
  If ``name_required`` is ``False``, the method recursively encodes
1714
- the subformulas and populates self's clauses with unit clauses,
1715
- each containing the auxiliary "name" of the corresponding
1716
- subformula, thus representing their conjunction.
1717
-
1718
- If ``name_required`` is set to ``True``, the method encodes the
1719
- conjunction using the standard logic: :math:`x \\equiv
1720
- \\bigwedge{y_i}`, if :math:`x` is the new auxiliary variable
1721
- encoding ``self`` and :math:`y_i` is the auxiliary variable
1722
- representing :math:`i`'s subformula.
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
- :param name_required: bool
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
- # first, recursively encoding subformulas
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._clausify(name_required=True)
1734
-
1735
- # adding unit clauses
1736
- self.clauses.append([sub.name])
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 new name for this formula if required
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
- if save_clauses:
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
- self.name = Formula._vpool[Formula._context].id(self)
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
- cl = [self.name] # final clause (converse implication)
1749
- for i in range(len(self.encoded)):
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
- # adding final clause
1754
- self.encoded.append(cl)
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 clauses,
1951
- each containing the auxiliary "name" of the corresponding
1952
- subformula, thus representing their conjunction.
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
- conjunction using the standard logic: :math:`x \\equiv
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
- :param name_required: bool
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
- :param name_required: bool
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 the populates self's clauses with a
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
- :param name_required: bool
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') == Atom('y') == Atom('z')
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
- :param name_required: bool
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 *equivalence* sub-operands into the list of
2656
- operands. For example, if ``XOr(XOr(x, y), z, merge=True)`` is called,
2657
- a new Formula object will be created with two operands: ``XOr(x, y)``
2658
- and ``z``, followed by merging ``x`` and ``y`` into the list of
2659
- root-level ``XOr``. This will result in a formula ``XOr(x, y, z)``.
2660
- Merging sub-operands is disabled by default if bitwise operations are
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(a1) == id(a3)
2683
- True # formulas a1 and a3 refer to the same object
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
- :param name_required: bool
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
- >>> (x >> y) & (~x >> z)
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`, respectivela, and
3075
- the populates self's clauses with two binary clauses
3076
- :math:`(\\neg{x}\\lor y)` and :math:`(x \\lor y)`.
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 y)\\right]`.
3197
+ y)\\land(x\\lor z)\\right]`.
3082
3198
 
3083
3199
  :param name_required: whether or not a Tseitin variable is needed
3084
- :param name_required: bool
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 conjuction symbol ``' & '``
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
- else: # single clause - nothing left to encode
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
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
- iter1, iter2 = itertools.tee(self.core)
736
- self.core_sels = list(l for l in iter1 if l in self.sels_set)
737
- self.core_sums = list(l for l in iter2 if l not in self.sels_set)
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(lambda: set([]))
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
- if -l2 in self.sels_set:
823
- conns[l1].add(-l2)
824
- conns[-l2].add(l1)
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
- if confl: # filtering out unnecessary connections
830
- ccopy = {}
831
- confl = set(confl)
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
- for l in conns:
834
- if l not in confl:
835
- cc = conns[l].difference(confl)
836
- if cc:
837
- ccopy[l] = cc
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
- conns = ccopy
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
- lits = set(conns.keys())
883
+
855
884
  while lits:
856
- am1 = [min(lits, key=lambda l: len(conns[l]))]
885
+ am1 = [min(lits, key=lambda l: degree[l])]
857
886
 
858
- for l in sorted(conns[am1[0]], key=lambda l: len(conns[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
- if l_added not in conns[l]:
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 lits and connections
867
- lits.difference_update(set(am1))
868
- for l in conns:
869
- conns[l] = conns[l].difference(set(am1))
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-sat
3
- Version: 1.9.dev3
3
+ Version: 1.9.dev12
4
4
  Summary: A Python library for prototyping with SAT oracles
5
5
  Home-page: https://github.com/pysathq/pysat
6
6
  Author: Alexey Ignatiev, Joao Marques-Silva, Antonio Morgado
@@ -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,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (82.0.1)
2
+ Generator: setuptools (84.0.0)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp313-cp313-win_amd64
5
5
 
@@ -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,,