cdclkit 0.1.4__tar.gz → 0.1.5__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.
- {cdclkit-0.1.4 → cdclkit-0.1.5}/PKG-INFO +3 -2
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit/__init__.py +1 -1
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit/solver.py +20 -4
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit.egg-info/PKG-INFO +3 -2
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit.egg-info/SOURCES.txt +2 -1
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit.egg-info/requires.txt +1 -1
- {cdclkit-0.1.4 → cdclkit-0.1.5}/pyproject.toml +3 -2
- cdclkit-0.1.5/tests/test_target_scaling.py +78 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/LICENSE +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/README.md +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit/__main__.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit/brute.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit/cli.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit/encodings.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit/heap.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit/model.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit/mus.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit/native.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit/pipeline.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit/portfolio.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit/preprocess.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit/py.typed +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit/pyeq.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit.egg-info/dependency_links.txt +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit.egg-info/entry_points.txt +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/cdclkit.egg-info/top_level.txt +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/setup.cfg +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_adaptive_proof.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_agent_docs.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_brute.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_cli_defaults.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_core.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_encodings.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_integration.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_mus.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_native.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_native_solver.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_packaging.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_pipeline.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_portfolio.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_preprocess.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_proof.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_pyeq.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_readme.py +0 -0
- {cdclkit-0.1.4 → cdclkit-0.1.5}/tests/test_solver.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cdclkit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: A CDCL SAT solver, preprocessor, encoding library and modelling layer, written from scratch in readable Python. Every answer comes with a certificate.
|
|
5
5
|
Author: Carlo Perassi
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
20
21
|
Classifier: Programming Language :: Rust
|
|
21
22
|
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
22
23
|
Requires-Python: >=3.10
|
|
@@ -24,7 +25,7 @@ Description-Content-Type: text/markdown
|
|
|
24
25
|
License-File: LICENSE
|
|
25
26
|
Requires-Dist: dratify>=0.1.7
|
|
26
27
|
Provides-Extra: native
|
|
27
|
-
Requires-Dist: cdclkit-native==0.1.
|
|
28
|
+
Requires-Dist: cdclkit-native==0.1.5; extra == "native"
|
|
28
29
|
Dynamic: license-file
|
|
29
30
|
|
|
30
31
|
# cdclkit
|
|
@@ -52,7 +52,7 @@ from .preprocess import Preprocessor, preprocess
|
|
|
52
52
|
from dratify.proof import DRATChecker, MemoryProof, ProofWriter, check_proof
|
|
53
53
|
from .solver import Config, SAT, Solver, Stats, UNKNOWN, UNSAT
|
|
54
54
|
|
|
55
|
-
__version__ = "0.1.
|
|
55
|
+
__version__ = "0.1.5"
|
|
56
56
|
|
|
57
57
|
__all__ = [
|
|
58
58
|
"CNF",
|
|
@@ -392,6 +392,11 @@ class Solver:
|
|
|
392
392
|
#: depth. Only meaningful when cfg.target_phase is on.
|
|
393
393
|
self._target = bytearray()
|
|
394
394
|
self._target_size = 0
|
|
395
|
+
#: how much of the current trail the target already records: for every
|
|
396
|
+
#: i < _target_synced, _target holds trail[i]'s value. The trail only
|
|
397
|
+
#: grows by appending and only shrinks in _cancel_until, so a prefix
|
|
398
|
+
#: that has been copied stays copied until a backtrack cuts into it.
|
|
399
|
+
self._target_synced = 0
|
|
395
400
|
self._walk_best = 1 << 30
|
|
396
401
|
self._walk_stale = 0
|
|
397
402
|
self.frozen = bytearray() # variables excluded from decisions
|
|
@@ -605,6 +610,8 @@ class Solver:
|
|
|
605
610
|
del trail[bound:]
|
|
606
611
|
del self.trail_lim[level:]
|
|
607
612
|
self.qhead = len(trail)
|
|
613
|
+
if self._target_synced > bound:
|
|
614
|
+
self._target_synced = bound
|
|
608
615
|
|
|
609
616
|
# ------------------------------------------------------------ propagation
|
|
610
617
|
|
|
@@ -1154,6 +1161,7 @@ class Solver:
|
|
|
1154
1161
|
self._target[:] = best
|
|
1155
1162
|
self.polarity[:] = best
|
|
1156
1163
|
self._target_size = 0 # the target now describes the walk, not a trail
|
|
1164
|
+
self._target_synced = 0
|
|
1157
1165
|
|
|
1158
1166
|
# -------------------------------------------------------------- restarts
|
|
1159
1167
|
|
|
@@ -1229,13 +1237,21 @@ class Solver:
|
|
|
1229
1237
|
self._reduce_db()
|
|
1230
1238
|
else:
|
|
1231
1239
|
# A new deepest conflict-free trail: remember the assignment
|
|
1232
|
-
# that reached it
|
|
1233
|
-
#
|
|
1240
|
+
# that reached it -- copying only what the target does not
|
|
1241
|
+
# already hold. This used to copy the whole trail on every
|
|
1242
|
+
# improvement, on the theory that improvements become rare
|
|
1243
|
+
# quickly. They do once conflicts start; a search that makes
|
|
1244
|
+
# many decisions without one improves on every decision, and
|
|
1245
|
+
# n of them cost O(n^2) (tests/test_target_scaling.py). The
|
|
1246
|
+
# target ends up byte-for-byte what the full copy produced.
|
|
1234
1247
|
if self.cfg.target_phase and len(self.trail) > self._target_size:
|
|
1235
|
-
|
|
1248
|
+
trail = self.trail
|
|
1249
|
+
self._target_size = len(trail)
|
|
1236
1250
|
target = self._target
|
|
1237
|
-
for
|
|
1251
|
+
for i in range(self._target_synced, len(trail)):
|
|
1252
|
+
t = trail[i]
|
|
1238
1253
|
target[t >> 1] = 0 if (t & 1) else 1
|
|
1254
|
+
self._target_synced = len(trail)
|
|
1239
1255
|
|
|
1240
1256
|
if max_conflicts is not None and conflicts >= max_conflicts:
|
|
1241
1257
|
self._cancel_until(len(self.assumptions_applied))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cdclkit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: A CDCL SAT solver, preprocessor, encoding library and modelling layer, written from scratch in readable Python. Every answer comes with a certificate.
|
|
5
5
|
Author: Carlo Perassi
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
20
21
|
Classifier: Programming Language :: Rust
|
|
21
22
|
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
22
23
|
Requires-Python: >=3.10
|
|
@@ -24,7 +25,7 @@ Description-Content-Type: text/markdown
|
|
|
24
25
|
License-File: LICENSE
|
|
25
26
|
Requires-Dist: dratify>=0.1.7
|
|
26
27
|
Provides-Extra: native
|
|
27
|
-
Requires-Dist: cdclkit-native==0.1.
|
|
28
|
+
Requires-Dist: cdclkit-native==0.1.5; extra == "native"
|
|
28
29
|
Dynamic: license-file
|
|
29
30
|
|
|
30
31
|
# cdclkit
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cdclkit"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.5"
|
|
8
8
|
description = "A CDCL SAT solver, preprocessor, encoding library and modelling layer, written from scratch in readable Python. Every answer comes with a certificate."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -22,6 +22,7 @@ classifiers = [
|
|
|
22
22
|
"Programming Language :: Python :: 3.12",
|
|
23
23
|
"Programming Language :: Python :: 3.13",
|
|
24
24
|
"Programming Language :: Python :: 3.14",
|
|
25
|
+
"Programming Language :: Python :: 3.15",
|
|
25
26
|
"Programming Language :: Rust",
|
|
26
27
|
"Topic :: Scientific/Engineering :: Mathematics",
|
|
27
28
|
]
|
|
@@ -35,7 +36,7 @@ dependencies = ["dratify>=0.1.7"]
|
|
|
35
36
|
# The Rust accelerator, as abi3 wheels for Linux and macOS. Pinned exactly:
|
|
36
37
|
# the two are bit-exact by construction and a mismatched pair is a divergence
|
|
37
38
|
# nobody would look for. tests/test_packaging.py enforces the pin.
|
|
38
|
-
native = ["cdclkit-native==0.1.
|
|
39
|
+
native = ["cdclkit-native==0.1.5"]
|
|
39
40
|
|
|
40
41
|
[project.scripts]
|
|
41
42
|
cdclkit = "cdclkit.__main__:main"
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Copyright (c) 2026 Carlo Perassi. Licensed under the Apache License 2.0.
|
|
3
|
+
"""Target phases must not make the search quadratic in the number of variables.
|
|
4
|
+
|
|
5
|
+
With target phases on (the default), each time the trail reached a new depth
|
|
6
|
+
the solver copied the whole trail into the target. The comment beside it said
|
|
7
|
+
improvements "become rare quickly". That holds only once conflicts happen. A
|
|
8
|
+
search that makes many decisions without a conflict improves on every one, so
|
|
9
|
+
n decisions copied 1, 2, ..., n entries.
|
|
10
|
+
|
|
11
|
+
Measured on the Python engine, solving one unit clause in a formula that
|
|
12
|
+
declares n variables: 4,096 took 0.8 s, 16,384 took 10.6 s and 65,536 took
|
|
13
|
+
144 s -- each 4x costing about 13x. Turning target phases off made 16,384
|
|
14
|
+
take 0.11 s. Since dratify 0.1.7 a header may declare up to 2^20 variables
|
|
15
|
+
from a file of any length, so a 22-byte file cost hours of CPU.
|
|
16
|
+
|
|
17
|
+
The test compares scaling rather than absolute time, which shared CI runners
|
|
18
|
+
cannot be trusted with: quadrupling the variables should cost about 4x when
|
|
19
|
+
the search is linear and about 16x when it is quadratic. The threshold sits
|
|
20
|
+
between them, and each size takes the best of three runs to damp noise.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import time
|
|
26
|
+
import unittest
|
|
27
|
+
|
|
28
|
+
from cdclkit import native
|
|
29
|
+
from cdclkit.solver import Solver
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _solve(nvars: int, engine: str) -> bool:
|
|
33
|
+
"""One unit clause, `nvars` declared: every other variable is a decision."""
|
|
34
|
+
if engine == "native":
|
|
35
|
+
s = native.require().Solver(nvars) # defaults match Config
|
|
36
|
+
else:
|
|
37
|
+
s = Solver(nvars)
|
|
38
|
+
s.add_clause([0])
|
|
39
|
+
return s.solve()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _seconds(nvars: int, engine: str) -> float:
|
|
43
|
+
best = float("inf")
|
|
44
|
+
for _ in range(3):
|
|
45
|
+
t0 = time.perf_counter()
|
|
46
|
+
sat = _solve(nvars, engine)
|
|
47
|
+
best = min(best, time.perf_counter() - t0)
|
|
48
|
+
assert sat
|
|
49
|
+
return best
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class TestUnusedVariablesCostLinearTime(unittest.TestCase):
|
|
53
|
+
#: quadrupling: ~4x if linear, ~16x if quadratic
|
|
54
|
+
LIMIT = 8.0
|
|
55
|
+
|
|
56
|
+
def _check(self, engine: str, small: int):
|
|
57
|
+
t_small = _seconds(small, engine)
|
|
58
|
+
t_large = _seconds(4 * small, engine)
|
|
59
|
+
ratio = t_large / max(t_small, 1e-4)
|
|
60
|
+
self.assertLess(
|
|
61
|
+
ratio, self.LIMIT,
|
|
62
|
+
f"{engine}: {small:,} vars took {t_small:.3f}s and "
|
|
63
|
+
f"{4 * small:,} took {t_large:.3f}s, a {ratio:.1f}x increase for "
|
|
64
|
+
f"4x the variables. Linear is ~4x; this is closer to quadratic.")
|
|
65
|
+
|
|
66
|
+
def test_python_engine(self):
|
|
67
|
+
self._check("python", 4096)
|
|
68
|
+
|
|
69
|
+
@unittest.skipUnless(native.available(),
|
|
70
|
+
"native engine not built for this interpreter")
|
|
71
|
+
def test_native_engine(self):
|
|
72
|
+
# Rust is fast enough that the quadratic term only dominates at
|
|
73
|
+
# larger sizes.
|
|
74
|
+
self._check("native", 32768)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|