python-sat 1.9.dev3__tar.gz → 1.9.dev5__tar.gz

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

Potentially problematic release.


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

Files changed (116) hide show
  1. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/MANIFEST.in +1 -0
  2. {python_sat-1.9.dev3/python_sat.egg-info → python_sat-1.9.dev5}/PKG-INFO +1 -1
  3. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/README.rst +1 -0
  4. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/examples/rc2.py +50 -23
  5. python_sat-1.9.dev5/formula/pf_plus.hh +36 -0
  6. python_sat-1.9.dev5/formula/pf_py.hh +185 -0
  7. python_sat-1.9.dev5/formula/pf_scan.hh +181 -0
  8. python_sat-1.9.dev5/formula/pf_weight.hh +93 -0
  9. python_sat-1.9.dev5/formula/pyformula.hh +99 -0
  10. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/pysat/__init__.py +1 -1
  11. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/pysat/solvers.py +3 -3
  12. {python_sat-1.9.dev3 → python_sat-1.9.dev5/python_sat.egg-info}/PKG-INFO +1 -1
  13. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/python_sat.egg-info/SOURCES.txt +5 -0
  14. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/LICENSE.txt +0 -0
  15. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/allies/__init__.py +0 -0
  16. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/allies/approxmc.py +0 -0
  17. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/allies/unigen.py +0 -0
  18. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/cardenc/bitwise.hh +0 -0
  19. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/cardenc/card.hh +0 -0
  20. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/cardenc/clset.hh +0 -0
  21. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/cardenc/common.hh +0 -0
  22. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/cardenc/itot.hh +0 -0
  23. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/cardenc/ladder.hh +0 -0
  24. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/cardenc/mto.hh +0 -0
  25. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/cardenc/pairwise.hh +0 -0
  26. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/cardenc/ptypes.hh +0 -0
  27. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/cardenc/pycard.cc +0 -0
  28. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/cardenc/seqcounter.hh +0 -0
  29. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/cardenc/sortcard.hh +0 -0
  30. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/cardenc/utils.hh +0 -0
  31. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/examples/__init__.py +0 -0
  32. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/examples/bbscan.py +0 -0
  33. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/examples/bica.py +0 -0
  34. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/examples/fm.py +0 -0
  35. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/examples/genhard.py +0 -0
  36. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/examples/hitman.py +0 -0
  37. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/examples/lbx.py +0 -0
  38. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/examples/lsu.py +0 -0
  39. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/examples/mcsls.py +0 -0
  40. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/examples/models.py +0 -0
  41. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/examples/musx.py +0 -0
  42. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/examples/optux.py +0 -0
  43. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/examples/primer.py +0 -0
  44. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/examples/usage.py +0 -0
  45. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/formula/pf_parse.cc +0 -0
  46. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/formula/pf_plus.cc +0 -0
  47. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/formula/pf_weight.cc +0 -0
  48. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/formula/pyformula.cc +0 -0
  49. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/pysat/_fileio.py +0 -0
  50. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/pysat/_utils.py +0 -0
  51. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/pysat/card.py +0 -0
  52. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/pysat/engines.py +0 -0
  53. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/pysat/formula.py +0 -0
  54. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/pysat/integer.py +0 -0
  55. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/pysat/pb.py +0 -0
  56. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/pysat/process.py +0 -0
  57. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/python_sat.egg-info/dependency_links.txt +0 -0
  58. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/python_sat.egg-info/requires.txt +0 -0
  59. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/python_sat.egg-info/top_level.txt +0 -0
  60. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/requirements.txt +0 -0
  61. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/setup.cfg +0 -0
  62. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/setup.py +0 -0
  63. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/cadical103.tar.gz +0 -0
  64. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/cadical153.tar.gz +0 -0
  65. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/cadical195.tar.gz +0 -0
  66. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/cadical300.tar.gz +0 -0
  67. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/glucose30.tar.gz +0 -0
  68. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/glucose41.tar.gz +0 -0
  69. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/glucose421.tar.gz +0 -0
  70. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/lingeling.tar.gz +0 -0
  71. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/maplechrono.zip +0 -0
  72. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/maplecm.zip +0 -0
  73. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/maplesat.zip +0 -0
  74. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/mergesat3.tar.gz +0 -0
  75. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/minicard.tar.gz +0 -0
  76. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/minisat22.tar.gz +0 -0
  77. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/minisatgh.zip +0 -0
  78. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/cadical103.patch +0 -0
  79. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/cadical153.patch +0 -0
  80. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/cadical195.patch +0 -0
  81. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/cadical300.patch +0 -0
  82. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/glucose30.patch +0 -0
  83. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/glucose41.patch +0 -0
  84. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/glucose421.patch +0 -0
  85. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/gluecard30.patch +0 -0
  86. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/gluecard41.patch +0 -0
  87. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/kissat404.patch +0 -0
  88. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/lingeling.patch +0 -0
  89. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/maplechrono.patch +0 -0
  90. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/maplecm.patch +0 -0
  91. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/maplesat.patch +0 -0
  92. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/mergesat3.patch +0 -0
  93. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/minicard.patch +0 -0
  94. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/minisat22.patch +0 -0
  95. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/minisatep.patch +0 -0
  96. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/patches/minisatgh.patch +0 -0
  97. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/prepare.py +0 -0
  98. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/solvers/pysolvers.cc +0 -0
  99. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_accum_stats.py +0 -0
  100. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_atmost.py +0 -0
  101. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_atmost1.py +0 -0
  102. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_atmostk.py +0 -0
  103. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_boolengine.py +0 -0
  104. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_clausification.py +0 -0
  105. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_cnf.py +0 -0
  106. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_encode_pb_conditional.py +0 -0
  107. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_equals1.py +0 -0
  108. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_formula_parsing.py +0 -0
  109. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_formula_unique.py +0 -0
  110. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_idpool.py +0 -0
  111. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_integer.py +0 -0
  112. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_process.py +0 -0
  113. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_propagate.py +0 -0
  114. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_unique_model.py +0 -0
  115. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_unique_mus.py +0 -0
  116. {python_sat-1.9.dev3 → python_sat-1.9.dev5}/tests/test_warmstart.py +0 -0
