Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
538c6d6
Move `hypercube_linear` module to `pumpkin-core` root
maartenflippo Mar 25, 2026
806f844
Setup the HL resolver scaffold
maartenflippo Mar 25, 2026
37f62bb
Start sketching the resolution implementation
maartenflippo Mar 26, 2026
cc6fcc8
Working toward propositional resolution
maartenflippo Mar 30, 2026
df8e935
Get propositional resolution to work
maartenflippo Mar 31, 2026
3aebe0a
Run tests with resolution resolver
maartenflippo Mar 31, 2026
6609762
Backjump using appropriate hypercube
maartenflippo Mar 31, 2026
3b1c960
Get tests to pass
maartenflippo Mar 31, 2026
0de6f06
Squashed commit of the following:
maartenflippo Mar 31, 2026
d8d6bee
Correctly backtrack
maartenflippo Mar 31, 2026
f9e1830
Re-enable incremental HL propagator
maartenflippo Mar 31, 2026
91b2d38
Remove debug output
maartenflippo Mar 31, 2026
05a03c8
Start testing fourier resolution
maartenflippo Apr 1, 2026
4b3d7ca
Get the int_lin_le test to pass
maartenflippo Apr 2, 2026
70643ff
Remove incorrect assertion
maartenflippo Apr 2, 2026
58aa044
Watch the highest decision level predicates or unassigned
maartenflippo Apr 2, 2026
a1545f4
Correctly set whether predicate is implied in predicate heap
maartenflippo Apr 2, 2026
6a5231e
Correctly register when fewer than two predicates in hypercube
maartenflippo Apr 2, 2026
12776bd
add more tracing
maartenflippo Apr 2, 2026
94aee73
Redefine linear constraints as int_lin_le[_imp]
maartenflippo Apr 2, 2026
ca23a09
Fix typo in mznlib
maartenflippo Apr 2, 2026
c5a0a80
Decompose int_lin_le_reif
maartenflippo Apr 8, 2026
165acf1
Add more assertions to hl resolver
maartenflippo Apr 9, 2026
c92c8b6
Add the HL constraint definition
maartenflippo Apr 9, 2026
41b7c15
Actually convert a HL to a clause when propositional resolving
maartenflippo Apr 10, 2026
2c612cb
Create an HL constraint
maartenflippo Apr 10, 2026
c4d2e90
Do not redefine linear constraints to le
maartenflippo Apr 10, 2026
1be6043
Label in trace when debug checks are happening
maartenflippo Apr 10, 2026
c72ad9f
Register for bound events on linear appropriately
maartenflippo Apr 10, 2026
96430e0
Only explain predicates at the current dl
maartenflippo Apr 10, 2026
ae66bd4
Skip pivots that no longer contribute to the conflict
maartenflippo Apr 10, 2026
df01e1d
Cleanup some of the tracing code
maartenflippo Apr 10, 2026
16fcaca
Construct the proper final learned HL
maartenflippo Apr 10, 2026
3b92dfd
Restore the library to what it is on main
maartenflippo Apr 10, 2026
d2a3bc6
Apply clippy fixes
maartenflippo Apr 10, 2026
4b3993a
Implement int_lin_eq using HL
maartenflippo Apr 10, 2026
fb255ff
Explain at proper decision level
maartenflippo Apr 10, 2026
ece3937
Fix int_lin constraint implementation
maartenflippo Apr 10, 2026
9b28b0d
Implement very preliminary proof tracing for HL
maartenflippo Apr 10, 2026
72533d9
Convert HL trace to SMTLIB
maartenflippo Apr 10, 2026
96a264c
Properly remove predicates from hypercube that are resolved on
maartenflippo Apr 10, 2026
44eeb44
Iterate hypercube in sorted order
maartenflippo Apr 10, 2026
d3f1acb
Fix the order in which predicates are explained
maartenflippo Apr 11, 2026
aa61ca2
Implement Display for Hypercube
maartenflippo Apr 11, 2026
0ecc5fc
Sort terms in linear
maartenflippo Apr 11, 2026
5beb764
Divide by divisor instead of term.scale
maartenflippo Apr 11, 2026
10d93c9
More info if invariant violated
maartenflippo Apr 11, 2026
b4a4f87
Correctly compute the predicates that describe a state
maartenflippo Apr 11, 2026
7c84cfa
Use display implementation on Hypercube
maartenflippo Apr 11, 2026
280ecdb
Fix conversion to clause of hypercube linear
maartenflippo Apr 11, 2026
d008cf9
Do not convert clausal explanations to hypercubes
maartenflippo Apr 11, 2026
2f5be83
Only write to trace when requested
maartenflippo Apr 11, 2026
63fd159
update appropriate statistics
maartenflippo Apr 13, 2026
c7d9744
Add toggle for intermediate hypercube linears in proof
maartenflippo Apr 13, 2026
be054af
Write variable names to proof trace in HL resolver
maartenflippo Apr 13, 2026
bd99aa7
Actually get the reason when explaining the pivot
maartenflippo Apr 13, 2026
a12d033
Started attempt at proof checker for HL
maartenflippo Apr 13, 2026
338911a
In trace, separate resolution iterations
maartenflippo Apr 13, 2026
806fd71
Add test cases to the resolver
maartenflippo Apr 13, 2026
0ccf57c
Do not do propositional resolution if not possible
maartenflippo Apr 13, 2026
2120e9a
Consider hypercube linear slack for the propagation of the hypercube
maartenflippo Apr 14, 2026
b136b13
Identify that a hypercube can be propagated to false
maartenflippo Apr 14, 2026
d885f7f
Skipping propositional resolution only when neither conflict nor
maartenflippo Apr 14, 2026
e2db9e3
Fix lazy explanation trail entry count
maartenflippo Apr 14, 2026
90c5268
Detect when the weird propagation is not a propagation
maartenflippo Apr 14, 2026
f70eb77
Explicitly construct clausal version of HL instead of iterative
maartenflippo Apr 14, 2026
3bfe5f8
Do not weaken terms to zero in explanation if not necessary
maartenflippo Apr 14, 2026
7c2e3bd
Fix formatting
maartenflippo Apr 14, 2026
0611d82
Improve Hypercube::with_predicates with incremental computation of
maartenflippo Apr 15, 2026
d517307
Only minimise once when extending the hypercube
maartenflippo Apr 15, 2026
c863ebd
Merge != and == on backward minimization pass
maartenflippo Apr 15, 2026
28deb72
Do not weaken before fourier if not necessary
maartenflippo Apr 15, 2026
bbf96f3
Stream the checking
maartenflippo Apr 15, 2026
1732ca8
In prop resolution use explanation linear if conflict linear is false
maartenflippo Apr 15, 2026
a46e170
Log when a clause is learned or a proper HL is learned
maartenflippo Apr 15, 2026
756c467
Re-add mznlib based on linears for hypercube
maartenflippo Apr 16, 2026
be6d528
Fix skipping of propositional resolution
maartenflippo Apr 16, 2026
2994b83
Move proof checker to separate project
maartenflippo Apr 16, 2026
07994e0
Reset to base linear library
maartenflippo Apr 16, 2026
de70c22
Do not decompose reified and half-reified constraints
maartenflippo Apr 16, 2026
f244ffc
Add conflict resolver flag to mznlib
maartenflippo Apr 20, 2026
9807151
Make branchers implement `Debug`
maartenflippo Apr 21, 2026
b3c3649
Sort 1uip nogoods for consistent propagation order
maartenflippo Apr 21, 2026
777b167
More improvements to tracing
maartenflippo Apr 21, 2026
42b29b1
Backup brancher that does not use vsids
maartenflippo Apr 21, 2026
a98ba3e
Remove commented code
maartenflippo Apr 21, 2026
28041bc
Use hl propagator when possible
maartenflippo Apr 21, 2026
4982566
Add the conflict resolver argument to pumpkin.msc
maartenflippo Apr 21, 2026
3b5a545
Python brancher implements Debug
maartenflippo Apr 21, 2026
85262cc
Add script to plot linears over decision levels
maartenflippo Apr 22, 2026
b63f87e
Really properly skip propositional resolution
maartenflippo Apr 22, 2026
0e0dbaa
Add proof checker to project
maartenflippo Apr 22, 2026
7c03dc6
Fix ordering bug in predicate heap
maartenflippo Apr 22, 2026
c38953e
Fixes to the propositional resolution step
maartenflippo Apr 22, 2026
cc28e64
Some plotting
maartenflippo Apr 22, 2026
c13416b
Do not repeatidly register and unregister if unnecessary in HL propag…
maartenflippo Apr 22, 2026
3136f40
Check whether backtrack could be possible before attempting it
maartenflippo Apr 23, 2026
a7bb6a8
format and remove log files
maartenflippo Apr 23, 2026
54bd37e
Refactor the resolver to make it easier to test the learned constraint
maartenflippo Apr 29, 2026
32e9d3e
Cleanup FakeBuilder interface
maartenflippo Apr 29, 2026
aa7ecb4
Introduce `linear_inequality` macro to simplify tests
maartenflippo Apr 29, 2026
846169e
Do not resolve on too strong a pivot in hypercube
maartenflippo Apr 30, 2026
1123cc7
Add claude context for HL resolution
maartenflippo Apr 30, 2026
eb23033
Extract propositional resolution operator
maartenflippo Apr 30, 2026
15d3b80
Squashed commit of the following:
maartenflippo Apr 30, 2026
97c4082
Remove vergen as a dependency
maartenflippo May 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions .claude/skills/hypercube-linear-context/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
---
name: hl-info
description: The mathematics of hypercube linear resolution.
---