@@ -6,3 +6,4 @@ recursive-include cardenc *.hh
6
6
  include solvers/prepare.py
7
7
  recursive-include solvers *.patch
8
8
  recursive-include solvers *.zip *.tar.gz
9
+ recursive-include formula *.hh
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-sat
3
- Version: 1.9.dev3
3
+ Version: 1.9.dev5
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
@@ -79,6 +79,7 @@ Minisat-based solvers only *core* versions are integrated):
79
79
  - Minicard (`1.2 <https://github.com/liffiton/minicard>`__)
80
80
  - Minisat (`2.2 release <http://minisat.se/MiniSat.html>`__)
81
81
  - Minisat (`GitHub version <https://github.com/niklasso/minisat>`__)
82
+ - Minisat (`with IPASIR-UP support <https://github.com/hchenqide/minisat>`__)
82
83
 
83
84
  In order to make SAT-based prototyping easier, PySAT integrates a variety of
84
85
  cardinality encodings. All of them are implemented from scratch in C++. The
@@ -131,6 +131,7 @@
131
131
  #
132
132
  #==============================================================================
133
133
  from __future__ import print_function
134
+ import bisect
134
135
  import collections
135
136
  import getopt
136
137
  import itertools
@@ -810,8 +811,8 @@ class RC2(object):
810
811
  handled by calling :func:`process_am1`.
811
812
  """
812
813
 
813
- # literal connections
814
- conns = collections.defaultdict(lambda: set([]))
814
+ # literal connections and selectors conflicting on their own
815
+ conns = collections.defaultdict(list)
815
816
  confl = []
816
817
 
817
818
  # prepare connections
@@ -819,26 +820,29 @@ class RC2(object):
819
820
  st, props = self.oracle.propagate(assumptions=[l1], phase_saving=2)
820
821
  if st:
821
822
  for l2 in props:
822
- if -l2 in self.sels_set:
823
- conns[l1].add(-l2)
824
- conns[-l2].add(l1)
823
+ l2 = -l2
824
+ if l2 in self.sels_set:
825
+ conns[l1].append(l2)
826
+ conns[l2].append(l1)
825
827
  else:
826
828
  # propagating this literal results in a conflict
827
829
  confl.append(l1)
828
830
 
829
- if confl: # filtering out unnecessary connections
830
- ccopy = {}
831
- confl = set(confl)
831
+ # sort (in-place) and deduplicate adjacency lists
832
+ for l in list(conns):
833
+ neigh = conns[l]
834
+ if neigh:
835
+ neigh.sort()
832
836
 
833
- for l in conns:
834
- if l not in confl:
835
- cc = conns[l].difference(confl)
836
- if cc:
837
- ccopy[l] = cc
837
+ j = 1
838
+ for i in range(1, len(neigh)):
839
+ if neigh[i] != neigh[j - 1]:
840
+ neigh[j] = neigh[i]
841
+ j += 1
838
842
 
839
- conns = ccopy
840
- confl = list(confl)
843
+ del neigh[j:]
841
844
 
845
+ if confl:
842
846
  # processing unit size cores
843
847
  for l in confl:
844
848
  self.core, self.minw = [l], self.wght[l]
@@ -849,24 +853,47 @@ class RC2(object):
849
853
  print('c unit cores found: {0}; cost: {1}'.format(len(confl),
850
854
  self.cost))
851
855
 
856
+ # the following graph manipulation requires
857
+ # fast membership checks for confl; making it a set
858
+ confl = set(confl)
859
+
860
+ # current degrees in the remaining graph;
861
+ # literals disconnected after removing conflicts are ignored
862
+ degree, lits = {}, set()
863
+ for l in conns:
864
+ if l not in confl:
865
+ d = sum(1 for l2 in conns[l] if l2 not in confl)
866
+ if d:
867
+ degree[l] = d
868
+ lits.add(l)
869
+
870
+ # detect AM1s on the original formula with conflicting selectors
871
+ # filtered out, similarly to the previous implementation
852
872
  nof_am1 = 0
853
873
  len_am1 = []
854
- lits = set(conns.keys())
874
+
855
875
  while lits:
856
- am1 = [min(lits, key=lambda l: len(conns[l]))]
876
+ am1 = [min(lits, key=lambda l: degree[l])]
857
877
 
858
- for l in sorted(conns[am1[0]], key=lambda l: len(conns[l])):
878
+ for l in sorted(conns[am1[0]], key=lambda l: degree.get(l, 0)):
859
879
  if l in lits:
860
880
  for l_added in am1[1:]:
861
- if l_added not in conns[l]:
881
+ # adjacency lists are sorted, so we use
882
+ # binary search for clique membership tests
883
+ i = bisect.bisect_left(conns[l], l_added)
884
+ if i == len(conns[l]) or conns[l][i] != l_added:
862
885
  break
863
886
  else:
864
887
  am1.append(l)
865
888
 
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))
889
+ # updating remaining literals and their degrees
890
+ am1_set = set(am1)
891
+ lits.difference_update(am1_set)
892
+
893
+ for l in am1:
894
+ for l2 in conns.get(l, []):
895
+ if l2 in lits:
896
+ degree[l2] -= 1
870
897
 
871
898
  if len(am1) > 1:
872
899
  # treat the new atmost1 relation
@@ -0,0 +1,36 @@
1
+ /*
2
+ * pf_plus.hh
3
+ *
4
+ * Created on: May 19, 2025
5
+ * Author: Alexey Ignatiev
6
+ * E-mail: alexey.ignatiev@monash.edu
7
+ */
8
+
9
+ #ifndef __PF_PLUS_HH__
10
+ #define __PF_PLUS_HH__
11
+
12
+ #include "pyformula.hh"
13
+
14
+ // reset the temporary state used for CNF+/WCNF+ bodies
15
+ //=============================================================================
16
+ static inline void init_plus_body(plus_body &body)
17
+ {
18
+ body.weighted = false;
19
+ body.clause = false;
20
+ body.rhs = 0;
21
+ body.sumw = 0;
22
+ body.lits.clear();
23
+ body.wght.clear();
24
+ }
25
+
26
+ // normalize a >= constraint into an equivalent <= form
27
+ //=============================================================================
28
+ static inline void normalize_ge(plus_body &body)
29
+ {
30
+ for (size_t i = 0; i < body.lits.size(); ++i)
31
+ body.lits[i] = -body.lits[i];
32
+
33
+ body.rhs = body.sumw - body.rhs;
34
+ }
35
+
36
+ #endif
@@ -0,0 +1,185 @@
1
+ /*
2
+ * pf_py.hh
3
+ *
4
+ * Created on: May 19, 2025
5
+ * Author: Alexey Ignatiev
6
+ * E-mail: alexey.ignatiev@monash.edu
7
+ */
8
+
9
+ #ifndef __PF_PY_HH__
10
+ #define __PF_PY_HH__
11
+
12
+ #include "pyformula.hh"
13
+ #include "pf_scan.hh"
14
+
15
+ // tiny integer cache for repeated literals/weights
16
+ //=============================================================================
17
+ // hash an integer for the tiny object cache
18
+ static inline uint32_t pyint_cache_hash(long long v)
19
+ {
20
+ return ((uint32_t)v * 2654435761u) & (PYINT_CACHE_SIZE - 1u);
21
+ }
22
+
23
+ // fetch a cached Python integer or create it on demand
24
+ //=============================================================================
25
+ static inline PyObject *pyint_cache_get_or_make(int_cache *cache, long long v)
26
+ {
27
+ uint32_t idx = pyint_cache_hash(v);
28
+ int_cache_ent *e = &cache->slots[idx];
29
+ if (e->used && e->key == v) {
30
+ Py_INCREF(e->obj);
31
+ return e->obj;
32
+ }
33
+
34
+ PyObject *obj = PyLong_FromLongLong(v);
35
+ if (obj == NULL)
36
+ return NULL;
37
+
38
+ if (e->used)
39
+ Py_DECREF(e->obj);
40
+ else
41
+ cache->used_idx[cache->used_count++] = (uint16_t)idx;
42
+
43
+ e->used = 1;
44
+ e->key = v;
45
+ e->obj = obj;
46
+
47
+ Py_INCREF(obj);
48
+ return obj;
49
+ }
50
+
51
+ // release all Python integers stored in the cache
52
+ //=============================================================================
53
+ static inline void pyint_cache_clear(int_cache *cache)
54
+ {
55
+ for (size_t i = 0; i < cache->used_count; ++i) {
56
+ int_cache_ent *e = &cache->slots[cache->used_idx[i]];
57
+ if (e->used) {
58
+ Py_DECREF(e->obj);
59
+ e->obj = NULL;
60
+ e->used = 0;
61
+ }
62
+ }
63
+
64
+ cache->used_count = 0;
65
+ }
66
+
67
+ // generic helpers
68
+ //=============================================================================
69
+ // build a fast lookup table for comment lead characters
70
+ static inline void init_comment_mask(PyObject *comment_lead, bool mask[256])
71
+ {
72
+ memset(mask, 0, sizeof(bool) * 256);
73
+ mask[(unsigned char)'p'] = true;
74
+
75
+ if (comment_lead == NULL)
76
+ return;
77
+
78
+ PyObject *iter = PyObject_GetIter(comment_lead);
79
+ if (iter == NULL) {
80
+ PyErr_Clear();
81
+ return;
82
+ }
83
+
84
+ PyObject *item;
85
+ while ((item = PyIter_Next(iter)) != NULL) {
86
+ if (PyUnicode_Check(item)) {
87
+ Py_ssize_t size = 0;
88
+ const char *s = PyUnicode_AsUTF8AndSize(item, &size);
89
+ if (s && size > 0)
90
+ mask[(unsigned char)s[0]] = true;
91
+ }
92
+ #if PY_MAJOR_VERSION < 3
93
+ else if (PyString_Check(item)) {
94
+ char *s = NULL;
95
+ Py_ssize_t size = 0;
96
+ if (PyString_AsStringAndSize(item, &s, &size) == 0 && s && size > 0)
97
+ mask[(unsigned char)s[0]] = true;
98
+ }
99
+ #endif
100
+ Py_DECREF(item);
101
+ }
102
+
103
+ Py_DECREF(iter);
104
+ PyErr_Clear();
105
+ }
106
+
107
+ // expose a Python string/bytes object as a contiguous text buffer
108
+ //=============================================================================
109
+ static inline bool get_text_view(PyObject *obj, const char **ptr,
110
+ Py_ssize_t *len)
111
+ {
112
+ if (PyUnicode_Check(obj)) {
113
+ *ptr = PyUnicode_AsUTF8AndSize(obj, len);
114
+ return *ptr != NULL;
115
+ }
116
+ else if (PyBytes_Check(obj)) {
117
+ return PyBytes_AsStringAndSize(obj, (char **)ptr, len) == 0;
118
+ }
119
+ #if PY_MAJOR_VERSION < 3
120
+ else if (PyString_Check(obj)) {
121
+ return PyString_AsStringAndSize(obj, (char **)ptr, len) == 0;
122
+ }
123
+ #endif
124
+
125
+ PyErr_SetString(PyExc_TypeError, "string or bytes expected");
126
+ return false;
127
+ }
128
+
129
+ // create a Python string from a line span
130
+ //=============================================================================
131
+ static inline PyObject *mk_line(const char *beg, const char *end)
132
+ {
133
+ return PyUnicode_FromStringAndSize(beg, end - beg);
134
+ }
135
+
136
+ // append a raw comment line to the comments list
137
+ //=============================================================================
138
+ static inline bool append_comment(PyObject *comments, const char *beg,
139
+ const char *end)
140
+ {
141
+ PyObject *line = mk_line(beg, end);
142
+ if (line == NULL)
143
+ return false;
144
+
145
+ int ok = PyList_Append(comments, line);
146
+ Py_DECREF(line);
147
+ return ok >= 0;
148
+ }
149
+
150
+ // update the current maximum variable identifier
151
+ //=============================================================================
152
+ static inline void update_nv(long long lit, long long &nv)
153
+ {
154
+ long long a = lit < 0 ? -lit : lit;
155
+ if (a > nv)
156
+ nv = a;
157
+ }
158
+
159
+ // convert a vector of integers into a Python list
160
+ //=============================================================================
161
+ static inline PyObject *mk_llist(const vector<long long> &vals,
162
+ int_cache *cache, long long *nv)
163
+ {
164
+ PyObject *lst = PyList_New((Py_ssize_t)vals.size());
165
+ if (lst == NULL)
166
+ return NULL;
167
+
168
+ for (Py_ssize_t i = 0; i < (Py_ssize_t)vals.size(); ++i) {
169
+ long long v = vals[(size_t)i];
170
+ if (nv)
171
+ update_nv(v, *nv);
172
+
173
+ PyObject *obj = pyint_cache_get_or_make(cache, v);
174
+ if (obj == NULL) {
175
+ Py_DECREF(lst);
176
+ return NULL;
177
+ }
178
+
179
+ PyList_SET_ITEM(lst, i, obj);
180
+ }
181
+
182
+ return lst;
183
+ }
184
+
185
+ #endif
@@ -0,0 +1,181 @@
1
+ /*
2
+ * pf_scan.hh
3
+ *
4
+ * Created on: May 19, 2025
5
+ * Author: Alexey Ignatiev
6
+ * E-mail: alexey.ignatiev@monash.edu
7
+ */
8
+
9
+ #ifndef __PF_SCAN_HH__
10
+ #define __PF_SCAN_HH__
11
+
12
+ #include "pyformula.hh"
13
+
14
+ // low-level cursor helpers
15
+ //=============================================================================
16
+ // skip inline whitespace while advancing the cursor
17
+ static inline void skip_ws(const char * &p, const char *end)
18
+ {
19
+ while (p < end && ((*p >= 9 && *p <= 13) || *p == 32))
20
+ ++p;
21
+ }
22
+
23
+ // check whether a bounded span matches a C string exactly
24
+ //=============================================================================
25
+ static inline bool span_eq(const char *beg, const char *end, const char *s)
26
+ {
27
+ size_t n = strlen(s);
28
+ return (Py_ssize_t)n == end - beg && memcmp(beg, s, n) == 0;
29
+ }
30
+
31
+ // check whether a line starts with a fixed prefix
32
+ //=============================================================================
33
+ static inline bool line_starts(const char *beg, const char *end,
34
+ const char *prefix)
35
+ {
36
+ size_t n = strlen(prefix);
37
+ return (Py_ssize_t)n <= end - beg && memcmp(beg, prefix, n) == 0;
38
+ }
39
+
40
+ // test whether a trimmed line is empty
41
+ //=============================================================================
42
+ static inline bool line_empty(const char *beg, const char *end)
43
+ {
44
+ return end <= beg;
45
+ }
46
+
47
+ // split the input into trimmed logical lines
48
+ //=============================================================================
49
+ static inline bool next_line(const char * &p, const char *end,
50
+ const char * &beg, const char * &lend)
51
+ {
52
+ if (p >= end)
53
+ return false;
54
+
55
+ beg = p;
56
+ while (p < end && *p != '\n')
57
+ ++p;
58
+
59
+ lend = p;
60
+ if (p < end)
61
+ ++p;
62
+
63
+ while (lend > beg && isspace((unsigned char)lend[-1]))
64
+ --lend;
65
+
66
+ return true;
67
+ }
68
+
69
+ // read the next whitespace-delimited token span
70
+ //=============================================================================
71
+ static inline bool read_span(const char * &p, const char *end,
72
+ const char * &beg, const char * &tend)
73
+ {
74
+ skip_ws(p, end);
75
+ if (p >= end)
76
+ return false;
77
+
78
+ beg = p;
79
+ while (p < end && !isspace((unsigned char)*p))
80
+ ++p;
81
+
82
+ tend = p;
83
+ return true;
84
+ }
85
+
86
+ // check whether a span has the syntax of a signed integer
87
+ //=============================================================================
88
+ static inline bool span_is_integer_like(const char *beg, const char *end)
89
+ {
90
+ if (beg >= end)
91
+ return false;
92
+
93
+ if (*beg == '+' || *beg == '-')
94
+ ++beg;
95
+ if (beg >= end)
96
+ return false;
97
+
98
+ for (; beg < end; ++beg) {
99
+ if (*beg < '0' || *beg > '9')
100
+ return false;
101
+ }
102
+
103
+ return true;
104
+ }
105
+
106
+ // parse a bounded signed integer without copying
107
+ //=============================================================================
108
+ static inline bool parse_ll_span(const char *beg, const char *end,
109
+ long long &out)
110
+ {
111
+ if (beg >= end)
112
+ return false;
113
+
114
+ bool neg = false;
115
+ if (*beg == '+' || *beg == '-') {
116
+ neg = (*beg == '-');
117
+ ++beg;
118
+ }
119
+ if (beg >= end)
120
+ return false;
121
+
122
+ unsigned long long lim = neg ? (unsigned long long)LLONG_MAX + 1ULL
123
+ : (unsigned long long)LLONG_MAX;
124
+ unsigned long long val = 0;
125
+
126
+ for (; beg < end; ++beg) {
127
+ unsigned char ch = (unsigned char)*beg;
128
+ if (ch < '0' || ch > '9')
129
+ return false;
130
+
131
+ unsigned long long dig = (unsigned long long)(ch - '0');
132
+ if (val > (lim - dig) / 10ULL)
133
+ return false;
134
+
135
+ val = val * 10ULL + dig;
136
+ }
137
+
138
+ if (neg) {
139
+ if (val == (unsigned long long)LLONG_MAX + 1ULL)
140
+ out = LLONG_MIN;
141
+ else
142
+ out = -(long long)val;
143
+ }
144
+ else {
145
+ out = (long long)val;
146
+ }
147
+
148
+ return true;
149
+ }
150
+
151
+ // parse the next integer directly from the cursor
152
+ //=============================================================================
153
+ static inline bool read_int(const char *&p, const char *end, long long &out)
154
+ {
155
+ skip_ws(p, end);
156
+ if (p >= end) {
157
+ PyErr_SetString(PyExc_ValueError,
158
+ "unexpected end of input while parsing integer");
159
+ return false;
160
+ }
161
+
162
+ const char *beg = p;
163
+ if (*p == '+' || *p == '-')
164
+ ++p;
165
+ if (p >= end || *p < '0' || *p > '9') {
166
+ PyErr_SetString(PyExc_ValueError, "invalid integer token");
167
+ return false;
168
+ }
169
+
170
+ while (p < end && *p >= '0' && *p <= '9')
171
+ ++p;
172
+
173
+ if (!parse_ll_span(beg, p, out)) {
174
+ PyErr_SetString(PyExc_ValueError, "invalid integer token");
175
+ return false;
176
+ }
177
+
178
+ return true;
179
+ }
180
+
181
+ #endif
@@ -0,0 +1,93 @@
1
+ /*
2
+ * pf_weight.hh
3
+ *
4
+ * Created on: May 19, 2025
5
+ * Author: Alexey Ignatiev
6
+ * E-mail: alexey.ignatiev@monash.edu
7
+ */
8
+
9
+ #ifndef __PF_WEIGHT_HH__
10
+ #define __PF_WEIGHT_HH__
11
+
12
+ #include "pyformula.hh"
13
+ #include "pf_scan.hh"
14
+
15
+ // weights and preambles
16
+ //=============================================================================
17
+ // parse a clause or top weight as h, int, or decimal.Decimal
18
+ static inline PyObject *parse_wght_span(const char *beg, const char *end)
19
+ {
20
+ if (span_eq(beg, end, "h")) {
21
+ Py_RETURN_NONE;
22
+ }
23
+
24
+ if (span_is_integer_like(beg, end)) {
25
+ long long val;
26
+ if (!parse_ll_span(beg, end, val)) {
27
+ PyErr_SetString(PyExc_ValueError, "invalid integer weight");
28
+ return NULL;
29
+ }
30
+
31
+ return PyLong_FromLongLong(val);
32
+ }
33
+
34
+ PyObject *text = PyUnicode_FromStringAndSize(beg, end - beg);
35
+ if (text == NULL)
36
+ return NULL;
37
+
38
+ PyObject *d = PyObject_CallFunctionObjArgs(dec_cls, text, NULL);
39
+ Py_DECREF(text);
40
+ if (d == NULL)
41
+ return NULL;
42
+
43
+ PyObject *inf = PyObject_CallMethod(d, (char *)"is_infinite", NULL);
44
+ if (inf == NULL) {
45
+ Py_DECREF(d);
46
+ return NULL;
47
+ }
48
+
49
+ int is_inf = PyObject_IsTrue(inf);
50
+ Py_DECREF(inf);
51
+ if (is_inf < 0) {
52
+ Py_DECREF(d);
53
+ return NULL;
54
+ }
55
+ if (is_inf)
56
+ return d;
57
+
58
+ PyObject *ival = PyObject_CallMethod(d, (char *)"to_integral_value", NULL);
59
+ if (ival == NULL) {
60
+ Py_DECREF(d);
61
+ return NULL;
62
+ }
63
+
64
+ int is_eq = PyObject_RichCompareBool(d, ival, Py_EQ);
65
+ Py_DECREF(ival);
66
+ if (is_eq < 0) {
67
+ Py_DECREF(d);
68
+ return NULL;
69
+ }
70
+
71
+ if (is_eq) {
72
+ PyObject *res = PyNumber_Long(d);
73
+ Py_DECREF(d);
74
+ return res;
75
+ }
76
+
77
+ return d;
78
+ }
79
+
80
+ // parse a weighted literal of the form w*l
81
+ //=============================================================================
82
+ static inline bool parse_wlit_span(const char *beg, const char *end,
83
+ long long &w, long long &l)
84
+ {
85
+ const char *star = (const char *)memchr(beg, '*', (size_t)(end - beg));
86
+ if (star == NULL)
87
+ return false;
88
+
89
+ return parse_ll_span(beg, star, w) &&
90
+ parse_ll_span(star + 1, end, l) && l != 0;
91
+ }
92
+
93
+ #endif
@@ -0,0 +1,99 @@
1
+ /*
2
+ * pyformula.hh
3
+ *
4
+ * Created on: May 19, 2025
5
+ * Author: Alexey Ignatiev
6
+ * E-mail: alexey.ignatiev@monash.edu
7
+ */
8
+
9
+ #ifndef __PYFORMULA_HH__
10
+ #define __PYFORMULA_HH__
11
+
12
+ #define PY_SSIZE_T_CLEAN
13
+
14
+ #include <Python.h>
15
+ #include <ctype.h>
16
+ #include <limits.h>
17
+ #include <stdint.h>
18
+ #include <string.h>
19
+ #include <vector>
20
+
21
+ using namespace std;
22
+
23
+ // parser overview
24
+ //=============================================================================
25
+ // The code below follows one cursor-based parsing style throughout.
26
+ // The aim is to replicate the former PySAT's parsers for all *CNF? formats.
27
+ //
28
+ // Generic path:
29
+ // next_line() -> split the input into trimmed lines
30
+ // read_span() -> scan the next token on a line
31
+ // parse_ll_span() -> parse a bounded integer token/span
32
+ // read_int() -> parse the next integer directly from a cursor
33
+ //
34
+ // Formula-specific path:
35
+ // CNF : parse_clause_body() -> mk_llist()
36
+ // WCNF : read weight with parse_wght_span(), parse clause body,
37
+ // classify with append_weighted_clause()
38
+ // CNF+ : parse_plus_body() -> clause or AtMost/PB constraint
39
+ // WCNF+ : parse weight with parse_wght_span(), then parse_plus_body(),
40
+ // then route either to append_weighted_clause() or mk_atm()
41
+ //
42
+ // Weight parsing:
43
+ // parse_wght_span() parses clause/top weights as hard-marker 'h', integer,
44
+ // or decimal.Decimal; parse_wlit_span() parses weighted PB literals w*l.
45
+ //
46
+ // Public wrappers are py_parse_*(); parse_*_impl() contain the actual parser
47
+ // implementations used by those wrappers.
48
+ //
49
+ // Note that preambles are only recognized enough to distinguish the format
50
+ // and, for weighted formats, extract the optional top weight.
51
+
52
+ #define PYINT_CACHE_SIZE (1u << 10)
53
+
54
+ extern PyObject *formula_err;
55
+ extern PyObject *dec_cls;
56
+ extern PyObject *dec_inf;
57
+
58
+ typedef struct {
59
+ long long key;
60
+ PyObject *obj;
61
+ unsigned char used;
62
+ } int_cache_ent;
63
+
64
+ typedef struct {
65
+ int_cache_ent slots[PYINT_CACHE_SIZE];
66
+ uint16_t used_idx[PYINT_CACHE_SIZE];
67
+ size_t used_count;
68
+ } int_cache;
69
+
70
+ typedef struct {
71
+ bool weighted;
72
+ bool clause;
73
+ long long rhs;
74
+ long long sumw;
75
+ vector<long long> lits;
76
+ vector<long long> wght;
77
+ } plus_body;
78
+
79
+ bool parse_weight_preamble(const char *beg, const char *end, const char *kind,
80
+ const char *errmsg, PyObject **topw);
81
+
82
+ bool parse_clause_body(const char *p, const char *end,
83
+ vector<long long> &lits);
84
+
85
+ bool parse_plus_body(const char *first_beg, const char *first_end,
86
+ const char *p, const char *end, plus_body &body);
87
+
88
+ PyObject *mk_atm(const plus_body &body, int_cache *cache, long long &nv);
89
+
90
+ bool append_weighted_clause(PyObject *hard, PyObject *soft, PyObject *wght,
91
+ PyObject *negs, PyObject *topw, PyObject *zero, PyObject *cl,
92
+ PyObject *wt);
93
+
94
+ PyObject *parse_cnf_impl (PyObject *txt_obj, PyObject *comment_lead);
95
+ PyObject *parse_wcnf_impl (PyObject *txt_obj, PyObject *comment_lead);
96
+ PyObject *parse_cnfplus_impl (PyObject *txt_obj, PyObject *comment_lead);
97
+ PyObject *parse_wcnfplus_impl(PyObject *txt_obj, PyObject *comment_lead);
98
+
99
+ #endif
@@ -10,7 +10,7 @@
10
10
 
11
11
  # current version
12
12
  #==============================================================================
13
- VERSION = (1, 9, 'dev', 3)
13
+ VERSION = (1, 9, 'dev', 5)
14
14
 
15
15
 
16
16
  # PEP440 Format
@@ -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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-sat
3
- Version: 1.9.dev3
3
+ Version: 1.9.dev5
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
@@ -37,8 +37,13 @@ examples/rc2.py
37
37
  examples/usage.py
38
38
  formula/pf_parse.cc
39
39
  formula/pf_plus.cc
40
+ formula/pf_plus.hh
41
+ formula/pf_py.hh
42
+ formula/pf_scan.hh
40
43
  formula/pf_weight.cc
44
+ formula/pf_weight.hh
41
45
  formula/pyformula.cc
46
+ formula/pyformula.hh
42
47
  pysat/__init__.py
43
48
  pysat/_fileio.py
44
49
  pysat/_utils.py
File without changes
File without changes
File without changes
File without changes