# Hypercube Linear Resolution — Implementation Reference

> Summary of *Resolution Meets Cutting Planes: Introducing Hypercube Linear Resolution* (Flippo, Stuckey, Demirović — CPAIOR '26).
> Use as context when implementing the proof system, propagator, or conflict analysis.

## 1. Core Idea

A **hypercube linear constraint** is a unification of a clause and an integer linear inequality:

```
H → Σ wᵢ·xᵢ ≤ r
```

where `H` is a *hypercube* (a consistent conjunction of atomic constraints).

- If `wᵢ = 0` for all `i` and `r ≤ -1` → degenerates to a **clause** (`H → ⊥`).
- If `D ⊨ H` (hypercube entailed by the current domain) → behaves as the plain linear inequality `Σ wᵢxᵢ ≤ r`.
- Generalises **indicator constraints** (single 0-1 bound in `H`).
- Variables may appear in *both* `H` and the linear part — no separation required.

The proof system, **hypercube linear resolution (HLR)**, is **sound and complete** for integer linear reasoning. It can be viewed as an extended cutting-planes system where extended variables are bound constraints.

---

## 2. Preliminaries / Data Model

### Hypercube `H`
- A **consistent** conjunction of atomic constraints (e.g. no `⟨x ≥ l⟩ ∧ ⟨x ≤ u⟩` with `l > u`).
- May implicitly include the variable's full domain bounds without changing meaning.
- `D_H` denotes the domain induced by `H`.

### Linear constraint `Σ wᵢxᵢ ≤ r` slack
```
slack(D, R) = r − Σ lb(D, wᵢxᵢ)
lb(D, wᵢxᵢ) = wᵢ · lb(D, xᵢ) if wᵢ ≥ 0
= wᵢ · ub(D, xᵢ) if wᵢ < 0
```

---

## 3. Hypercube Linear Slack

**Key definition** (Definition 2):
```
hlslack(D, L) = r − Σ max( lb(D, wᵢxᵢ), lb(D_H, wᵢxᵢ) )
```

Takes the *tighter* of the current domain bound or the hypercube bound for each variable contribution. Rationale: once `H` becomes entailed, `D_H`'s bounds will hold, so we can already use them when reasoning about the linear component.

- If `D ⊨ H` then `hlslack(D, H → R) = slack(D, R)`.
- Conflict detection: `L` is conflicting w.r.t. `D` iff `D ⊨ H` AND `slack(D, R) < 0`.

---

## 4. Hypercube Linear Propagator

For `L ≡ H → R` under domain `D`, three propagation rules:

### Rule 1 — Hypercube fully entailed
If `D ⊨ H`: propagate `R` as a standard linear constraint.

### Rule 2 — Negative slack, one hypercube bound unassigned
If `hlslack(D, L) < 0` and exactly one bound `c ∈ H` is unsatisfied, propagate `¬c`:
- `c = ⟨x ≤ d⟩` → propagate `x ≥ d + 1`
- `c = ⟨x ≥ d⟩` → propagate `x ≤ d − 1`

### Rule 3 — Non-negative slack, one hypercube bound unassigned
If `hlslack(D, L) ≥ 0` and exactly one `c ∈ H` over variable `x` (with linear coefficient `w`) is unassigned, you can still propagate a *weaker* bound:
- `c ≡ ⟨x ≥ d⟩`, `w > 0` → `x ≤ ⌊(hlslack(D, L) + w·d) / w⌋`
- `c ≡ ⟨x ≤ d⟩`, `w < 0` → `x ≥ ⌈(hlslack(D, L) + w·d) / w⌉`

**Reason**: in the case `c` is true, `R` enforces a tighter bound; in the case `¬c`, the bound is implied directly. The weaker bound is true in either case.

### Explanations
Every propagated bound `c` carries an explanation `B` — the conjunction of bounds that caused it. Same concept as in LCG solvers.

---

## 5. Inference Rules of HLR

Four rules: `Weaken`, `Divide`, `ResF` (Fourier), `ResH` (propositional).

### 5.1 Weakening (Definition 3)
Move part of a coefficient from the linear term into the hypercube.

```
H → Σ wᵢxᵢ ≤ r
───────────────────────────────────────────── (lower bound)
H ∧ ⟨xⱼ ≥ d⟩ → Σᵢ≠ⱼ wᵢxᵢ + (wⱼ−1)xⱼ ≤ r − d

H → Σ wᵢxᵢ ≤ r
───────────────────────────────────────────── (upper bound)
H ∧ ⟨xⱼ ≤ d⟩ → Σᵢ≠ⱼ wᵢxᵢ + (wⱼ+1)xⱼ ≤ r + d
```

**Full elimination of `xⱼ`** = apply weakening `|wⱼ|` times, ending at:
- `wⱼ > 0`: `H ∧ ⟨xⱼ ≥ d⟩ → Σᵢ≠ⱼ wᵢxᵢ ≤ r − wⱼd`
- `wⱼ < 0`: `H ∧ ⟨xⱼ ≤ d⟩ → Σᵢ≠ⱼ wᵢxᵢ ≤ r + wⱼd`

**Slack-preserving property (Proposition 3)**: weakening on the *current* domain bound `⟨x ≥ lb(D,x)⟩` (or `⟨x ≤ ub(D,x)⟩`) does **not change `hlslack(D, L)`**. This is critical for keeping the conflict alive during analysis.

### 5.2 Division
```
H → Σ wᵢxᵢ ≤ r (d > 0)
────────────────────────────────
H → Σ ⌊wᵢ/d⌋ xᵢ ≤ ⌊r/d⌋
```
Sound (standard rounded division on integer inequalities). Not required for completeness but prevents coefficient overflow.

### 5.3 Fourier Resolution `ResF`
For `L₁ ≡ H₁ → R₁`, `L₂ ≡ H₂ → R₂` where `xⱼ` has positive weight `a` in `R₁` and negative weight `b` in `R₂` (so `a·b < 0`), and `H₁ ∧ H₂` is consistent:

```
H₁ → R₁ H₂ → R₂
──────────────────────────────
H₁ ∧ H₂ → FR(R₁, R₂, xⱼ)
```

where `FR(R₁, R₂, xⱼ) = Σᵢ≠ⱼ (−w'ⱼ·wᵢ + wⱼ·w'ᵢ) xᵢ ≤ −w'ⱼ·r + wⱼ·r'`, eliminating `xⱼ`.

### 5.4 Propositional-style Resolution `ResH`
For two hypercube linears with bounds `c₁ ≡ ⟨x ≥ d₁⟩ ∈ H₁` and `c₂ ≡ ⟨x ≤ d₂⟩ ∈ H₂` such that `d₁ ≤ d₂ + 1` (covers all values of `x`), and `H'₁ ∧ H'₂` consistent (`Hₖ ≡ H'ₖ ∧ cₖ`):

The rule is parameterised as `ResH_F`. The default instantiation requires `L₂` to be reduced to a clause first (linear part `⊥`):
```
H'₁ ∧ c₁ → R₁ H'₂ ∧ c₂ → ⊥
─────────────────────────────────
H'₁ ∧ H'₂ → R₁
```
Soundness relies on: at least one of `c₁`, `c₂` is true for every value of `x`; weakening `L₂` to a clause is always possible.


16 changes: 16 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: 2
updates:
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "weekly"

- package-ecosystem: "uv"
directory: "pumpkin-solver-py/"
schedule:
interval: "weekly"

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
32 changes: 16 additions & 16 deletions .github/workflows/ci-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ jobs:
- runner: ubuntu-22.04
target: armv7
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: 3.x
- name: Build wheels
Expand All @@ -52,7 +52,7 @@ jobs:
manylinux: auto
docker-options: -e NO_CHECKERS
- name: Upload wheels
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: wheels-linux-${{ matrix.platform.target }}
path: pumpkin-solver-py/dist
Expand All @@ -71,8 +71,8 @@ jobs:
- runner: ubuntu-22.04
target: armv7
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: 3.x
- name: Build wheels
Expand All @@ -84,7 +84,7 @@ jobs:
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
manylinux: musllinux_1_2
- name: Upload wheels
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: wheels-musllinux-${{ matrix.platform.target }}
path: pumpkin-solver-py/dist
Expand All @@ -99,8 +99,8 @@ jobs:
- runner: windows-latest
target: x86
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: 3.x
architecture: ${{ matrix.platform.target }}
Expand All @@ -112,7 +112,7 @@ jobs:
args: --release --out dist --find-interpreter
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
- name: Upload wheels
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: wheels-windows-${{ matrix.platform.target }}
path: pumpkin-solver-py/dist
Expand All @@ -127,8 +127,8 @@ jobs:
- runner: macos-latest
target: aarch64
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: 3.x
- name: Build wheels
Expand All @@ -139,23 +139,23 @@ jobs:
args: --release --out dist --find-interpreter
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
- name: Upload wheels
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: wheels-macos-${{ matrix.platform.target }}
path: pumpkin-solver-py/dist

sdist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
working-directory: pumpkin-solver-py/
command: sdist
args: --out dist
- name: Upload sdist
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: wheels-sdist
path: pumpkin-solver-py/dist
Expand All @@ -174,9 +174,9 @@ jobs:
# Used to generate artifact attestation
attestations: write
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v2
uses: actions/attest-build-provenance@v4
with:
subject-path: 'wheels-*/*'
- name: Publish to PyPI
Expand Down
22 changes: 11 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ jobs:
name: Test Suite
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
- uses: actions/checkout@v6
- uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
Expand All @@ -26,13 +26,13 @@ jobs:
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- uses: dtolnay/rust-toolchain@stable
- run: cargo test --release --no-fail-fast --features pumpkin-solver/check-propagations
- run: cargo test --release --no-fail-fast --features pumpkin-solver/check-propagations --features pumpkin-core/check-deductions

wasm-test:
name: Test Suite for pumpkin-core in WebAssembly
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown
Expand All @@ -48,8 +48,8 @@ jobs:
runner: [ ubuntu-latest, macos-latest, windows-latest ]
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: 3.x
- name: Install pytest
Expand All @@ -65,8 +65,8 @@ jobs:
name: Documentation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
- uses: actions/checkout@v6
- uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
Expand All @@ -82,8 +82,8 @@ jobs:
name: Code Style and Lints
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
- uses: actions/checkout@v6
- uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
Expand All @@ -102,5 +102,5 @@ jobs:
name: Dependency Licensing
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: EmbarkStudios/cargo-deny-action@v2
2 changes: 1 addition & 1 deletion .github/workflows/reset_reviewed_status.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
pull-requests: write
steps:
- name: Dismiss old reviews
uses: actions/github-script@v7
uses: actions/github-script@v9
with:
script: |
const reviews = await github.paginate(github.rest.pulls.listReviews, {
Expand Down
Loading