Skip to content

Add staff-scheduling QUBO and tighten direct-reduction bounds - #1217

Merged
GiggleLiu merged 25 commits into
issue-1213-direct-qubo-phase3from
issue-1213-direct-qubo-phase4
Oct 29, 2026
Merged

GiggleLiu merged 25 commits into
issue-1213-direct-qubo-phase3from
issue-1213-direct-qubo-phase4

Conversation

@isPANN

@isPANN isPANN commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Adds an exact StaffScheduling → DecisionQUBO<i64> reduction and reduces the theoretical overhead of the direct QUBO rules inherited from the stack. Linear arrangement and three-partition use smaller encodings; four other rules receive proved tighter coupling bounds. The direct shortest-common-supersequence rule is withdrawn because it failed the small-instance runtime criterion. Its existing ILP route remains.

The resulting stack retains 24 direct rules, with all 2,400 independent corpus cases passing and zero timeouts or mismatches.

References #1213; keep the roadmap open. This remains a draft stacked on #1216 (issue-1213-direct-qubo-phase3), which is stacked on #1215/#1214. The 24-rule validation includes inherited rules; this PR does not introduce 24 new edges.

Constructions and theoretical bounds

  • Staff scheduling: encode integer schedule counts, unused worker budget, and positive coverage surpluses with bounded binary weights. Squared budget/coverage residuals vanish exactly on feasible staffing assignments. Extraction preserves worker multiplicities and completed YES/NO values. Signed requirements, duplicate schedules, empty dimensions, and checked source/target arithmetic retain their existing semantics.
  • Optimal linear arrangement: replace the vertex-position assignment matrix with tournament comparison bits. An edge's rank distance is one plus the number of vertices between its endpoints, expressed through comparison XORs. Triangle penalties enforce a total order; predecessor counts recover the source arrangement. Variables decrease from n² to exactly n(n−1)/2; couplings are bounded by n(n−1)(n−2)/2, cubic even for dense graphs.
  • Three-partition: replace labeled group assignments with bounded multiplicities of eligible unordered value triples. Squared count equations enforce exact use of every value; extraction assigns the selected copies to original item indices. Variables decrease from n²/3 to at most n(n−1)/6, and couplings are bounded by n(n−1)(n−2)/8. Coefficients depend on item counts rather than squared size magnitudes.

The remaining bound improvements count the actual possible interactions without changing the constructions:

Rule Proved upper bound on off-diagonal couplings
CyclicOrdering n(n−1)(n−2)/2
ShortestCommonSuperstring n(n−1)(2n−1)
StackerCrane 2n²(n−1)
StaffScheduling binom(63(s+1), 2) + p(3969s+1953)

Here n is respectively the element, string, or required-arc count; s and p are the schedule and period counts. Staffing retains the variable bound 63(s+p+1); its coupling bound is linear in periods for fixed schedule count. The manual includes the constructions, bidirectional correctness arguments, extraction contracts, and support-count proofs. Graph metadata and parameter regressions use the same formulas.

Verification

  • Corpus: 100 cases per retained source variant (90 random, 10 special), all 24 rules, from pred-validation-corpus at b9ae9e39f6e51237760f26f8aec205d485490ab0. All 2,400 actual emitted targets completed correctly. The corpus tracked tree remains unchanged.
  • Fixed raw solver: OR-Tools 9.15.6755 CP-SAT, 30-second cap, one solver worker, linearization_level=2, seed 1; two concurrent cases. Exact Boolean-product linearization uses only emitted target coefficients and decision bounds, with no source hints or oracle objective cutoffs. Optimization requires OPTIMAL; decision NO requires INFEASIBLE. Every result passes actual CLI aggregate recovery, witness extraction where applicable, and source evaluation against independent corpus truth.
  • Final source verification: after a behavior-preserving count-conversion cleanup, all 2,400 targets were regenerated and required to match the solved polynomials, dimensions, and decision bounds exactly. All recovery/evaluation checks were repeated. The changed three-partition constructor received 100 fresh raw solves; the other 2,300 identical targets reused their completed exact solves. These are 2,400 unique cases, not 2,500.
  • Replacement performance: linear arrangement's slowest corpus solve was 3.342 s, with at most 28 bits. Three-partition's slowest was 0.000356 s, with at most four bits. Both passed all 100 cases.
  • Mathematics: independent constructor/adversary checks cover all 1,100 simple graphs through five vertices and 9,691 valid three-partition families, with exhaustive target assignments and additional property tests. All 200 production targets for the two replacement rules match the independently checked polynomials exactly. These checks supplement the general proofs.
  • Repository gates: make check passes 7,133 tests/doctests, formatting, and clippy. Line coverage is 98.15% for linear arrangement and 95.20% for three-partition. Paper and website builds pass. Browser verification confirms the updated bounds, 368 graph edges, absent SCS direct-QUBO edge, retained SCS-to-ILP route, and no browser errors. /simplify found no further worthwhile cleanup.

Verified source commit: 3d3b020c0d93f5e27ecce98eaf148321a7eced9d. Its diff has SHA-256 672fc2886a48792c0879c11771dc35b591593be6605e156df3851a2991a0a446 relative to 0dd744809f524cd5669ea04f4f699cc428bbf53c, exactly matching the tested working snapshot; the final tested CLI has SHA-256 31a19445bf633bd7c9ec60a6c7bb5bb0596f935d22c7b42a0032a4aa28d014b5.

Limits and deferred work

ShortestCommonSupersequence's original encoding and two mathematically checked replacements failed the small-case runtime criterion, so the direct rule, registration, example, and theorem are removed. This is a practicality failure, not a demonstrated mathematical counterexample. Its removal is explicit in the 24-rule total; no timeout is accepted as a passing case.

The other Phase 4 candidates remain deferred: JobShopScheduling, OpenShopScheduling, FlowShopScheduling, PreemptiveScheduling, SequencingWithDeadlinesAndSetUpTimes, SequencingToMinimizeMaximumCumulativeCost, ProductionPlanning, RegisterSufficiency, and RootedTreeStorageAssignment. Earlier deferrals and unrelated verification gaps remain outside scope.

Larger-instance performance and hardware embedding remain unverified. Finite checks are not proof-assistant certification or a source-solver speedup claim. Remote CI/Codecov is not claimed: the workflows target PR bases main/develop, while this PR targets the Phase 3 branch.

The earlier staffing-specific certificate, measurements, and review report document the original staffing work. Their earlier whole-stack scope and bounds are superseded by this description.

@isPANN

isPANN commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Verification certificate

VERIFIED — StaffScheduling → DecisionQUBO. Mathematical construction was independently checked before production registration; production output is subsequently compared exactly with that prototype. Constructor and proof-only fresh-context adversary passed, with 124 shared target polynomials and 0 disagreements.

Type resolution

src/models/misc/staff_scheduling.rs:226: StaffScheduling::Solution=Vec, Value=Or. src/models/algebraic/qubo.rs:293: QUBO::Solution=Vec, Value=Min<W::Sum>; i64 weight sum=i64. src/models/decision.rs:286: Decision

::Solution=P::Solution, Value=Or. Thus source Or→target Or, with identity completed decision recovery. The source is not Boolean schedule selection and is not an optimization problem.

Independent evidence

  • Constructor seed 4412026: all constant-weight matrices for s,p≤2, W∈{0,1,2}, requirements∈{−1,0,1,2,3}, exhaustive source and target witnesses; plus 300source-domain samples per (s,p)∈{0,…,5}², W≤3, independently enumerated counts and random target residual identities. This is a stated finite-domain exhaustive check, not a claim to exhaust unbounded integer inputs.
  • Constructor sections cover bounded representation and symbolic registry bounds; forward/backward existence; every feasible target extraction in exhaustive domains; size/normalized support; target structure; worked YES and NO. Count audit follows. Four supplementary symbolic checks explicitly verify both registered bounds; these strengthen evidence without changing the construction.
  • Fresh adversary received ONLY the proof and wrote its own constructor, extractor and checkers; no repository code, prototype or corpus. Seed20261022, all (s,p)≤5, bounded families exhausted when ≤300 else 300 distinct seeded samples; two Hypothesis strategies,200 examples each, deadline=None and deterministic settings (script below). Includes signed/zero/empty/multiplicity/early-NO and coefficient-prefix boundaries, and120 independent mathematical X3C compositions. These mathematical compositions are separate from actual CLI-chain validation.
  • Symbolic/general proof covers arbitrary accepted dimensions and signed i64 inputs; finite experiments do not replace it. Nonrepresentable source count products, coefficients, constants and separate-sign coefficient sums return typed overflow. Conservative arithmetic guards can reject some representable special cases. Allocation exhaustion is not simulated.

Constructor audit:

{
  "seed": 4412026,
  "counts": {
    "symbolic": 133,
    "size": 23748,
    "structure": 167202,
    "forward_backward": 45085,
    "extraction": 54675,
    "yes_example": 2,
    "no_example": 2
  },
  "total": 290847
}

Adversary audit:

{
  "verdict": "PASS",
  "counts": {
    "exhaustive_bounded_block_ranges": 257,
    "multiplicity_and_nonunique_encoding": 2,
    "worked_example_assignments": 2304,
    "source_target_decisions": 7086,
    "forward_backward_witnesses": 2088,
    "size_and_coefficient_bounds": 3605,
    "assignment_identity_and_extraction": 51400,
    "malformed_target_rejections": 16,
    "unsatisfying_extraction_rejections": 2303,
    "dedicated_degenerate_cases": 8,
    "early_no": 3481,
    "malformed_source_rejections": 6,
    "sympy_polynomial_identities": 3,
    "independent_diagonal_bounds": 10416,
    "i128_product_bound_checks": 2,
    "large_block_weight_bounds": 9,
    "random_evaluation_prefix_orders": 1000,
    "typed_arithmetic_rejections": 5,
    "negative_requirement_boundaries": 4,
    "composition_feasible_count_witnesses": 166,
    "exact_cover_staff_qubo_composition": 120,
    "hypothesis_general": 200,
    "hypothesis_planted": 200
  },
  "arithmetic_boundaries": {
    "safe_budget_for_separate_sum_guard": 2322860046,
    "next_rejected_budget": 2322860047,
    "safe_negative_sum": 9223372032470907730,
    "unsafe_negative_sum": 9223372041411595117,
    "sqrt_i64_max": 3037000499
  },
  "shared_instances": 124,
  "seed": 20261022,
  "limits": [
    "Finite base family has W in {0,1,2}, requirements in {-1,0,1,2}, ordered duplicate-permitting uniform-k patterns.",
    "All (s,p) in 0..5 covered; families of at most 300 instances exhausted, others have 300 distinct seeded samples.",
    "Hypothesis adds W through 6; arbitrary i64 coefficient bounds are algebraic, boundary checks finite.",
    "No repository code, prototype, or shared corpus was read; this validates the proof and independent reference only.",
    "Exact-cover composition is independently modeled mathematically; no repository graph dispatch was exercised.",
    "No real allocation exhaustion, usize overflow, Rust serialization, or implementation error types were exercised."
  ]
}

Claims and evidence categories

Claim Evidence
Interval coverage/multiplicities Bounded-block induction in proof; constructor symbolic checks; adversary exhaustive blocks through 256
Forward and backward feasibility Separate proof arguments; constructor source enumeration/target exhaustive domains; adversary7,086 decisions
Extraction and malformed witnesses Proof weighted sums; exhaustive feasible-target checks; Rust behavior tests reject malformed lengths and unsatisfying assignments
Penalty separation and constant Sum of integer squares; worked YES −21 and NO minimum−15 versus bound−16; independent polynomial identities
Both registered parameter bounds Block-count proof and table; sympy gap factorizations; measured size/support; repository canonical formula test
Source and target arithmetic sW guard before expansion, signed requirements skipped; checked builder intermediates and separate final-sign sums; adversary large-weight/prefix checks; Rust typed-overflow cases
Completed Or and incoming chain Identity proof; actual production CLI aggregate/witness extraction for all 219 cases, including independent NO
Production translation 219exact coefficient/constant/dimension comparisons against independently verified prototype; independent raw solves of actual emitted targets

Raw solves: 219/219 proven decisions under 15 s/one-worker/linearization2, no timeout. Oracle/constructor checks: the checks above construct some target witnesses from independent source witnesses and inspect residual identities; they are not counted as raw solves. Coefficient checks: 124 constructor–adversary structural agreements, then219 production–prototype agreements; these are also not raw solves. Per-case raw table and baseline audit are posted separately. Corpus integrity:32,372 files before/after,0 changed/added/removed.

Typst proof
#set page(margin: 20mm)
#set text(size: 10pt)
= Bounded staffing counts to Decision QUBO

*Theorem.* For the exact StaffScheduling constructor described below, the following finite quadratic Boolean construction preserves feasibility, recovers every satisfying target witness, and maps completed decision values identically. Integer arithmetic that cannot be represented by the implementation must return a typed reduction overflow, before returning a target. This is not a positive-requirement-only or Boolean-worker variant.

== Source and endpoint contract

There are $s$ schedule patterns $A_j in {0,1}^p$, each with exactly $k$ active periods. Duplicate patterns are permitted. Each requirement $r_u$ is any signed i64 integer. The budget $W$ is a nonnegative i64 integer, with $W+1$ representable as usize. Empty patterns, empty periods, zero budget, empty schedule collection, and arbitrary $k$ when there are no patterns are accepted. The witness is the length-$s$ vector of integer multiplicities $c_j in [0,W]$. The evaluator first checks the witness length, then each coordinate bound, then checked i64 total worker count, and only if the budget is satisfied, checked period coverage. It accepts exactly when $sum_j c_j <= W$ and $sum_j A_(j,u)c_j >= r_u$ for every period. Negative requirements impose no condition because coverage is nonnegative. Type resolution is StaffScheduling.Value = Or, Decision(QUBO(i64)).Value = Or, and QUBO(i64).Value = Min(i64).

== Construction

1. For each integer $B >= 0$, define $b(B)=ceil(log_2(B+1))$ with $b(0)=0$. If $b>0$, take weights $1,2,...,2^(b-2),B-(2^(b-1)-1)$. Their sum is exactly $B$, each is positive, and their subset sums cover every integer from zero through $B$. The last weight is at most $2^(b-1)$; the preceding weights cover $[0,2^(b-1)-1]$, so the intervals with and without the last weight overlap or touch. Encoding is generally nonunique; extraction returns the weighted sum, never an index.
2. If any $r_u>W$, return a zero-variable target polynomial $Q=0$ with bound $-1$. Every period has coverage at most total workers, so the source is NO. Extraction cannot succeed.
3. Otherwise allocate a bounded binary block for each $c_j in [0,W]$, followed by a budget slack block $z in [0,W]$. For each positive requirement allocate surplus $h_u in [0,W-r_u]$. Nonpositive requirement rows are omitted.
4. Define the nonnegative integer polynomial
$ F = (sum_j c_j + z - W)^2 + sum_(u:r_u>0) (sum_j A_(j,u)c_j - h_u-r_u)^2. $
Every encoded variable here is a linear weighted sum of its own Boolean block. Expand using $x_i^2=x_i$ and combine coefficients for unordered pairs. The omitted constant is $C=W^2+sum_(u:r_u>0)r_u^2$. The actual QUBO has energy $Q=F-C$ and the Decision bound is $-C$. Penalty weight is exactly one; integer residual squares separate YES energy zero from every violation by at least one. There is no optimization surrogate or supplied optimum bound.

== Correctness

*Forward.* A source witness obeys total workers at most $W$. Choose $z=W-sum_j c_j$, within $[0,W]$. Each positive requirement has coverage between $r_u$ and $W$, so choose $h_u="coverage"_u-r_u$ within its block's range. Every block has a representing bitstring by the interval argument. Every residual vanishes, hence $F=0$ and $Q=-C$ satisfies the target threshold.

*Backward.* Every Boolean block decodes within its prescribed finite range. If the target accepts, $Q<=-C$, so $F<=0$. All residuals are nonnegative integer squares; each must vanish. The budget equality implies $sum c_j=W-z<=W$. The positive coverage equalities imply $"coverage"_u=r_u+h_u>=r_u$. All omitted nonpositive requirements hold because coverage is nonnegative. Thus the decoded count vector satisfies the original source. The case $r_u>W$ cannot yield a target witness.

*Witness extraction.* Validate the target configuration length and Boolean domain, evaluate its actual emitted polynomial once, and reject unless its Decision value is true. Decode just the $s$ count blocks by checked integer weighted sums. Budget and coverage then bound every source addition by $W$, so evaluation of the recovered witness cannot overflow. Reject malformed target lengths or unsatisfying target assignments; never repair a bitstring or reduce a count modulo a bound.

*Completed result.* Both endpoints are decision problems; completed Or(false) and Or(true) map identically. No source witness is extracted from a completed NO result. Along ExactCoverBy3Sets to StaffScheduling, the existing count-to-selected-set map and identity aggregate map compose. Its budget $q$ and $3q$ unit requirements force exactly $q$ workers with no overlaps, hence all selected multiplicities are one. This chain must be solved through its actual QUBO, separately from the already present direct exact-cover QUBO.

== Arithmetic and size

All inputs remain in their constructor domains. Before returning a target, conservatively check $s W<=2^63-1$ so the source's total count fold is safe even on budget-violating coordinate-valid witnesses. Successful witnesses have the stronger bound $W$ for both total and coverage. Do not take absolute values of negative requirements: they are skipped, including i64::MIN. Every positive $r_u<=W$ and every $W-r_u$ is safe. Zero-variable NO branches may return without arithmetic for unavailable source configurations because they do not certify any witness; the implementation should document the order of guards.

With $p_+$ positive requirements, the precise non-early-return variable count is
$ N=(s+1)b(W)+sum_(u:r_u>0)b(W-r_u). $
Thus $N<=(s+p+1)b(W)<=63(s+p+1)$ for i64 budgets, and $N<=(s+p+1)(W+1)$ is a valid registry upper bound using existing parameters only. Couplings after coefficient cancellation are at most $N(N-1)/2$, or the simpler $N^2$. These are upper bounds, not measured support. Construction uses the sum of squared row widths, at most $(p+1)N^2$ coefficient operations plus $O(s+p)$ row bookkeeping, and $O(p+N+E)$ construction storage. Its bit count is logarithmic in the worker budget; the loose polynomial registry bound must not be mistaken for unary encoding.

For $W>0$, each variable weight is at most $W$. A single residual contributes at most $3W^2$ in absolute diagonal coefficient and $2W^2$ per off-diagonal. A variable appears in at most $p+1$ residuals, so maximum coefficient magnitude is at most $3(p+1)W^2$; the constant is at most $(p+1)W^2$. Ordered-map coefficient updates add a logarithmic lookup factor. The magnitude bit length is at most $ceil(log_2(3(p+1)W^2+1))$. For $W=0$, every block is empty and every permitted residual is zero. Check intermediate coefficient accumulation, the omitted constant, its negative, and separate positive/negative sums of the final polynomial against i64. This last check certifies all target evaluation prefixes, even when combined coefficients have cancellation. i128 products of two i64 weights and twice their product are representable; constants and accumulations narrow only with checked conversions. Checked usize counts and a byte-size guard precede allocation of the budget and coverage rows; source-sized bookkeeping uses the already validated period list. Arithmetic rejection is typed, never wrapping, saturation or constructor-domain modification.

#table(columns: 2,
 [Target parameter], [Certified relation],
 [num_vars], [$N=(s+1)b(W)+sum_(u:r_u>0)b(W-r_u)<=63(s+p+1)$; early NO gives zero],
 [num_quadratic_terms], [$E<=N(N-1)/2<=(63(s+p+1))^2$],
)

== Worked YES

Take three schedules on three periods: $A_0=(1,1,0)$, $A_1=(0,1,1)$, $A_2=(1,0,1)$, $k=2$, $W=3$, requirements $(2,2,2)$. Every count and budget block has weights $(1,2)$; each surplus has weight $(1)$. Thus $N=11$ and $C=9+4+4+4=21$. Counts $(1,1,1)$, slack zero, and all surpluses zero give residuals $(0,0,0,0)$, hence $F=0$ and $Q=-21$. The actual target threshold is $-21$ and extraction returns the three integer counts.

== Worked NO

Use the same three patterns and requirements, but $W=2$. Each count/budget block has weights $(1,1)$, and all surplus bounds are zero, hence $N=8$, $C=4+4+4+4=16$. If every coverage residual vanished, total coverage would be six. Each worker contributes two units, so at least three workers would be required, contradicting the budget two. No target reaches $Q<=-16$. Counts $(1,1,1)$ with budget slack zero yield $F=1$ and $Q=-15$. Nonnegative integer residuals prove this is the minimum and still NO. Multiplicity is essential: with a single all-active pattern, demand two and budget two, count two is required; a Boolean selection cannot represent that YES instance.
Constructor script
import json,random,itertools,collections
from pathlib import Path
import sympy as S
from prototypes import staff
OUT=Path(__file__).parent
counts=collections.Counter()
def check(section,condition):
    assert condition,section
    counts[section]+=1

def valid(d,c):
    return len(c)==len(d['schedules']) and all(0<=v<=d['num_workers'] for v in c) and sum(c)<=d['num_workers'] and all(sum(v for v,row in zip(c,d['schedules']) if row[u])>=r for u,r in enumerate(d['requirements']))
def represent(q,d,c):
    values=list(c)+[d['num_workers']-sum(c)]+[sum(v for v,row in zip(c,d['schedules']) if row[u])-r for u,r in enumerate(d['requirements']) if r>0]
    bounds=[d['num_workers']]*(len(c)+1)+[d['num_workers']-r for r in d['requirements'] if r>0]
    bits=[]
    for v,B in zip(values,bounds):
        weights=[];power=1
        while B:w=min(power,B);weights.append(w);B-=w;power*=2
        encoded=[0]*len(weights)
        for i in reversed(range(len(weights))):
            if v>=weights[i]:encoded[i]=1;v-=weights[i]
        assert v==0
        bits+=encoded
    return bits

def inspect(d,exhaust=False):
    q=staff(d);s=len(d['schedules']);p=len(d['requirements']);W=d['num_workers']
    impossible=any(r>W for r in d['requirements'])
    N=0 if impossible else (s+1)*W.bit_length()+sum((W-r).bit_length() for r in d['requirements'] if r>0)
    check('size',q.n==N and q.n<=63*(s+p+1) and q.n<=(s+p+1)*(W+1))
    check('size',sum(i!=j for i,j in q.q)<=q.n*(q.n-1)//2)
    check('structure',all(0<=i<=j<q.n and v!=0 for (i,j),v in q.q.items()))
    check('structure',max(map(abs,q.q.values()),default=0)<=3*(p+1)*W*W)
    possible=False
    for c in itertools.product(range(W+1),repeat=s):
        if valid(d,c):
            possible=True;b=represent(q,d,c);check('forward_backward',len(b)==q.n and q.energy(b)==0)
            check('extraction',[sum(v*b[i] for i,v in row) for row in q.rows]==list(c))
    if impossible:check('forward_backward',not possible and q.c==1)
    if exhaust:
        target_yes=False
        for b in itertools.product(range(2),repeat=q.n):
            energy=q.energy(b)
            check('structure',energy>=0)
            if energy==0:
                c=[sum(v*b[i] for i,v in row) for row in q.rows]
                check('extraction',valid(d,c));target_yes=True
        check('forward_backward',possible==target_yes)
    else:
        for _ in range(12):
            b=[R.randrange(2) for _ in range(q.n)]
            if impossible:check('structure',q.energy(b)==1);continue
            # Check residual identity using independently sliced bounded-weight blocks.
            c=[sum(v*b[i] for i,v in row) for row in q.rows]
            at=s*W.bit_length();res=[]
            for B in [W]+[W-r for r in d['requirements'] if r>0]:
                k=B.bit_length();weights=[1<<i for i in range(max(0,k-1))]+([B-((1<<(k-1))-1)] if k else [])
                res.append(sum(v*x for v,x in zip(weights,b[at:at+k])));at+=k
            expected=(sum(c)+res[0]-W)**2
            v=1
            for u,r in enumerate(d['requirements']):
                if r>0:expected+=(sum(c[j] for j in range(s) if d['schedules'][j][u])-r-res[v])**2;v+=1
            check('structure',q.energy(b)==expected)
            if expected==0:check('extraction',valid(d,c))
    return q

R=random.Random(4412026)
x,y,z,r=S.symbols('x y z r',integer=True)
expanded=S.Poly(S.expand((x*y+z-r)**2),x,z)
check('symbolic',S.expand((x*y+z-r)**2)==x*x*y*y+z*z+r*r+2*x*y*z-2*r*x*y-2*r*z)
# Symbolic certificates for both registered upper bounds, with nonnegative gaps.
s,p,b,delta,N=S.symbols('s p b delta N',nonnegative=True,integer=True)
exact_N=(s+1)*b+p*b-delta
check('symbolic',S.expand((s+p+1)*b-exact_N)==delta)
check('symbolic',S.expand(63*(s+p+1)-exact_N)==S.expand((63-b)*(s+p+1)+delta))
check('symbolic',S.simplify(N*N-N*(N-1)/2)==N*(N+1)/2)
check('symbolic',S.expand((63*(s+p+1))**2-N**2)==S.expand((63*(s+p+1)-N)*(63*(s+p+1)+N)))
for B in range(128):
    k=B.bit_length();w=[1<<i for i in range(max(0,k-1))]+([B-((1<<(k-1))-1)] if k else [])
    check('symbolic',sum(w)==B and {sum(a*b for a,b in zip(w,t)) for t in itertools.product(range(2),repeat=k)}==set(range(B+1)))
# Exhaust every constant-weight schedule matrix, requirement vector and budget in this finite domain.
for p in range(3):
    for s in range(3):
        for k in range(p+1):
            patterns=[list(t) for t in itertools.product([False,True],repeat=p) if sum(t)==k]
            for A in itertools.product(patterns,repeat=s):
                for req in itertools.product([-1,0,1,2,3],repeat=p):
                    for W in range(3):inspect(dict(schedules=list(A),requirements=req,num_workers=W),True)
# 300 source-domain samples per (s,p) through 5, independently enumerate source counts.
for s in range(6):
    for p in range(6):
        for _ in range(300):
            k=R.randrange(p+1);patterns=[list(t) for t in itertools.product([False,True],repeat=p) if sum(t)==k]
            inspect(dict(schedules=[R.choice(patterns) for _ in range(s)],requirements=[R.randrange(-2,5) for _ in range(p)],num_workers=R.randrange(4)))
A=[[1,1,0],[0,1,1],[1,0,1]]
for W,E,section in [(3,0,'yes_example'),(2,1,'no_example')]:
    d=dict(schedules=A,requirements=[2,2,2],num_workers=W);q=staff(d)
    check(section,q.n==(11 if W==3 else 8) and q.c==(21 if W==3 else 16))
    check(section,min(q.energy(b) for b in itertools.product(range(2),repeat=q.n))==E)
# Explicit signed-boundary requirements are skipped without absolute-value arithmetic.
q=staff(dict(schedules=[[True]],requirements=[-(1<<63)],num_workers=2))
check('structure',q.n==4 and q.c==4)
(OUT/'constructor-counts.json').write_text(json.dumps(dict(seed=4412026,counts=dict(counts),total=sum(counts.values())),indent=2))
print(dict(counts),sum(counts.values()),flush=True)
Independent adversary script
"""Independent proof-only checker. No repository or shared-corpus imports."""
import itertools as it
import json
import math
import random
from collections import Counter
from dataclasses import dataclass
from pathlib import Path

import sympy as sp
from hypothesis import given, settings, strategies as st
from ortools.sat.python import cp_model

OUT = Path(__file__).resolve().parent
MAX = 2**63 - 1
MIN = -2**63
COUNTS = Counter()


class Overflow(Exception):
    pass


def checked(value):
    if not MIN <= value <= MAX:
        raise Overflow(value)
    return value


@dataclass(frozen=True)
class Instance:
    schedules: tuple
    requirements: tuple
    budget: int
    k: int

    @property
    def s(self):
        return len(self.schedules)

    @property
    def p(self):
        return len(self.requirements)


def source(inst, counts):
    if len(counts) != inst.s or any(type(c) is not int or c < 0 or c > inst.budget for c in counts):
        return False
    total = 0
    for c in counts:
        total = checked(total + c)
    if total > inst.budget:
        return False
    for u, r in enumerate(inst.requirements):
        coverage = 0
        for row, c in zip(inst.schedules, counts):
            if row[u]:
                coverage = checked(coverage + c)
        if coverage < r:
            return False
    return True


def source_solve(inst):
    # This oracle knows no QUBO variables, row penalties, or slack construction.
    for counts in it.product(range(inst.budget + 1), repeat=inst.s):
        if source(inst, counts):
            return counts
    return None


def weights(bound):
    n = bound.bit_length()
    return tuple(1 << i for i in range(n - 1)) + ((bound - ((1 << (n - 1)) - 1),) if n else ())


@dataclass
class Target:
    n: int
    coefficients: dict
    threshold: int
    count_blocks: list
    all_blocks: list
    rows: list
    constant: int
    early: bool = False


def build(inst, arithmetic=False):
    if any(r > inst.budget for r in inst.requirements):
        return Target(0, {}, -1, [], [], [], 0, True)
    if arithmetic:
        checked(inst.s * inst.budget)
    n = 0
    blocks = []

    def block(bound):
        nonlocal n
        entries = tuple((n + j, w) for j, w in enumerate(weights(bound)))
        n += len(entries)
        blocks.append(entries)
        return entries

    counts = [block(inst.budget) for _ in range(inst.s)]
    budget_slack = block(inst.budget)
    rows = [(dict(it.chain.from_iterable(counts + [budget_slack])), -inst.budget)]
    for u, req in enumerate(inst.requirements):
        if req > 0:
            terms = dict(it.chain.from_iterable(counts[j] for j in range(inst.s) if inst.schedules[j][u]))
            terms.update((i, -w) for i, w in block(inst.budget - req))
            rows.append((terms, -req))
    polynomial = Counter()
    constant = 0
    for row, offset in rows:
        constant += offset * offset
        if arithmetic:
            checked(constant)
        for i, a in row.items():
            polynomial[i, i] += a * a + 2 * a * offset
            if arithmetic:
                checked(polynomial[i, i])
        for (i, a), (j, b) in it.combinations(row.items(), 2):
            key = min(i, j), max(i, j)
            polynomial[key] += 2 * a * b
            if arithmetic:
                checked(polynomial[key])
    polynomial = {key: v for key, v in sorted(polynomial.items()) if v}
    if arithmetic:
        checked(-constant)
        checked(sum(v for v in polynomial.values() if v > 0))
        checked(sum(v for v in polynomial.values() if v < 0))
    return Target(n, polynomial, -constant, counts, blocks, rows, constant)


def energy(target, bits, arithmetic=False):
    value = 0
    for (i, j), coeff in target.coefficients.items():
        value += coeff * bits[i] * bits[j]
        if arithmetic:
            checked(value)
    return value


def extract(inst, target, bits):
    if len(bits) != target.n or any(type(x) is not int or x not in (0, 1) for x in bits):
        return None
    if energy(target, bits) > target.threshold:
        return None
    result = tuple(sum(w * bits[i] for i, w in block) for block in target.count_blocks)
    return result


def target_solve(target, minimize=False):
    if not target.n:
        return (0, ()) if 0 <= target.threshold or minimize else None
    model = cp_model.CpModel()
    variables = [model.new_bool_var(f'x{i}') for i in range(target.n)]
    terms = []
    for (i, j), coeff in target.coefficients.items():
        if i == j:
            variable = variables[i]
        else:
            variable = model.new_bool_var(f'e{i}_{j}')
            model.add_multiplication_equality(variable, [variables[i], variables[j]])
        terms.append(coeff * variable)
    if minimize:
        model.minimize(sum(terms))
    else:
        model.add(sum(terms) <= target.threshold)
    solver = cp_model.CpSolver()
    solver.parameters.num_search_workers = 1
    solver.parameters.random_seed = 1729
    status = solver.solve(model)
    assert status in (cp_model.OPTIMAL, cp_model.INFEASIBLE), status
    if status == cp_model.INFEASIBLE:
        return None
    bits = tuple(solver.value(x) for x in variables)
    return energy(target, bits), bits


def encode(block, value):
    # Explicit subset search, independent of the proof's interval argument.
    for bits in it.product((0, 1), repeat=len(block)):
        if sum(w * bit for (_, w), bit in zip(block, bits)) == value:
            return bits
    raise AssertionError((block, value))


def forward(inst, target, counts):
    values = list(counts) + [inst.budget - sum(counts)]
    values += [sum(row[u] * c for row, c in zip(inst.schedules, counts)) - req
               for u, req in enumerate(inst.requirements) if req > 0]
    return tuple(it.chain.from_iterable(encode(block, value) for block, value in zip(target.all_blocks, values)))


def check_instance(inst, rng, export=False):
    target = build(inst, arithmetic=True)
    witness = source_solve(inst)
    solution = target_solve(target)
    assert (witness is not None) == (solution is not None), (inst, witness, solution)
    COUNTS['source_target_decisions'] += 1
    if solution is not None:
        recovered = extract(inst, target, solution[1])
        assert recovered is not None and source(inst, recovered)
        bits = forward(inst, target, witness)
        assert energy(target, bits, True) == target.threshold
        assert extract(inst, target, bits) == witness
        COUNTS['forward_backward_witnesses'] += 1
    if not target.early:
        expected_n = (inst.s + 1) * inst.budget.bit_length() + sum((inst.budget - r).bit_length() for r in inst.requirements if r > 0)
        assert target.n == expected_n
        assert target.n <= (inst.s + inst.p + 1) * inst.budget.bit_length()
        assert target.n <= (inst.s + inst.p + 1) * (inst.budget + 1)
        assert sum(i != j for i, j in target.coefficients) <= target.n * (target.n - 1) // 2
        bound = 3 * (inst.p + 1) * inst.budget**2
        assert max(map(abs, target.coefficients.values()), default=0) <= bound
        assert target.constant <= (inst.p + 1) * inst.budget**2
        COUNTS['size_and_coefficient_bounds'] += 1
        assignments = it.product((0, 1), repeat=target.n) if target.n <= 6 else [tuple(rng.randrange(2) for _ in range(target.n)) for _ in range(4)]
        for bits in assignments:
            residual_squares = (sum(bits[i] * a for i, a in row.items()) + offset for row, offset in target.rows)
            f = sum(x * x for x in residual_squares)
            assert energy(target, bits, True) + target.constant == f
            recovered = extract(inst, target, bits)
            assert (recovered is not None) == (f == 0)
            if recovered is not None:
                assert source(inst, recovered)
            COUNTS['assignment_identity_and_extraction'] += 1
    else:
        assert target.n == 0 and extract(inst, target, ()) is None
        COUNTS['early_no'] += 1
    if export:
        return {'schedules': inst.schedules, 'requirements': inst.requirements,
                'budget': inst.budget, 'k': inst.k, 'n': target.n,
                'threshold': target.threshold, 'constant': target.constant,
                'coefficients': [[i, j, v] for (i, j), v in target.coefficients.items()],
                'source_yes': witness is not None, 'source_witness': witness,
                'target_witness': solution[1] if solution else None,
                'early_no': target.early}


def uniform_patterns(p, k):
    return [tuple(int(i in active) for i in range(p)) for active in it.combinations(range(p), k)]


def family_size(s, p):
    choices = sum(math.comb(p, k)**s for k in range(p + 1)) if s else 1
    return 3 * 4**p * choices


def finite_family(s, p):
    for w in range(3):
        for requirements in it.product((-1, 0, 1, 2), repeat=p):
            if not s:
                yield Instance((), requirements, w, p + 2)
            else:
                for k in range(p + 1):
                    for patterns in it.product(uniform_patterns(p, k), repeat=s):
                        yield Instance(patterns, requirements, w, k)


def sampled_family(s, p, rng):
    selected = set()
    while len(selected) < 300:
        k = rng.randrange(p + 1) if s else p + 2
        patterns = tuple(rng.choice(uniform_patterns(p, k)) for _ in range(s)) if s else ()
        inst = Instance(patterns, tuple(rng.choice((-1, 0, 1, 2)) for _ in range(p)), rng.randrange(3), k)
        if inst not in selected:
            selected.add(inst)
            yield inst


def finite_checks():
    rng = random.Random(20261022)
    coverage = []
    shared = []
    for s in range(6):
        for p in range(6):
            size = family_size(s, p)
            exhaustive = size <= 300
            instances = finite_family(s, p) if exhaustive else sampled_family(s, p, rng)
            n = 0
            for inst in instances:
                item = check_instance(inst, rng, export=n < 3)
                if item:
                    shared.append(item)
                n += 1
            assert n == (size if exhaustive else 300)
            coverage.append({'s': s, 'p': p, 'family_size': size, 'mode': 'exhaustive' if exhaustive else 'unique_seeded_samples', 'instances': n})
            print(json.dumps(coverage[-1]), flush=True)
    (OUT / 'finite-coverage.json').write_text(json.dumps(coverage, indent=2) + '\n')
    return shared


@st.composite
def general_instances(draw):
    s = draw(st.integers(0, 5))
    p = draw(st.integers(0, 5))
    w = draw(st.integers(0, 5))
    k = draw(st.integers(0, p))
    patterns = uniform_patterns(p, k)
    rows = tuple(draw(st.lists(st.sampled_from(patterns), min_size=s, max_size=s)))
    req = tuple(draw(st.lists(st.integers(-3, w + 1), min_size=p, max_size=p)))
    return Instance(rows, req, w, k)


@st.composite
def known_feasible_instances(draw):
    s = draw(st.integers(1, 5))
    p = draw(st.integers(1, 5))
    w = draw(st.integers(1, 6))
    k = draw(st.integers(0, p))
    rows = tuple(draw(st.lists(st.sampled_from(uniform_patterns(p, k)), min_size=s, max_size=s)))
    counts = [0] * s
    for _ in range(draw(st.integers(0, w))):
        counts[draw(st.integers(0, s - 1))] += 1
    req = tuple(draw(st.integers(-2, sum(a[u] * c for a, c in zip(rows, counts)))) for u in range(p))
    return Instance(rows, req, w, k), tuple(counts)


@settings(max_examples=200, deadline=None, derandomize=True, database=None)
@given(general_instances())
def test_general_hypothesis(inst):
    check_instance(inst, random.Random(11))
    COUNTS['hypothesis_general'] += 1


@settings(max_examples=200, deadline=None, derandomize=True, database=None)
@given(known_feasible_instances())
def test_planted_hypothesis(pair):
    inst, counts = pair
    assert source(inst, counts)
    target = build(inst, arithmetic=True)
    bits = forward(inst, target, counts)
    assert energy(target, bits) == target.threshold
    assert extract(inst, target, bits) == counts
    assert target_solve(target) is not None
    COUNTS['hypothesis_planted'] += 1


def block_checks():
    for bound in range(257):
        w = weights(bound)
        represented = {sum(a * b for a, b in zip(w, bits)) for bits in it.product((0, 1), repeat=len(w))}
        assert represented == set(range(bound + 1))
        assert sum(w) == bound and len(w) == bound.bit_length()
        assert all(0 < x <= bound for x in w)
        COUNTS['exhaustive_bounded_block_ranges'] += 1
    # The extraction must retain multiplicities and nonunique subset encodings.
    inst = Instance(((1,),), (2,), 2, 1)
    target = build(inst)
    accepted = []
    for bits in it.product((0, 1), repeat=target.n):
        result = extract(inst, target, bits)
        if result is not None:
            accepted.append(result)
    assert accepted == [(2,)]
    inst = Instance(((1,), (1,)), (1,), 2, 1)
    target = build(inst)
    accepted_counts = Counter()
    for bits in it.product((0, 1), repeat=target.n):
        result = extract(inst, target, bits)
        if result is not None:
            assert source(inst, result)
            accepted_counts[result] += 1
    assert accepted_counts[(1, 0)] > 1 and accepted_counts[(2, 0)] > 0
    COUNTS['multiplicity_and_nonunique_encoding'] += 2


def examples_and_malformed():
    exported = []
    patterns = ((1, 1, 0), (0, 1, 1), (1, 0, 1))
    for w, expected_n, expected_c, expected_min in [(3, 11, 21, -21), (2, 8, 16, -15)]:
        inst = Instance(patterns, (2, 2, 2), w, 2)
        target = build(inst)
        assert (target.n, target.constant) == (expected_n, expected_c)
        solution = target_solve(target, minimize=True)
        all_energies = [energy(target, bits) for bits in it.product((0, 1), repeat=target.n)]
        assert solution[0] == min(all_energies) == expected_min
        COUNTS['worked_example_assignments'] += len(all_energies)
        exported.append(check_instance(inst, random.Random(7), True))
        bad = [(), (0,) * (target.n - 1), (0,) * (target.n + 1)]
        for value in (-1, 2, 0.5, '1', None):
            bad.append((value,) + (0,) * (target.n - 1))
        for bits in bad:
            assert extract(inst, target, bits) is None
            COUNTS['malformed_target_rejections'] += 1
        for bits in it.product((0, 1), repeat=target.n):
            if energy(target, bits) > target.threshold:
                assert extract(inst, target, bits) is None
                COUNTS['unsatisfying_extraction_rejections'] += 1
    for inst in [Instance((), (), 0, 99), Instance((), (MIN, 0), 0, 99),
                 Instance((), (1,), 0, 99), Instance(((), ()), (), 2, 0),
                 Instance(((0, 0),), (0, -5), 0, 0), Instance(((1,),), (2,), 2, 1),
                 Instance(((1,), (1,)), (2,), 2, 1), Instance(((0,),), (1,), 2, 0)]:
        exported.append(check_instance(inst, random.Random(8), True))
        COUNTS['dedicated_degenerate_cases'] += 1
    inst = Instance(((1,), (1,)), (0,), 2, 1)
    for counts in [(), (1,), (0, 0, 0), (-1, 0), (3, 0), (2, 1)]:
        assert source(inst, counts) is False
        COUNTS['malformed_source_rejections'] += 1
    return exported


def symbolic_checks():
    for inst in [Instance(((1, 0), (0, 1), (1, 0)), (1, -2), 3, 1),
                 Instance(((1, 1), (1, 1)), (2, 1), 2, 2), Instance((), (0,), 0, 5)]:
        target = build(inst)
        x = sp.symbols(f'x0:{target.n}')
        independent = sum((sum(a * x[i] for i, a in row.items()) + c)**2 for row, c in target.rows)
        independent = sp.expand(independent).subs({a**2: a for a in x})
        emitted = sum(v * x[i] * x[j] if i != j else v * x[i] for (i, j), v in target.coefficients.items())
        assert sp.expand(independent - emitted - target.constant) == 0
        COUNTS['sympy_polynomial_identities'] += 1
    # Per residual: ordinary bit coefficient a^2-2ra and surplus a^2+2ra.
    # Enumerating the full small triangle checks the sharper derived W^2 bound.
    for w in range(31):
        for r in range(w + 1):
            for a in range(w + 1):
                assert abs(a*a - 2*r*a) <= w*w
                if a <= w-r:
                    assert a*a + 2*r*a <= w*w
                COUNTS['independent_diagonal_bounds'] += 1
    a, r, w = sp.symbols('a r w')
    assert sp.expand(a*a - 2*r*a - ((a-r)**2-r*r)) == 0
    assert sp.expand((w-r)**2 + 2*r*(w-r) - (w*w-r*r)) == 0


def composition_checks():
    rng = random.Random(20261023)
    exported = []
    for trial in range(120):
        q = trial % 3
        p = 3 * q
        options = uniform_patterns(p, 3) if p else []
        s = rng.randrange(7) if options else 0
        rows = tuple(rng.choice(options) for _ in range(s))
        inst = Instance(rows, (1,) * p, q, 3)
        exact_cover = any(all(sum(row[u] * flag for row, flag in zip(rows, flags)) == 1 for u in range(p))
                          for flags in it.product((0, 1), repeat=s))
        target = build(inst, arithmetic=True)
        result = target_solve(target)
        assert exact_cover == (result is not None)
        for counts in it.product(range(q + 1), repeat=s):
            if source(inst, counts):
                assert sum(counts) == q and all(c in (0, 1) for c in counts)
                assert all(sum(row[u] * c for row, c in zip(rows, counts)) == 1 for u in range(p))
                COUNTS['composition_feasible_count_witnesses'] += 1
        if result:
            counts = extract(inst, target, result[1])
            assert counts is not None and source(inst, counts)
            assert all(c in (0, 1) for c in counts)
        COUNTS['exact_cover_staff_qubo_composition'] += 1
        if trial < 6:
            exported.append(check_instance(inst, rng, True))
    return exported


def expect_overflow(call):
    try:
        call()
    except Overflow:
        COUNTS['typed_arithmetic_rejections'] += 1
    else:
        raise AssertionError('Expected arithmetic overflow')


def arithmetic_checks():
    assert 2 * MAX * MAX <= 2**127 - 1
    # All three pairwise i128 products fit, but their arbitrary sum need not.
    assert MAX * MAX + 2 * MAX * MAX > 2**127 - 1
    COUNTS['i128_product_bound_checks'] += 2
    for bound in [0, 1, 2, 3, 2**31-1, 2**31, 2**62-1, 2**62, MAX]:
        w = weights(bound)
        assert sum(w) == bound and len(w) <= 63
        assert len(w) == bound.bit_length()
        COUNTS['large_block_weight_bounds'] += 1
    # Budget row with one block: energy lies in [-W^2, 0].
    # Its exact negative coefficient sum is W^2+sum_{i<j}2a_i*a_j.
    def negative_sum(w):
        a = weights(w)
        return sum(2*w*x-x*x for x in a)
    lo, hi = 0, math.isqrt(MAX) + 1
    while lo + 1 < hi:
        mid = (lo + hi)//2
        if negative_sum(mid) <= MAX:
            lo = mid
        else:
            hi = mid
    assert negative_sum(lo) <= MAX < negative_sum(hi)
    inst = Instance((), (), lo, 77)
    target = build(inst, arithmetic=True)
    rng = random.Random(23)
    for _ in range(1000):
        bits = tuple(rng.randrange(2) for _ in range(target.n))
        values = [v * bits[i] * bits[j] for (i, j), v in target.coefficients.items()]
        rng.shuffle(values)
        total = 0
        for value in values:
            total = checked(total + value)
        assert total == energy(target, bits)
        COUNTS['random_evaluation_prefix_orders'] += 1
    expect_overflow(lambda: build(Instance((), (), hi, 77), arithmetic=True))
    expect_overflow(lambda: build(Instance((), (), math.isqrt(MAX) + 1, 77), arithmetic=True))
    expect_overflow(lambda: build(Instance(((1,), (1,)), (0,), MAX, 1), arithmetic=True))
    expect_overflow(lambda: build(Instance(((1,),), (MAX,), MAX, 1), arithmetic=True))
    expect_overflow(lambda: source(Instance(((1,), (1,)), (0,), MAX, 1), (MAX, 1)))
    # Length and coordinate validation precede the overflowing total fold.
    assert source(Instance(((1,), (1,)), (0,), MAX, 1), (MAX,)) is False
    assert source(Instance(((1,), (1,)), (0,), MAX, 1), (MAX, -1)) is False
    assert source(Instance(((1,), (1,)), (0,), MAX, 1), (MAX, MAX + 1)) is False
    # Budget failure precedes coverage, and a successful witness bounds coverage.
    assert source(Instance(((1,), (1,)), (MAX,), MAX, 1), (MAX, 0))
    early = build(Instance(((1,), (1,), (1,)), (MAX,), MAX - 1, 1), arithmetic=True)
    assert early.early and extract(Instance((), (), 0, 0), early, ()) is None
    for negative in [MIN, MIN+1, -1, 0]:
        target = build(Instance(((1,),), (negative,), 0, 1), arithmetic=True)
        assert target.n == 0 and target.threshold == 0
        assert extract(Instance(((1,),), (negative,), 0, 1), target, ()) == (0,)
        COUNTS['negative_requirement_boundaries'] += 1
    return {'safe_budget_for_separate_sum_guard': lo, 'next_rejected_budget': hi,
            'safe_negative_sum': negative_sum(lo), 'unsafe_negative_sum': negative_sum(hi),
            'sqrt_i64_max': math.isqrt(MAX)}


def main():
    block_checks()
    shared = examples_and_malformed()
    symbolic_checks()
    boundary = arithmetic_checks()
    shared += composition_checks()
    shared += finite_checks()
    test_general_hypothesis()
    test_planted_hypothesis()
    (OUT / 'shared-instances.json').write_text(json.dumps(shared, indent=2) + '\n')
    report = {'verdict': 'PASS', 'counts': dict(COUNTS), 'arithmetic_boundaries': boundary,
              'shared_instances': len(shared), 'seed': 20261022,
              'limits': ['Finite base family has W in {0,1,2}, requirements in {-1,0,1,2}, ordered duplicate-permitting uniform-k patterns.',
                         'All (s,p) in 0..5 covered; families of at most 300 instances exhausted, others have 300 distinct seeded samples.',
                         'Hypothesis adds W through 6; arbitrary i64 coefficient bounds are algebraic, boundary checks finite.',
                         'No repository code, prototype, or shared corpus was read; this validates the proof and independent reference only.',
                         'Exact-cover composition is independently modeled mathematically; no repository graph dispatch was exercised.',
                         'No real allocation exhaustion, usize overflow, Rust serialization, or implementation error types were exercised.']}
    (OUT / 'report.json').write_text(json.dumps(report, indent=2) + '\n')
    print(json.dumps(report, indent=2), flush=True)


if __name__ == '__main__':
    main()
Constructor implementation imported as prototypes.py (staffing portion)
"""Research polynomials, not certified production reductions."""
from collections import defaultdict
from itertools import combinations

class Q:
    def __init__(self): self.n=0; self.q=defaultdict(int); self.c=0; self.decision=False
    def var(self): i=self.n; self.n+=1; return i
    def add(self,i,j,v): self.q[min(i,j),max(i,j)]+=v
    def square(self,terms,c=0,p=1):
        terms=dict(terms); self.c+=p*c*c
        for i,v in terms.items(): self.add(i,i,p*(v*v+2*c*v))
        for (i,v),(j,w) in combinations(terms.items(),2): self.add(i,j,2*p*v*w)
    def one(self,row,p=1): self.square([(i,1) for i in row],-1,p)
    def integer(self,bound):
        assert bound>=0
        terms=[]; weight=1
        while bound:
            v=min(weight,bound); terms.append((self.var(),v)); bound-=v; weight*=2
        return terms
    def finish(self): self.q={ij:v for ij,v in self.q.items() if v}; return self
    def energy(self,bits): return self.c+sum(v*bits[i]*bits[j] for (i,j),v in self.q.items())

def staff(d):
    q=Q(); q.decision=True; W=d['num_workers']; r=d['requirements']; A=d['schedules']
    if any(v>W for v in r): q.c=1; q.rows=[]; return q.finish()
    q.rows=[q.integer(W) for _ in A]
    q.square([t for row in q.rows for t in row]+q.integer(W),-W)
    for u,required in enumerate(r):
        if required<=0: continue
        q.square([t for row,a in zip(q.rows,A) if a[u] for t in row]+[(i,-v) for i,v in q.integer(W-required)],-required)
    return q.finish()

@isPANN

isPANN commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Phase 4 domain and practicality audit

The final shipping scope is StaffScheduling → DecisionQUBO only. This is stacked on Phase 3 commit 109985f4103d4c4785f60b8a34007580cd7f2434, whose final scope has four rules. Phase 1/2/3 PRs and every roadmap comment were read. Historical seven-rule Phase 3 reports do not describe the base. No excluded rule is restored, and no inherited validation gap is declared resolved.

All rejected encodings below are preliminary research prototypes, not certified reductions. A timeout establishes failure of this proposed encoding under this validation gate, not impossibility of a better future formulation. Screening stopped at the first failure. The remaining 100-case candidate corpora were inventoried but not raw-solved after rejection. No rejected reducer, registration, example, test runner or research artifact is included in the package.

Domains, semantics, routes, and endpoints

Candidate Exact source contract and arithmetic Witness / current routes / proposed endpoint Outcome
JobShopScheduling Nonnegative lengths, zero/empty accepted; adjacent operations on the same machine rejected, later revisits allowed. Machine order plus job precedences must be acyclic. Earliest finish sums are checked; cycle/overflow yields no makespan. Finite serial horizon is sum of durations when representable. Concatenated per-machine Lehmer codes, not start times. Brute-force source; no previous outgoing route. Required endpoint QUBO preserving Min, not one fixed horizon. Time-indexed starts, precedence/conflict penalties and unary makespan fail raw canonical optimum proof. Zero-duration order/tie reconstruction remains uncertified. Deferred.
StaffScheduling W is nonnegative i64 with W+1 fitting usize; each indexed Boolean pattern has p periods and exactly k active periods. Duplicate/empty patterns accepted, k unrestricted with no patterns. Requirements arbitrary signed i64. Count coordinates 0..W; checked count sum then budget, checked coverage. Integer multiplicity vector. Source brute force; previously no outgoing route. ExactCoverBy3Sets incoming edge has witness and identity Or mappings. New endpoint DecisionQUBO, Or→Or. Full proof, independent adversary, complete raw staffing and incoming-chain validation. Ship first outgoing route; not faster than source solver.
OpenShopScheduling Nonnegative rectangular processing times, empty dimensions accepted, checked total horizon. Start-time witness; job AND machine intervals must not overlap. Zero-duration operations inside a positive interval still violate the evaluator's strict overlap predicate; endpoints may coincide. Checked finish arithmetic. Job-major starts; Min. Existing bounded-integer ILP→Boolean ILP→QUBO; source fixed ILP. Separate DecisionOpenShopScheduling Or wrapper has Partition incoming edge. Start-grid plus both conflict families and unary makespan times out. Neither optimization nor separate decision rule ships; Partition chain not newly validated.
FlowShopScheduling Or feasibility, not a makespan optimization model: nonnegative processing times and deadline; empty dimensions accepted. Same job permutation on every machine. Checked max(previous machine, previous job)+duration recurrence. Actual job permutation (stale source prose mentions Lehmer); brute-force registration decodes its search indices first. Existing bounded ILP route and fixed ILP source solver. Appropriate endpoint DecisionQUBO. Assignment permutation plus binary completion inequalities and common deadline times out. Deferred.
PreemptiveScheduling Strictly positive task lengths: zero length rejected. Empty task set accepted with positive processor count. Witness is n×H Boolean activity, H=sum lengths. Exact processing counts, per-slot capacity, last predecessor slot before first successor slot. Duplicate/self/cyclic precedences accepted and may be NO. Integer slots are the source semantics already. Activity matrix; Min. Existing ILP uses tighter scheduling windows. Incoming 3SAT/Ullman route has threshold/value semantics, not naive identity. Full-horizon activity with capacity/precedence and makespan penalties times out; no claim that it dominates tighter existing windows. No new 3SAT chain certified. Deferred.
SequencingWithDeadlinesAndSetUpTimes Signed nonzero task lengths, signed setup times/deadlines accepted. Initial task has no setup; switching compiler adds the destination compiler's setup before task duration. Sequential additions checked, deadlines checked after each task. Actual permutation; Or. Existing ILP source/route uses positive setup coefficients and positive-domain big-M logic; signed-domain equivalence is not established and must not be assumed as a trustworthy baseline. Transition AND gates plus complete signed deadline-prefix equalities times out. No pairwise-cost surrogate ships. Deferred.
SequencingToMinimizeMaximumCumulativeCost Signed costs, duplicate/self/cyclic precedence arcs accepted. Checked cumulative additions; maximum starts at zero, so empty prefix is included and optimum cannot be negative. All relevant prefixes matter. Actual permutation; Min. Existing bounded ILP plus integer encoding route; witness map exists, completed objective mapping must be checked separately and was not recovered from timed-out raw baseline. Position assignment, signed prefixes and bounded max variable times out. Deferred.
ProductionPlanning At least one period; nonnegative demands, capacities, unit/setup/holding costs and budget; zero allowed. Quantity coordinates 0..capacity with capacity+1 fitting usize. Checked cumulative production/demand, no backlog, end-of-period inventory, setup iff quantity>0; nonnegative cost accumulation and early budget failure. Quantities; Or, so DecisionQUBO. Existing bounded ILP source/route; Partition incoming identity aggregate. Quantity one-hot, inventory flow and budget squares pass canonical but time out on first corpus instance. Remaining corpus and incoming Partition chain not attempted for this proposal. Deferred.
RegisterSufficiency Directed edge (v,u) means v depends on u; self loops/out-of-range rejected, duplicates/cycles accepted. Register bound usize can exceed i64 and then source evaluation errors. Live values depend on execution order and future consumers; sinks remain live through end. Positions per vertex; Or. Customized branch-and-bound source; binary ILP route; 3SAT incoming. Cumulative computed/live bits with order-dependent last-consumer constraints time out. Static interference approximation would be wrong. Incoming chain not newly validated. Deferred.
RootedTreeStorageAssignment Parent array has exactly one self-root, is connected/acyclic; each subset must form an ancestor chain; cost depth span+1−subset size. Duplicate members rejected, duplicate subsets allowed, empty subsets zero, signed budget accepted. Empty-universe source has a special condition on collection emptiness. Parent array; Or. Existing bounded ILP source/route with parent/depth/ancestor/product/endpoints; RootedTreeArrangement incoming. No independently proved practical full-tree encoding. Path-only shortcut is false: subsets {0,1},{0,2},{0,3}, bound0 have the zero-cost rooted star [0,0,0,0], while no path places 0 next to three leaves. Existing raw route also times out. Deferred without claiming a direct construction.

Primary sources and interpretation

The source code determines the variant; roadmap prose is a hypothesis. Venturelli et al. use fixed-makespan decisions with search, which cannot silently replace the repository's optimization contract. Zhang, Lo Bianco and Beck analyze direct job-shop QUBOs and their scale; exact makespan and sum-of-completion surrogates are distinct. Lucas and Glover et al. supply squared-constraint tools, not a proof of this exact staffing variant. The staffing certificate supplies the finite multiplicity bounds and both directions independently.

Sethi's register-allocation paper supports examining sequencing-dependent liveness; it does not justify a static interference replacement. The cumulative-cost primary paper includes signed costs and special precedence structures; its special-case algorithm is not a general direct reduction. The Wagner–Whitin lot-sizing source was identified, but its uncapacitated setting cannot establish the repository's exact capacitated decision variant. Flow-shop DOI 10.1145/3638530.3664150 and the Garey–Johnson SR5 scan were located but full text was unavailable; they are not treated as verification evidence.

Validation protocol and limitations

The inventory and suite were fixed before screening: all 100 independent cases per candidate, canonical cases, explicit boundaries, and relevant incoming corpus families for surviving rules. Fixed small-case raw budget: 15 seconds, one worker, CP-SAT 9.15.6755, linearization_level=2, random_seed=1. The raw model contains only emitted Boolean quadratic terms, exact Boolean product equalities (one auxiliary per nonzero off-diagonal), and the emitted Decision threshold or optimization objective. No source constraints, source witness hints, expected-optimum bounds, or truth-dependent solver configuration. CP-SAT's linearization setting is documented in its parameter source.

Decision YES requires a certified threshold witness; NO requires INFEASIBLE. Optimization requires OPTIMAL, never a FEASIBLE incumbent. Independent truth is checked only after solving. Feasibility raw results, separate oracle/constructor checks and exact coefficient comparisons are reported separately. Source timing includes debug-CLI startup. Construction and recovery are measured through actual CLI bundles. Dense target memory was not material for the shipped sparse targets; peak RSS was not separately measured. Finite tests do not establish performance for larger p,s,W or different target solvers. Numeric magnitude remains visible: staffing uses O(log W) bits per count, squared coefficients can overflow even when the source does not. Typed errors reject unrepresentable constructions, including conservative guards; no silent source-domain restriction or saturation.

All corpus-specific scripts, environments, logs, rejected prototypes, proofs and reports remain outside both this repository and the read-only corpus. The whole corpus tree, including hidden files, is SHA-256 checked before/after. Existing-route timeouts/semantic gaps are recorded as unavailable evidence, never evidence that a new rule is superior.

Timeout diagnosis

Repeated the unchanged canonical PreemptiveScheduling and FlowShopScheduling emitted prototype polynomials under the same 15-second configuration, collecting solver response statistics. Both remained unresolved, with no MODEL_INVALID result. Preemptive: FEASIBLE, objective −545 versus best bound −798, 17,484 conflicts, 337,701 branches and 637,439 LP iterations. Flow: UNKNOWN, 29,942 conflicts, 133,161 branches and 346,316 LP iterations. Thus Boolean-product LP strengthening was active but insufficient for these encodings. No source constraints or optimum bounds were introduced, and no budget was increased. This diagnostic supports deferral; it is not proof that another formulation cannot work. No formulation or solver fix was made, so no partial rerun is claimed to clear a failure.

@isPANN

isPANN commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Phase 4 measurements

Measured on a 32-GiB macOS host, debug CLI; seconds are wall-clock and include startup where stated. Solver: OR-Tools 9.15.6755 CP-SAT, 15 s,1worker,linearization2,seed 1. Product auxiliaries equal the coupling count. Source cases are small; expanded target size is shown separately.

Complete independent corpus inventory

Each row has 100 verified source instances; ranges are actual source dimensions.

Exact model Dimensions
JobShopScheduling jobs 3..3, operations 6..6, machines 2..2, horizon 8..21
StaffScheduling periods 7..7, patterns 4..6, workers 3..6
OpenShopScheduling jobs 4..4, machines 3..3, operations 12..12, horizon 23..906
DecisionOpenShopScheduling jobs 4..4, machines 3..3, operations 12..12, horizon 23..906
FlowShopScheduling jobs 5..5, machines 3..3, operations 15..15, horizon 45..1646
PreemptiveScheduling tasks 3..3, horizon 3..6
SequencingWithDeadlinesAndSetUpTimes tasks 5..5, horizon 10..407
SequencingToMinimizeMaximumCumulativeCost tasks 6..6, magnitude 24..448
ProductionPlanning periods 4..4, max_capacity 3..7, total_capacity 10..23
RegisterSufficiency vertices 6..8, arcs 6..15, bound 2..5
RootedTreeStorageAssignment universe 5..5, subsets 4..4
ExactCoverBy3Sets incoming universe 9, subsets 7;100 cases, all YES

Shipped production results

219/219 proven decisions,0 timeouts:115 staffing (100 corpus,13 boundaries,2 canonical) and104 incoming X3C (100 corpus,1 canonical,3 independently enumerated boundary instances). X3C corpus has no NO instance; the separate exhaustive six-element intersecting-triples instance supplies an actual NO chain. Every YES witness evaluates true at the original source, every completed Or value maps identically, every emitted polynomial equals the independently verified prototype.

Suite N off-diagonal E / solver auxiliaries max coefficient / bits raw seconds min / median / max source seconds min / median / max
StaffScheduling corpus 12..39 49..455 8..126 / 4..7 0.0031 / 0.1484 / 13.9043 0.0079 / 0.0104 / 0.0406
ExactCoverBy3Sets corpus 34..34 213..213 24..24 / 5..5 0.0108 / 0.2140 / 1.2245 0.0068 / 0.0089 / 0.0216

Direct staffing had no old QUBO route. This adds connectivity but does not beat source brute force. Existing direct X3C is much better than routing through staffing:7 variables,13 couplings/auxiliaries,max coefficient 4(3bits),raw 0.0007..0.0015 s on the same101 canonical/corpus sources. The new upstream route is validated for compatibility, not recommended over that direct edge.

Rejected prototype screening and existing-route baselines

Candidate / attempted case source dimensions prototype N/E max abs coefficient / bits raw status / seconds attempted / planned canonical+corpus
FlowShopScheduling / canonical {'jobs': 5, 'machines': 3, 'operations': 15, 'horizon': 45} 250/3825 640 / 10 UNKNOWN / 15.0022 1/101
JobShopScheduling / canonical {'jobs': 5, 'operations': 12, 'machines': 2, 'horizon': 34} 420/14844 70 / 7 FEASIBLE / 15.0027 1/101
OpenShopScheduling / canonical {'jobs': 4, 'machines': 3, 'operations': 12, 'horizon': 23} 288/4880 48 / 6 FEASIBLE / 15.0004 1/101
PreemptiveScheduling / canonical {'tasks': 5, 'horizon': 9} 72/494 70 / 7 FEASIBLE / 15.0001 1/101
ProductionPlanning / canonical {'periods': 4, 'max_capacity': 4, 'total_capacity': 16} 41/634 192 / 8 OPTIMAL / 1.0227 2/101
ProductionPlanning / instances/ProductionPlanning/random-000-uniform_small-982978211.json {'periods': 4, 'max_capacity': 4, 'total_capacity': 10} 36/506 248705 / 18 UNKNOWN / 15.0041 2/101
RegisterSufficiency / canonical {'vertices': 7, 'arcs': 8, 'bound': 3} 217/909 14 / 4 UNKNOWN / 15.0002 1/101
SequencingToMinimizeMaximumCumulativeCost / canonical {'tasks': 6, 'magnitude': 12} 63/1352 756 / 10 FEASIBLE / 15.0007 1/101
SequencingWithDeadlinesAndSetUpTimes / canonical {'tasks': 5, 'horizon': 10} 103/3794 448 / 9 UNKNOWN / 15.0019 1/101

RootedTreeStorageAssignment: no certified direct prototype; full branching proof/practicality unresolved, direct corpus0/100. DecisionOpenShop is separately inventoried and its old route measured; no new decision prototype ships. The other rejected candidates stop at first timeout; unattempted cases and upstream chains remain unverified. Time-indexed shop/preemptive grids depend pseudopolynomially on duration sums; quantity one-hot production depends pseudopolynomially on capacities. Binary-slack constructions still depend on coefficient magnitude and conditioning.

Existing source / case source seconds / result existing QUBO route N/E max coefficient / bits raw status / seconds construction / full seconds
JobShopScheduling / canonical 0.9159 / Min(19) no outgoing route at base — — —
JobShopScheduling / first-corpus 0.0074 / Min(10) no outgoing route at base — — —
StaffScheduling / canonical 0.0073 / Or(true) no outgoing route at base — — —
StaffScheduling / first-corpus 0.0065 / Or(true) no outgoing route at base — — —
OpenShopScheduling / canonical 0.0289 / Min(8) 515/7060 1815360 / 21 FEASIBLE / 15.0026 0.0699 / 15.1107
OpenShopScheduling / first-corpus 0.0444 / Min(154) 855/20886 10425584356 / 34 FEASIBLE / 15.0040 0.1307 / 15.2284
DecisionOpenShopScheduling / canonical 0.0117 / Or(true) 7/17 32 / 6 OPTIMAL / 0.0012 0.0406 / 0.0939
DecisionOpenShopScheduling / first-corpus 0.0175 / Or(true) 604/11677 615096832 / 30 FEASIBLE / 15.0000 0.0881 / 15.1433
FlowShopScheduling / canonical 0.0154 / Or(true) 565/7280 3819200 / 22 FEASIBLE / 15.0018 0.0799 / 15.1166
FlowShopScheduling / first-corpus 0.0546 / infeasible 940/21469 7309661184 / 33 FEASIBLE / 15.0001 0.1490 / 15.2419
PreemptiveScheduling / canonical 0.0164 / Min(5) 183/938 8800 / 14 FEASIBLE / 15.0005 0.0481 / 15.0544
PreemptiveScheduling / first-corpus 0.0837 / Min(5) 118/601 4544 / 13 FEASIBLE / 15.0106 0.0508 / 15.0663
SequencingWithDeadlinesAndSetUpTimes / canonical 0.0157 / Or(true) 355/4611 2285244 / 22 FEASIBLE / 15.0019 0.0630 / 15.0856
SequencingWithDeadlinesAndSetUpTimes / first-corpus 0.0162 / infeasible 450/7231 7095245432 / 33 FEASIBLE / 15.0003 0.0723 / 15.1054
SequencingToMinimizeMaximumCumulativeCost / canonical 0.0169 / Min(3) 137/2032 80896 / 17 FEASIBLE / 15.0008 0.0462 / 15.0567
SequencingToMinimizeMaximumCumulativeCost / first-corpus 0.0193 / Min(52) 177/3636 942669824 / 30 FEASIBLE / 15.0009 0.0496 / 15.0666
ProductionPlanning / canonical 0.0208 / Or(true) 61/690 6400 / 13 FEASIBLE / 15.0006 0.0440 / 15.0487
ProductionPlanning / first-corpus 0.0561 / infeasible 50/623 217816064 / 28 FEASIBLE / 15.0005 0.0471 / 15.0510
RegisterSufficiency / canonical 0.0122 / Or(true) 307/1089 650 / 10 FEASIBLE / 15.0008 0.0527 / 15.0608
RegisterSufficiency / first-corpus 0.0127 / Or(true) 204/684 520 / 10 FEASIBLE / 15.0003 0.0481 / 15.0525
RootedTreeStorageAssignment / canonical 0.1023 / Or(true) 2375/8260 101520 / 17 FEASIBLE / 15.0014 0.7083 / 15.7537
RootedTreeStorageAssignment / first-corpus 0.1112 / Or(true) 2378/8302 106400 / 17 FEASIBLE / 15.0019 0.7049 / 15.7614

All shortest available routes were constructed on identical canonical and first-corpus inputs; all existing paths were enumerated. Almost all baseline QUBOs lack proven optimum under the same15 s limit, so completed recovery is unavailable. The tiny DecisionOpenShop canonical target is OPTIMAL with a valid extracted witness, but its existing route has no aggregate-capable path; this inherited gap is not fixed here. Signed setup ILP semantics are not established, so those source results are observational only. No baseline failure is claimed as a speedup.

Composed parameter bounds

  • StaffScheduling → DecisionQUBO: {"fields":[{"field":"num_vars","formula":"63 * (1 + num_periods + num_schedules)","relation":"upper_bound"},{"field":"num_quadratic_terms","formula":"(63 * (1 + num_periods + num_schedules))^2","relation":"upper_bound"}]}
  • ExactCoverBy3Sets → DecisionQUBO: {"fields":[{"field":"num_vars","formula":"num_subsets","relation":"exact"},{"field":"num_quadratic_terms","formula":"num_subsets^2","relation":"upper_bound"}]}
  • ExactCoverBy3Sets → StaffScheduling → DecisionQUBO: {"fields":[{"field":"num_vars","formula":"63 + 63 * num_subsets + 63 * universe_size","relation":"upper_bound"},{"field":"num_quadratic_terms","formula":"3969 + 3969 * num_subsets^2 + 3969 * universe_size^2 + 7938 * num_subsets + 7938 * num_subsets * universe_size + 7938 * universe_size","relation":"upper_bound"}]}
Existing baseline shortest-route composed bounds
  • DecisionOpenShopScheduling: {"fields":[{"field":"num_vars","formula":"num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1 + schedule_horizon_bits * (num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1) + (1 + num_machines + num_jobs * num_machines * (-1 + num_jobs) + num_jobs * num_machines * (-1 + num_machines)) * (1 + 2 * schedule_horizon_bits + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1) + (1 + num_machines + num_jobs * num_machines * (-1 + num_jobs) + num_jobs * num_machines * (-1 + num_machines)) * (num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1 + schedule_horizon_bits * (num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1))","relation":"upper_bound"},{"field":"num_quadratic_terms","formula":"2 * (1 + num_machines + num_jobs * num_machines * (-1 + num_jobs) + num_jobs * num_machines * (-1 + num_machines)) * (1 + 2 * schedule_horizon_bits + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1) * (num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1 + schedule_horizon_bits * (num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1)) + 2 * (1 + num_machines + num_jobs * num_machines * (-1 + num_jobs) + num_jobs * num_machines * (-1 + num_machines)) * (num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1 + schedule_horizon_bits * (num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1))^2 + 2 * (1 + 2 * schedule_horizon_bits + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1) * (num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1 + schedule_horizon_bits * (num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1)) * (1 + num_machines + num_jobs * num_machines * (-1 + num_jobs) + num_jobs * num_machines * (-1 + num_machines))^2 + (1 + num_machines + num_jobs * num_machines * (-1 + num_jobs) + num_jobs * num_machines * (-1 + num_machines))^2 * (1 + 2 * schedule_horizon_bits + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1)^2 + (1 + num_machines + num_jobs * num_machines * (-1 + num_jobs) + num_jobs * num_machines * (-1 + num_machines))^2 * (num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1 + schedule_horizon_bits * (num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1))^2 + (num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1 + schedule_horizon_bits * (num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1))^2","relation":"upper_bound"}]}
  • FlowShopScheduling: {"fields":[{"field":"num_vars","formula":"num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1 + (1 + max_time_bits) * (num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1) + (1 + 2 * (1 + max_time_bits) + num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1) * (2 * num_jobs + num_jobs * num_processors * (-1 + num_jobs) + num_jobs * (-1 + num_jobs) + num_jobs * (-1 + num_processors)) + (2 * num_jobs + num_jobs * num_processors * (-1 + num_jobs) + num_jobs * (-1 + num_jobs) + num_jobs * (-1 + num_processors)) * (num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1 + (1 + max_time_bits) * (num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1))","relation":"upper_bound"},{"field":"num_quadratic_terms","formula":"2 * (1 + 2 * (1 + max_time_bits) + num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1) * (2 * num_jobs + num_jobs * num_processors * (-1 + num_jobs) + num_jobs * (-1 + num_jobs) + num_jobs * (-1 + num_processors)) * (num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1 + (1 + max_time_bits) * (num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1)) + 2 * (1 + 2 * (1 + max_time_bits) + num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1) * (num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1 + (1 + max_time_bits) * (num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1)) * (2 * num_jobs + num_jobs * num_processors * (-1 + num_jobs) + num_jobs * (-1 + num_jobs) + num_jobs * (-1 + num_processors))^2 + 2 * (2 * num_jobs + num_jobs * num_processors * (-1 + num_jobs) + num_jobs * (-1 + num_jobs) + num_jobs * (-1 + num_processors)) * (num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1 + (1 + max_time_bits) * (num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1))^2 + (1 + 2 * (1 + max_time_bits) + num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1)^2 * (2 * num_jobs + num_jobs * num_processors * (-1 + num_jobs) + num_jobs * (-1 + num_jobs) + num_jobs * (-1 + num_processors))^2 + (2 * num_jobs + num_jobs * num_processors * (-1 + num_jobs) + num_jobs * (-1 + num_jobs) + num_jobs * (-1 + num_processors))^2 * (num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1 + (1 + max_time_bits) * (num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1))^2 + (num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1 + (1 + max_time_bits) * (num_jobs * num_processors + num_jobs * (-1 + num_jobs) * 2^-1))^2","relation":"upper_bound"}]}
  • OpenShopScheduling: {"fields":[{"field":"num_vars","formula":"1 + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1 + schedule_horizon_bits * (1 + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1) + (1 + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1 + schedule_horizon_bits * (1 + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1)) * (num_machines + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) + num_jobs * num_machines * (-1 + num_machines)) + (2 + 2 * schedule_horizon_bits + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1) * (num_machines + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) + num_jobs * num_machines * (-1 + num_machines))","relation":"upper_bound"},{"field":"num_quadratic_terms","formula":"2 * (1 + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1 + schedule_horizon_bits * (1 + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1)) * (2 + 2 * schedule_horizon_bits + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1) * (num_machines + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) + num_jobs * num_machines * (-1 + num_machines)) + 2 * (1 + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1 + schedule_horizon_bits * (1 + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1)) * (2 + 2 * schedule_horizon_bits + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1) * (num_machines + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) + num_jobs * num_machines * (-1 + num_machines))^2 + 2 * (num_machines + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) + num_jobs * num_machines * (-1 + num_machines)) * (1 + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1 + schedule_horizon_bits * (1 + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1))^2 + (1 + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1 + schedule_horizon_bits * (1 + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1))^2 * (num_machines + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) + num_jobs * num_machines * (-1 + num_machines))^2 + (2 + 2 * schedule_horizon_bits + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1)^2 * (num_machines + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) + num_jobs * num_machines * (-1 + num_machines))^2 + (1 + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1 + schedule_horizon_bits * (1 + num_jobs * num_machines + num_jobs * num_machines * (-1 + num_jobs) * 2^-1 + num_jobs * num_machines * (-1 + num_machines) * 2^-1))^2","relation":"upper_bound"}]}
  • PreemptiveScheduling: {"fields":[{"field":"num_vars","formula":"1 + 2 * num_tasks + d_max * num_tasks + max_schedule_magnitude_bits * (1 + 2 * num_tasks + d_max * num_tasks) + (1 + 2 * num_tasks + d_max * num_tasks + max_schedule_magnitude_bits * (1 + 2 * num_tasks + d_max * num_tasks)) * (d_max + num_precedences + 2 * d_max * num_tasks + 2 * num_tasks) + (2 + 2 * max_schedule_magnitude_bits + 2 * num_tasks + d_max * num_tasks) * (d_max + num_precedences + 2 * d_max * num_tasks + 2 * num_tasks)","relation":"upper_bound"},{"field":"num_quadratic_terms","formula":"2 * (1 + 2 * num_tasks + d_max * num_tasks + max_schedule_magnitude_bits * (1 + 2 * num_tasks + d_max * num_tasks)) * (2 + 2 * max_schedule_magnitude_bits + 2 * num_tasks + d_max * num_tasks) * (d_max + num_precedences + 2 * d_max * num_tasks + 2 * num_tasks) + 2 * (1 + 2 * num_tasks + d_max * num_tasks + max_schedule_magnitude_bits * (1 + 2 * num_tasks + d_max * num_tasks)) * (2 + 2 * max_schedule_magnitude_bits + 2 * num_tasks + d_max * num_tasks) * (d_max + num_precedences + 2 * d_max * num_tasks + 2 * num_tasks)^2 + 2 * (d_max + num_precedences + 2 * d_max * num_tasks + 2 * num_tasks) * (1 + 2 * num_tasks + d_max * num_tasks + max_schedule_magnitude_bits * (1 + 2 * num_tasks + d_max * num_tasks))^2 + (1 + 2 * num_tasks + d_max * num_tasks + max_schedule_magnitude_bits * (1 + 2 * num_tasks + d_max * num_tasks))^2 * (d_max + num_precedences + 2 * d_max * num_tasks + 2 * num_tasks)^2 + (2 + 2 * max_schedule_magnitude_bits + 2 * num_tasks + d_max * num_tasks)^2 * (d_max + num_precedences + 2 * d_max * num_tasks + 2 * num_tasks)^2 + (1 + 2 * num_tasks + d_max * num_tasks + max_schedule_magnitude_bits * (1 + 2 * num_tasks + d_max * num_tasks))^2","relation":"upper_bound"}]}
  • ProductionPlanning: {"fields":[{"field":"num_vars","formula":"3 * num_periods + 3 * num_periods * (max_numeric_magnitude_bits + num_periods) + (1 + 2 * (max_numeric_magnitude_bits + num_periods) + 3 * num_periods) * (1 + 3 * num_periods) + (1 + 3 * num_periods) * (3 * num_periods + 3 * num_periods * (max_numeric_magnitude_bits + num_periods))","relation":"upper_bound"},{"field":"num_quadratic_terms","formula":"2 * (1 + 2 * (max_numeric_magnitude_bits + num_periods) + 3 * num_periods) * (1 + 3 * num_periods) * (3 * num_periods + 3 * num_periods * (max_numeric_magnitude_bits + num_periods)) + 2 * (1 + 2 * (max_numeric_magnitude_bits + num_periods) + 3 * num_periods) * (3 * num_periods + 3 * num_periods * (max_numeric_magnitude_bits + num_periods)) * (1 + 3 * num_periods)^2 + 2 * (1 + 3 * num_periods) * (3 * num_periods + 3 * num_periods * (max_numeric_magnitude_bits + num_periods))^2 + (1 + 2 * (max_numeric_magnitude_bits + num_periods) + 3 * num_periods)^2 * (1 + 3 * num_periods)^2 + (1 + 3 * num_periods)^2 * (3 * num_periods + 3 * num_periods * (max_numeric_magnitude_bits + num_periods))^2 + (3 * num_periods + 3 * num_periods * (max_numeric_magnitude_bits + num_periods))^2","relation":"upper_bound"}]}
  • RegisterSufficiency: {"fields":[{"field":"num_vars","formula":"-1 * num_sinks * num_vertices + 2 * num_vertices^2 + (1 + num_vertices) * (num_vertices + 2 * num_arcs * num_vertices + num_vertices^2) + (num_vertices + 2 * num_arcs * num_vertices + num_vertices^2) * (-1 * num_sinks * num_vertices + 2 * num_vertices^2)","relation":"upper_bound"},{"field":"num_quadratic_terms","formula":"2 * (1 + num_vertices) * (num_vertices + 2 * num_arcs * num_vertices + num_vertices^2) * (-1 * num_sinks * num_vertices + 2 * num_vertices^2) + 2 * (1 + num_vertices) * (-1 * num_sinks * num_vertices + 2 * num_vertices^2) * (num_vertices + 2 * num_arcs * num_vertices + num_vertices^2)^2 + 2 * (num_vertices + 2 * num_arcs * num_vertices + num_vertices^2) * (-1 * num_sinks * num_vertices + 2 * num_vertices^2)^2 + (1 + num_vertices)^2 * (num_vertices + 2 * num_arcs * num_vertices + num_vertices^2)^2 + (num_vertices + 2 * num_arcs * num_vertices + num_vertices^2)^2 * (-1 * num_sinks * num_vertices + 2 * num_vertices^2)^2 + (-1 * num_sinks * num_vertices + 2 * num_vertices^2)^2","relation":"upper_bound"}]}
  • RootedTreeStorageAssignment: {"fields":[{"field":"num_vars","formula":"universe_size + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + (1 + num_subsets + universe_size) * (universe_size + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + universe_size^3) + (1 + universe_size + 2 * (1 + num_subsets + universe_size) + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + universe_size^3) * (2 + 4 * universe_size^3 + 5 * universe_size + 6 * universe_size^2 + num_subsets * (8 + 2 * universe_size^3 + 5 * universe_size^2 + 8 * universe_size)) + (2 + 4 * universe_size^3 + 5 * universe_size + 6 * universe_size^2 + num_subsets * (8 + 2 * universe_size^3 + 5 * universe_size^2 + 8 * universe_size)) * (universe_size + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + (1 + num_subsets + universe_size) * (universe_size + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + universe_size^3) + universe_size^3) + universe_size^3","relation":"upper_bound"},{"field":"num_quadratic_terms","formula":"2 * (1 + universe_size + 2 * (1 + num_subsets + universe_size) + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + universe_size^3) * (2 + 4 * universe_size^3 + 5 * universe_size + 6 * universe_size^2 + num_subsets * (8 + 2 * universe_size^3 + 5 * universe_size^2 + 8 * universe_size)) * (universe_size + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + (1 + num_subsets + universe_size) * (universe_size + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + universe_size^3) + universe_size^3) + 2 * (1 + universe_size + 2 * (1 + num_subsets + universe_size) + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + universe_size^3) * (universe_size + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + (1 + num_subsets + universe_size) * (universe_size + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + universe_size^3) + universe_size^3) * (2 + 4 * universe_size^3 + 5 * universe_size + 6 * universe_size^2 + num_subsets * (8 + 2 * universe_size^3 + 5 * universe_size^2 + 8 * universe_size))^2 + 2 * (2 + 4 * universe_size^3 + 5 * universe_size + 6 * universe_size^2 + num_subsets * (8 + 2 * universe_size^3 + 5 * universe_size^2 + 8 * universe_size)) * (universe_size + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + (1 + num_subsets + universe_size) * (universe_size + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + universe_size^3) + universe_size^3)^2 + (1 + universe_size + 2 * (1 + num_subsets + universe_size) + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + universe_size^3)^2 * (2 + 4 * universe_size^3 + 5 * universe_size + 6 * universe_size^2 + num_subsets * (8 + 2 * universe_size^3 + 5 * universe_size^2 + 8 * universe_size))^2 + (2 + 4 * universe_size^3 + 5 * universe_size + 6 * universe_size^2 + num_subsets * (8 + 2 * universe_size^3 + 5 * universe_size^2 + 8 * universe_size))^2 * (universe_size + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + (1 + num_subsets + universe_size) * (universe_size + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + universe_size^3) + universe_size^3)^2 + (universe_size + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + (1 + num_subsets + universe_size) * (universe_size + 2 * universe_size^2 + num_subsets * (3 + 2 * universe_size + universe_size^2) + universe_size^3) + universe_size^3)^2","relation":"upper_bound"}]}
  • SequencingToMinimizeMaximumCumulativeCost: {"fields":[{"field":"num_vars","formula":"1 + (1 + num_precedences + 3 * num_tasks + num_tasks^2) * (1 + (1 + num_tasks^2) * (max_cost_bits + num_tasks) + num_tasks^2) + (1 + num_precedences + 3 * num_tasks + num_tasks^2) * (2 + 2 * (max_cost_bits + num_tasks) + num_tasks^2) + (1 + num_tasks^2) * (max_cost_bits + num_tasks) + num_tasks^2","relation":"upper_bound"},{"field":"num_quadratic_terms","formula":"2 * (1 + num_precedences + 3 * num_tasks + num_tasks^2) * (1 + (1 + num_tasks^2) * (max_cost_bits + num_tasks) + num_tasks^2) * (2 + 2 * (max_cost_bits + num_tasks) + num_tasks^2) + 2 * (1 + num_precedences + 3 * num_tasks + num_tasks^2) * (1 + (1 + num_tasks^2) * (max_cost_bits + num_tasks) + num_tasks^2)^2 + 2 * (1 + (1 + num_tasks^2) * (max_cost_bits + num_tasks) + num_tasks^2) * (2 + 2 * (max_cost_bits + num_tasks) + num_tasks^2) * (1 + num_precedences + 3 * num_tasks + num_tasks^2)^2 + (1 + num_precedences + 3 * num_tasks + num_tasks^2)^2 * (1 + (1 + num_tasks^2) * (max_cost_bits + num_tasks) + num_tasks^2)^2 + (1 + num_precedences + 3 * num_tasks + num_tasks^2)^2 * (2 + 2 * (max_cost_bits + num_tasks) + num_tasks^2)^2 + (1 + (1 + num_tasks^2) * (max_cost_bits + num_tasks) + num_tasks^2)^2","relation":"upper_bound"}]}
  • SequencingWithDeadlinesAndSetUpTimes: {"fields":[{"field":"num_vars","formula":"num_tasks + 2 * num_tasks^2 + (2 + max_time_bits + num_tasks) * (2 * num_tasks + 3 * num_tasks * (-1 + num_tasks) + (-1 + num_tasks) * num_tasks^2 + num_tasks^2) + (num_tasks + 2 * num_tasks^2) * (2 * num_tasks + 3 * num_tasks * (-1 + num_tasks) + (-1 + num_tasks) * num_tasks^2 + num_tasks^2)","relation":"upper_bound"},{"field":"num_quadratic_terms","formula":"2 * (2 + max_time_bits + num_tasks) * (num_tasks + 2 * num_tasks^2) * (2 * num_tasks + 3 * num_tasks * (-1 + num_tasks) + (-1 + num_tasks) * num_tasks^2 + num_tasks^2) + 2 * (2 + max_time_bits + num_tasks) * (num_tasks + 2 * num_tasks^2) * (2 * num_tasks + 3 * num_tasks * (-1 + num_tasks) + (-1 + num_tasks) * num_tasks^2 + num_tasks^2)^2 + 2 * (2 * num_tasks + 3 * num_tasks * (-1 + num_tasks) + (-1 + num_tasks) * num_tasks^2 + num_tasks^2) * (num_tasks + 2 * num_tasks^2)^2 + (2 + max_time_bits + num_tasks)^2 * (2 * num_tasks + 3 * num_tasks * (-1 + num_tasks) + (-1 + num_tasks) * num_tasks^2 + num_tasks^2)^2 + (num_tasks + 2 * num_tasks^2)^2 * (2 * num_tasks + 3 * num_tasks * (-1 + num_tasks) + (-1 + num_tasks) * num_tasks^2 + num_tasks^2)^2 + (num_tasks + 2 * num_tasks^2)^2","relation":"upper_bound"}]}

@isPANN

isPANN commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Production raw-target per-case results

All219cases. N=target bits; E=nonzero off-diagonals and exact product auxiliaries; M=max coefficient magnitude, B=bit length; C=CLI construction; R=raw solve; F=construction+solver-model+solve+aggregate/witness recovery+source evaluate; S=source solve (debug CLI). Each row passed independent truth, exact coefficient equivalence, completed aggregate recovery, and YES source evaluation.

Case source dimensions N/E M/B status C/R/F/S seconds
model-canonical periods=7,patterns=5,workers=4 30/281 40/6 OPTIMAL 0.0472/0.1124/0.2604/0.0093
rule-canonical periods=3,patterns=3,workers=3 11/40 16/5 OPTIMAL 0.0465/0.0042/0.1457/0.0094
staff/random-000-uniform_small-1683386628.json periods=7,patterns=4,workers=3 15/87 40/6 OPTIMAL 0.0416/0.0169/0.1478/0.0085
staff/random-001-uniform_small-1683491357.json periods=7,patterns=6,workers=4 26/266 24/5 INFEASIBLE 0.0462/0.8039/0.8973/0.0126
staff/random-002-uniform_small-1683596086.json periods=7,patterns=5,workers=5 28/249 32/6 INFEASIBLE 0.0428/0.2473/0.3360/0.0113
staff/random-003-uniform_small-1683700815.json periods=7,patterns=4,workers=6 28/162 54/6 INFEASIBLE 0.0448/0.0685/0.1598/0.0101
staff/random-004-uniform_small-1683805544.json periods=7,patterns=6,workers=3 22/178 48/6 INFEASIBLE 0.0419/0.1468/0.2339/0.0099
staff/random-005-uniform_small-1683910273.json periods=7,patterns=5,workers=4 29/293 48/6 INFEASIBLE 0.0419/2.3738/2.4591/0.0091
staff/random-006-uniform_small-1684015002.json periods=7,patterns=4,workers=5 22/168 56/6 INFEASIBLE 0.0407/0.1221/0.2052/0.0096
staff/random-007-uniform_small-1684119731.json periods=7,patterns=6,workers=6 29/269 36/6 INFEASIBLE 0.0412/0.2028/0.2870/0.0392
staff/random-008-uniform_small-1684224460.json periods=7,patterns=5,workers=3 19/79 24/5 INFEASIBLE 0.0448/0.0072/0.0946/0.0101
staff/random-009-uniform_small-1684329189.json periods=7,patterns=4,workers=4 20/154 52/6 INFEASIBLE 0.0451/0.1263/0.2133/0.0099
staff/random-010-bimodal-1684433918.json periods=7,patterns=6,workers=5 28/284 64/7 INFEASIBLE 0.0422/0.7824/0.8705/0.0223
staff/random-011-bimodal-1684538647.json periods=7,patterns=5,workers=6 33/283 90/7 INFEASIBLE 0.0416/1.9959/2.0821/0.0131
staff/random-012-bimodal-1684643376.json periods=7,patterns=4,workers=3 12/61 32/6 INFEASIBLE 0.0508/0.0072/0.1038/0.0091
staff/random-013-bimodal-1684748105.json periods=7,patterns=6,workers=4 27/212 12/4 INFEASIBLE 0.0449/0.0438/0.1363/0.0126
staff/random-014-bimodal-1684852834.json periods=7,patterns=5,workers=5 32/327 56/6 INFEASIBLE 0.0432/6.7704/6.8685/0.0108
staff/random-015-bimodal-1684957563.json periods=7,patterns=4,workers=6 32/262 108/7 INFEASIBLE 0.0516/1.0087/1.1080/0.0110
staff/random-016-bimodal-1685062292.json periods=7,patterns=6,workers=3 18/128 32/6 INFEASIBLE 0.0419/0.0295/0.1210/0.0094
staff/random-017-bimodal-1685167021.json periods=7,patterns=5,workers=4 28/218 32/6 INFEASIBLE 0.0467/0.1882/0.2810/0.0108
staff/random-018-bimodal-1685271750.json periods=7,patterns=4,workers=5 23/109 16/5 INFEASIBLE 0.0445/0.0152/0.1050/0.0097
staff/random-019-bimodal-1685376479.json periods=7,patterns=6,workers=6 33/381 126/7 INFEASIBLE 0.0428/12.6412/12.7289/0.0393
staff/random-020-chain-1685481208.json periods=7,patterns=5,workers=3 18/118 48/6 INFEASIBLE 0.0377/0.0294/0.1062/0.0102
staff/random-021-chain-1685585937.json periods=7,patterns=4,workers=4 21/132 48/6 INFEASIBLE 0.0376/0.0282/0.1041/0.0079
staff/random-022-chain-1685690666.json periods=7,patterns=6,workers=5 24/247 40/6 INFEASIBLE 0.0368/0.2837/0.3602/0.0170
staff/random-023-chain-1685795395.json periods=7,patterns=5,workers=6 31/254 54/6 INFEASIBLE 0.0390/0.3437/0.4228/0.0117
staff/random-024-chain-1685900124.json periods=7,patterns=4,workers=3 15/87 48/6 OPTIMAL 0.0391/0.0056/0.1270/0.0082
staff/random-025-chain-1686004853.json periods=7,patterns=6,workers=4 33/396 48/6 INFEASIBLE 0.0385/8.0741/8.1547/0.0104
staff/random-026-chain-1686109582.json periods=7,patterns=5,workers=5 28/250 48/6 INFEASIBLE 0.0391/0.4500/0.5295/0.0097
staff/random-027-chain-1686214311.json periods=7,patterns=4,workers=6 23/145 36/6 INFEASIBLE 0.0383/0.0256/0.1035/0.0087
staff/random-028-chain-1686319040.json periods=7,patterns=6,workers=3 20/93 8/4 INFEASIBLE 0.0371/0.0055/0.0814/0.0088
staff/random-029-chain-1686423769.json periods=7,patterns=5,workers=4 23/230 40/6 OPTIMAL 0.0381/0.1002/0.2228/0.0084
staff/random-030-sparse_constraints-1686528498.json periods=7,patterns=4,workers=5 23/168 52/6 INFEASIBLE 0.0409/0.1207/0.2090/0.0097
staff/random-031-sparse_constraints-1686633227.json periods=7,patterns=6,workers=6 31/291 78/7 INFEASIBLE 0.0419/1.3953/1.4789/0.0359
staff/random-032-sparse_constraints-1686737956.json periods=7,patterns=5,workers=3 19/99 24/5 INFEASIBLE 0.0390/0.0174/0.0990/0.0090
staff/random-033-sparse_constraints-1686842685.json periods=7,patterns=4,workers=4 26/158 24/5 INFEASIBLE 0.0426/0.0432/0.1270/0.0107
staff/random-034-sparse_constraints-1686947414.json periods=7,patterns=6,workers=5 31/360 56/6 INFEASIBLE 0.0403/8.5208/8.6068/0.0190
staff/random-035-sparse_constraints-1687052143.json periods=7,patterns=5,workers=6 31/313 108/7 INFEASIBLE 0.0435/3.6064/3.6952/0.0149
staff/random-036-sparse_constraints-1687156872.json periods=7,patterns=4,workers=3 21/90 40/6 INFEASIBLE 0.0461/0.0208/0.1119/0.0088
staff/random-037-sparse_constraints-1687261601.json periods=7,patterns=6,workers=4 27/248 36/6 INFEASIBLE 0.0465/0.1713/0.2665/0.0134
staff/random-038-sparse_constraints-1687366330.json periods=7,patterns=5,workers=5 28/202 36/6 INFEASIBLE 0.0442/0.1203/0.2062/0.0112
staff/random-039-sparse_constraints-1687471059.json periods=7,patterns=4,workers=6 24/196 90/7 INFEASIBLE 0.0437/0.6091/0.6986/0.0098
staff/random-040-dense_constraints-1687575788.json periods=7,patterns=6,workers=3 15/103 32/6 INFEASIBLE 0.0453/0.0135/0.1054/0.0099
staff/random-041-dense_constraints-1687680517.json periods=7,patterns=5,workers=4 27/262 40/6 INFEASIBLE 0.0467/1.0550/1.1512/0.0108
staff/random-042-dense_constraints-1687785246.json periods=7,patterns=4,workers=5 24/159 48/6 INFEASIBLE 0.0486/0.0876/0.1787/0.0098
staff/random-043-dense_constraints-1687889975.json periods=7,patterns=6,workers=6 32/253 69/7 INFEASIBLE 0.0402/0.2564/0.3420/0.0352
staff/random-044-dense_constraints-1687994704.json periods=7,patterns=5,workers=3 14/86 32/6 OPTIMAL 0.0468/0.0076/0.1552/0.0084
staff/random-045-dense_constraints-1688099433.json periods=7,patterns=4,workers=4 19/142 36/6 INFEASIBLE 0.0499/0.0820/0.1773/0.0088
staff/random-046-dense_constraints-1688204162.json periods=7,patterns=6,workers=5 34/363 32/6 INFEASIBLE 0.0458/3.6715/3.7670/0.0229
staff/random-047-dense_constraints-1688308891.json periods=7,patterns=5,workers=6 31/254 54/6 INFEASIBLE 0.0458/0.5321/0.6233/0.0147
staff/random-048-dense_constraints-1688413620.json periods=7,patterns=4,workers=3 18/49 24/5 INFEASIBLE 0.0433/0.0047/0.0950/0.0093
staff/random-049-dense_constraints-1688518349.json periods=7,patterns=6,workers=4 22/228 56/6 OPTIMAL 0.0414/0.0085/0.1448/0.0100
staff/random-050-antichain-1688623078.json periods=7,patterns=5,workers=5 25/216 44/6 INFEASIBLE 0.0413/0.3378/0.4270/0.0114
staff/random-051-antichain-1688727807.json periods=7,patterns=4,workers=6 21/145 78/7 INFEASIBLE 0.0450/0.0326/0.1218/0.0100
staff/random-052-antichain-1688832536.json periods=7,patterns=6,workers=3 19/105 24/5 INFEASIBLE 0.0412/0.0151/0.0988/0.0107
staff/random-053-antichain-1688937265.json periods=7,patterns=5,workers=4 27/201 24/5 INFEASIBLE 0.0436/0.1132/0.2052/0.0096
staff/random-054-antichain-1689041994.json periods=7,patterns=4,workers=5 25/222 48/6 INFEASIBLE 0.0461/0.3684/0.4580/0.0109
staff/random-055-antichain-1689146723.json periods=7,patterns=6,workers=6 33/379 108/7 INFEASIBLE 0.0447/3.5896/3.6780/0.0394
staff/random-056-antichain-1689251452.json periods=7,patterns=5,workers=3 17/98 32/6 INFEASIBLE 0.0493/0.0174/0.1102/0.0092
staff/random-057-antichain-1689356181.json periods=7,patterns=4,workers=4 26/182 32/6 INFEASIBLE 0.0435/0.1688/0.2563/0.0092
staff/random-058-antichain-1689460910.json periods=7,patterns=6,workers=5 26/268 32/6 INFEASIBLE 0.0404/1.0157/1.1007/0.0264
staff/random-059-antichain-1689565639.json periods=7,patterns=5,workers=6 30/328 108/7 INFEASIBLE 0.0445/3.1699/3.2571/0.0124
staff/random-060-loose_bounds-1689670368.json periods=7,patterns=4,workers=3 13/62 40/6 INFEASIBLE 0.0426/0.0078/0.0944/0.0093
staff/random-061-loose_bounds-1689775097.json periods=7,patterns=6,workers=4 23/247 52/6 INFEASIBLE 0.0426/0.1854/0.2735/0.0151
staff/random-062-loose_bounds-1689879826.json periods=7,patterns=5,workers=5 28/191 52/6 INFEASIBLE 0.0437/0.0972/0.1928/0.0110
staff/random-063-loose_bounds-1689984555.json periods=7,patterns=4,workers=6 23/157 54/6 INFEASIBLE 0.0480/0.0178/0.1094/0.0102
staff/random-064-loose_bounds-1690089284.json periods=7,patterns=6,workers=3 20/165 56/6 OPTIMAL 0.0454/0.0134/0.1558/0.0091
staff/random-065-loose_bounds-1690194013.json periods=7,patterns=5,workers=4 24/215 52/6 INFEASIBLE 0.0424/0.6775/0.7673/0.0093
staff/random-066-loose_bounds-1690298742.json periods=7,patterns=4,workers=5 27/196 40/6 INFEASIBLE 0.0430/0.2984/0.3842/0.0094
staff/random-067-loose_bounds-1690403471.json periods=7,patterns=6,workers=6 39/370 72/7 INFEASIBLE 0.0424/10.4220/10.5127/0.0372
staff/random-068-loose_bounds-1690508200.json periods=7,patterns=5,workers=3 15/87 16/5 INFEASIBLE 0.0436/0.0090/0.0991/0.0104
staff/random-069-loose_bounds-1690612929.json periods=7,patterns=4,workers=4 24/193 48/6 INFEASIBLE 0.0471/0.6981/0.7950/0.0108
staff/random-070-tight_bounds-1690717658.json periods=7,patterns=6,workers=5 34/397 48/6 INFEASIBLE 0.0439/6.2854/7.2770/0.0218
staff/random-071-tight_bounds-1690822387.json periods=7,patterns=5,workers=6 29/251 72/7 INFEASIBLE 0.0439/1.2141/1.3048/0.0128
staff/random-072-tight_bounds-1690927116.json periods=7,patterns=4,workers=3 14/54 16/5 INFEASIBLE 0.0409/0.0031/0.0867/0.0098
staff/random-073-tight_bounds-1691031845.json periods=7,patterns=6,workers=4 30/250 24/5 INFEASIBLE 0.0433/0.2021/0.2899/0.0122
staff/random-074-tight_bounds-1691136574.json periods=7,patterns=5,workers=5 28/309 64/7 INFEASIBLE 0.0477/2.2654/2.3562/0.0103
staff/random-075-tight_bounds-1691241303.json periods=7,patterns=4,workers=6 23/148 54/6 INFEASIBLE 0.0436/0.0738/0.1628/0.0096
staff/random-076-tight_bounds-1691346032.json periods=7,patterns=6,workers=3 15/103 32/6 OPTIMAL 0.0434/0.0033/0.1392/0.0094
staff/random-077-tight_bounds-1691450761.json periods=7,patterns=5,workers=4 25/185 40/6 INFEASIBLE 0.0431/0.0859/0.1742/0.0102
staff/random-078-tight_bounds-1691555490.json periods=7,patterns=4,workers=5 20/169 24/5 INFEASIBLE 0.0470/0.0428/0.1325/0.0094
staff/random-079-tight_bounds-1691660219.json periods=7,patterns=6,workers=6 34/455 108/7 INFEASIBLE 0.0414/13.9043/13.9907/0.0385
staff/random-080-planted_witness-1691764948.json periods=7,patterns=5,workers=3 19/118 48/6 INFEASIBLE 0.0430/0.0465/0.1329/0.0103
staff/random-081-planted_witness-1691869677.json periods=7,patterns=4,workers=4 23/168 40/6 INFEASIBLE 0.0429/0.1501/0.2357/0.0088
staff/random-082-planted_witness-1691974406.json periods=7,patterns=6,workers=5 35/311 36/6 INFEASIBLE 0.0455/1.1625/1.2512/0.0216
staff/random-083-planted_witness-1692079135.json periods=7,patterns=5,workers=6 30/205 57/6 INFEASIBLE 0.0449/0.1513/0.2421/0.0141
staff/random-084-planted_witness-1692183864.json periods=7,patterns=4,workers=3 18/97 48/6 INFEASIBLE 0.0427/0.0294/0.1165/0.0089
staff/random-085-planted_witness-1692288593.json periods=7,patterns=6,workers=4 28/303 40/6 INFEASIBLE 0.0425/2.1058/2.1948/0.0128
staff/random-086-planted_witness-1692393322.json periods=7,patterns=5,workers=5 33/330 40/6 INFEASIBLE 0.0422/1.6889/1.7774/0.0118
staff/random-087-planted_witness-1692498051.json periods=7,patterns=4,workers=6 21/167 72/7 INFEASIBLE 0.0421/0.0509/0.1360/0.0095
staff/random-088-planted_witness-1692602780.json periods=7,patterns=6,workers=3 21/106 16/5 INFEASIBLE 0.0424/0.0184/0.1050/0.0148
staff/random-089-planted_witness-1692707509.json periods=7,patterns=5,workers=4 26/246 40/6 INFEASIBLE 0.0419/1.1691/1.2652/0.0110
staff/special-00-minimum_legal-1684386631.json periods=7,patterns=5,workers=6 29/208 36/6 INFEASIBLE 0.0499/0.0875/0.1905/0.0147
staff/special-01-empty_or_zero_constraints-1684491360.json periods=7,patterns=4,workers=3 16/55 24/5 INFEASIBLE 0.0486/0.0044/0.1060/0.0096
staff/special-02-single_element-1684596089.json periods=7,patterns=6,workers=4 24/247 36/6 INFEASIBLE 0.0489/0.2928/0.3932/0.0143
staff/special-03-obvious_yes-1684700818.json periods=7,patterns=5,workers=5 27/252 48/6 INFEASIBLE 0.0481/0.8313/0.9307/0.0114
staff/special-04-obvious_no-1684805547.json periods=7,patterns=4,workers=6 27/210 90/7 INFEASIBLE 0.0473/0.2002/0.2974/0.0101
staff/special-05-bound_equality-1684910276.json periods=7,patterns=6,workers=3 21/141 32/6 INFEASIBLE 0.0479/0.0508/0.1480/0.0105
staff/special-06-bound_adjacent-1685015005.json periods=7,patterns=5,workers=4 23/155 12/4 INFEASIBLE 0.0473/0.0214/0.1154/0.0102
staff/special-07-symmetric_multiple_optima-1685119734.json periods=7,patterns=4,workers=5 24/193 80/7 INFEASIBLE 0.0461/0.4714/0.5654/0.0093
staff/special-08-repeated_or_tied-1685224463.json periods=7,patterns=6,workers=6 34/365 90/7 INFEASIBLE 0.0489/5.3493/5.4550/0.0406
staff/special-09-problem_specific-1685329192.json periods=7,patterns=5,workers=3 19/99 32/6 INFEASIBLE 0.0482/0.0239/0.1229/0.0125
edge-0 periods=0,patterns=0,workers=0 0/0 0/0 OPTIMAL 0.0472/0.0002/0.1449/0.0100
edge-1 periods=0,patterns=2,workers=0 0/0 0/0 OPTIMAL 0.0450/0.0003/0.1418/0.0095
edge-2 periods=0,patterns=0,workers=2 2/1 3/2 OPTIMAL 0.0429/0.0003/0.1325/0.0102
edge-3 periods=1,patterns=0,workers=2 3/1 3/2 INFEASIBLE 0.0453/0.0001/0.0881/0.0090
edge-4 periods=1,patterns=1,workers=2 0/0 0/0 INFEASIBLE 0.0410/0.0016/0.0831/0.0090
edge-5 periods=1,patterns=1,workers=1 2/1 2/2 OPTIMAL 0.0411/0.0006/0.1341/0.0082
edge-6 periods=1,patterns=1,workers=0 0/0 0/0 OPTIMAL 0.0429/0.0002/0.1365/0.0090
edge-7 periods=1,patterns=1,workers=2 4/6 6/3 OPTIMAL 0.0431/0.0007/0.1386/0.0089
edge-8 periods=1,patterns=2,workers=2 6/15 6/3 OPTIMAL 0.0444/0.0011/0.1379/0.0088
edge-9 periods=1,patterns=1,workers=3 6/11 8/4 OPTIMAL 0.0454/0.0009/0.1442/0.0092
edge-10 periods=1,patterns=1,workers=3 6/7 8/4 INFEASIBLE 0.0464/0.0008/0.0914/0.0091
edge-11 periods=3,patterns=3,workers=2 8/28 9/4 INFEASIBLE 0.0413/0.0029/0.0879/0.0094
edge-12 periods=3,patterns=3,workers=3 11/40 16/5 OPTIMAL 0.0447/0.0050/0.1437/0.0095
x3c-canonical universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0444/1.2523/1.3922/0.0110
x3c/random-000-bernoulli-3467994108.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0439/0.3644/0.5085/0.0091
x3c/random-001-bernoulli-3468098837.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0430/0.1049/0.2450/0.0102
x3c/random-002-bernoulli-3468203566.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0429/0.1229/0.2606/0.0094
x3c/random-003-bernoulli-3468308295.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0413/0.1259/0.2615/0.0084
x3c/random-004-bernoulli-3468413024.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0421/0.3029/0.4390/0.0080
x3c/random-005-bernoulli-3468517753.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0447/0.3990/0.5644/0.0090
x3c/random-006-bernoulli-3468622482.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0458/0.1782/0.3220/0.0099
x3c/random-007-bernoulli-3468727211.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0467/0.4830/0.6143/0.0090
x3c/random-008-bernoulli-3468831940.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0385/0.6272/0.7676/0.0075
x3c/random-009-bernoulli-3468936669.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0465/1.2245/1.3659/0.0091
x3c/random-010-fixed_cardinality-3469041398.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0491/0.1154/0.2813/0.0088
x3c/random-011-fixed_cardinality-3469146127.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0449/0.0185/0.1634/0.0092
x3c/random-012-fixed_cardinality-3469250856.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0434/0.3155/0.4599/0.0084
x3c/random-013-fixed_cardinality-3469355585.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0477/1.0625/1.2104/0.0101
x3c/random-014-fixed_cardinality-3469460314.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0444/1.0351/1.1794/0.0095
x3c/random-015-fixed_cardinality-3469565043.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0479/0.4312/0.5803/0.0094
x3c/random-016-fixed_cardinality-3469669772.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0446/0.1550/0.2988/0.0089
x3c/random-017-fixed_cardinality-3469774501.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0481/0.5701/0.7217/0.0082
x3c/random-018-fixed_cardinality-3469879230.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0499/0.3776/0.5276/0.0095
x3c/random-019-fixed_cardinality-3469983959.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0473/0.5925/0.7431/0.0092
x3c/random-020-interval-3470088688.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0482/0.3132/0.4638/0.0092
x3c/random-021-interval-3470193417.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0439/0.4146/0.5650/0.0079
x3c/random-022-interval-3470298146.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0448/0.3597/0.5057/0.0095
x3c/random-023-interval-3470402875.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0454/0.7005/0.8624/0.0091
x3c/random-024-interval-3470507604.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0460/0.7750/0.9181/0.0102
x3c/random-025-interval-3470612333.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0459/0.1633/0.3095/0.0106
x3c/random-026-interval-3470717062.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0495/0.2391/0.3924/0.0099
x3c/random-027-interval-3470821791.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0462/0.1311/0.2821/0.0102
x3c/random-028-interval-3470926520.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0497/0.0108/0.1628/0.0111
x3c/random-029-interval-3471031249.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0468/0.0384/0.1858/0.0096
x3c/random-030-laminar-3471135978.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0480/0.0179/0.1662/0.0080
x3c/random-031-laminar-3471240707.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0484/0.4939/0.6391/0.0093
x3c/random-032-laminar-3471345436.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0434/0.2368/0.3796/0.0082
x3c/random-033-laminar-3471450165.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0458/0.3930/0.5377/0.0094
x3c/random-034-laminar-3471554894.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0461/0.1566/0.3053/0.0092
x3c/random-035-laminar-3471659623.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0483/0.6383/0.7850/0.0092
x3c/random-036-laminar-3471764352.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0470/0.2331/0.3787/0.0090
x3c/random-037-laminar-3471869081.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0465/0.0629/0.2092/0.0088
x3c/random-038-laminar-3471973810.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0448/0.0598/0.2089/0.0088
x3c/random-039-laminar-3472078539.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0462/0.2331/0.3782/0.0100
x3c/random-040-partition-3472183268.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0482/0.8204/0.9760/0.0087
x3c/random-041-partition-3472287997.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0505/0.2330/0.3847/0.0083
x3c/random-042-partition-3472392726.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0494/0.1327/0.2803/0.0113
x3c/random-043-partition-3472497455.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0514/0.0119/0.1635/0.0084
x3c/random-044-partition-3472602184.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0471/0.4734/0.6159/0.0093
x3c/random-045-partition-3472706913.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0484/0.2572/0.4061/0.0106
x3c/random-046-partition-3472811642.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0422/0.5510/0.7002/0.0103
x3c/random-047-partition-3472916371.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0423/0.1748/0.3042/0.0085
x3c/random-048-partition-3473021100.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0382/0.0729/0.2098/0.0079
x3c/random-049-partition-3473125829.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0453/0.1172/0.2666/0.0095
x3c/random-050-sunflower-3473230558.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0443/0.7338/0.8750/0.0091
x3c/random-051-sunflower-3473335287.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0445/0.5372/0.6800/0.0084
x3c/random-052-sunflower-3473440016.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0442/0.1884/0.3333/0.0075
x3c/random-053-sunflower-3473544745.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0448/0.0766/0.2237/0.0091
x3c/random-054-sunflower-3473649474.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0402/0.1342/0.2607/0.0090
x3c/random-055-sunflower-3473754203.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0394/0.2980/0.4320/0.0082
x3c/random-056-sunflower-3473858932.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0401/0.2368/0.3878/0.0073
x3c/random-057-sunflower-3473963661.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0470/0.1188/0.2658/0.0080
x3c/random-058-sunflower-3474068390.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0483/0.6454/0.7883/0.0099
x3c/random-059-sunflower-3474173119.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0450/0.0913/0.2363/0.0069
x3c/random-060-sparse_overlap-3474277848.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0486/0.0505/0.1976/0.0099
x3c/random-061-sparse_overlap-3474382577.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0438/0.0212/0.1647/0.0077
x3c/random-062-sparse_overlap-3474487306.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0434/0.0553/0.1916/0.0091
x3c/random-063-sparse_overlap-3474592035.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0433/0.3434/0.4861/0.0068
x3c/random-064-sparse_overlap-3474696764.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0465/0.5272/0.6735/0.0086
x3c/random-065-sparse_overlap-3474801493.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0445/0.0453/0.1855/0.0082
x3c/random-066-sparse_overlap-3474906222.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0428/0.0551/0.1939/0.0087
x3c/random-067-sparse_overlap-3475010951.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0452/0.6835/0.8276/0.0084
x3c/random-068-sparse_overlap-3475115680.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0443/0.0727/0.2130/0.0085
x3c/random-069-sparse_overlap-3475220409.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0449/0.1773/0.3198/0.0083
x3c/random-070-dense_overlap-3475325138.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0440/0.0352/0.1764/0.0090
x3c/random-071-dense_overlap-3475429867.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0432/0.0144/0.1573/0.0080
x3c/random-072-dense_overlap-3475534596.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0456/0.7708/0.9121/0.0089
x3c/random-073-dense_overlap-3475639325.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0453/0.4597/0.5924/0.0092
x3c/random-074-dense_overlap-3475744054.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0392/0.0477/0.1746/0.0081
x3c/random-075-dense_overlap-3475848783.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0383/0.0719/0.1968/0.0079
x3c/random-076-dense_overlap-3475953512.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0396/0.0201/0.1472/0.0086
x3c/random-077-dense_overlap-3476058241.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0388/0.9789/1.1029/0.0072
x3c/random-078-dense_overlap-3476162970.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0384/0.0313/0.1561/0.0069
x3c/random-079-dense_overlap-3476267699.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0392/0.2033/0.3276/0.0084
x3c/random-080-planted_witness-3476372428.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0398/0.4204/0.5578/0.0072
x3c/random-081-planted_witness-3476477157.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0457/0.1905/0.3315/0.0109
x3c/random-082-planted_witness-3476581886.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0442/0.1016/0.2425/0.0087
x3c/random-083-planted_witness-3476686615.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0424/0.0682/0.2073/0.0076
x3c/random-084-planted_witness-3476791344.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0422/0.1698/0.3051/0.0091
x3c/random-085-planted_witness-3476896073.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0436/0.5430/0.6967/0.0099
x3c/random-086-planted_witness-3477000802.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0534/0.0461/0.1867/0.0134
x3c/random-087-planted_witness-3477105531.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0386/0.1815/0.3278/0.0069
x3c/random-088-planted_witness-3477210260.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0478/0.3143/0.4807/0.0094
x3c/random-089-planted_witness-3477314989.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0462/0.5656/0.7158/0.0216
x3c/special-00-minimum_legal-3469094102.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0455/0.0559/0.2081/0.0081
x3c/special-01-empty_or_zero_constraints-3469098840.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0560/0.2246/0.3661/0.0138
x3c/special-02-single_element-3469203569.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0451/0.6711/0.8036/0.0070
x3c/special-03-obvious_yes-3469308298.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0385/0.0196/0.1437/0.0070
x3c/special-04-obvious_no-3469413027.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0443/0.7015/0.8310/0.0089
x3c/special-05-bound_equality-3469517756.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0398/0.1079/0.2591/0.0076
x3c/special-06-bound_adjacent-3469622485.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0487/0.4816/0.6183/0.0089
x3c/special-07-symmetric_multiple_optima-3469727214.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0394/0.3547/0.5006/0.0076
x3c/special-08-repeated_or_tied-3469831943.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0488/0.0969/0.2514/0.0086
x3c/special-09-problem_specific-3469936672.json universe=9,subsets=7 34/213 24/5 OPTIMAL 0.0580/0.1752/0.3202/0.0161
upstream-edge-0 universe=6,subsets=3 14/46 8/4 INFEASIBLE 0.0469/0.0071/0.0944/0.0118
upstream-edge-1 universe=0,subsets=0 0/0 0/0 OPTIMAL 0.0390/0.0003/0.1233/0.0078
upstream-edge-2 universe=3,subsets=2 3/3 8/4 OPTIMAL 0.0377/0.0027/0.1449/0.0072
raw.py
import json,time
from ortools.sat.python import cp_model

def solve(q):
    start=time.monotonic(); model=cp_model.CpModel(); x=[model.new_bool_var(f'x{i}') for i in range(q.n)]; terms=[]; aux=0
    for (i,j),v in q.q.items():
        if i==j:terms.append(v*x[i])
        else:
            y=model.new_bool_var(f'p{i}_{j}');model.add_multiplication_equality(y,[x[i],x[j]]);terms.append(v*y);aux+=1
    if q.decision:model.add(sum(terms)<=-q.c)
    else:model.minimize(sum(terms))
    solver=cp_model.CpSolver();solver.parameters.max_time_in_seconds=15;solver.parameters.num_search_workers=1;solver.parameters.linearization_level=2;solver.parameters.random_seed=1
    constructed=time.monotonic();status=solver.solve(model);end=time.monotonic()
    result=dict(status=solver.status_name(status),solver_model_seconds=constructed-start,solve_seconds=end-constructed,variables=q.n,couplings=aux,solver_auxiliaries=aux,coefficient_max=max(map(abs,q.q.values()),default=0),coefficient_bits=max(map(abs,q.q.values()),default=0).bit_length(),constant=q.c)
    result['search_statistics']=solver.response_stats()
    if status in (cp_model.OPTIMAL,cp_model.FEASIBLE):
        bits=[int(solver.value(v)) for v in x];result.update(bits=bits,energy=q.energy(bits))
    return result

if __name__=='__main__':
    from prototypes import Q
    q=Q();x=q.var();q.add(x,x,-3);q.finish();r=solve(q)
    assert r['status']=='OPTIMAL' and r['energy']==-3
    q.decision=True;q.c=4;r=solve(q);assert r['status']=='INFEASIBLE'
    print('raw validator self-checks passed')
production.py
import json,time,itertools,sys
from inventory import ROOT,OUT,dimensions
from screen import cli
from prototypes import Q,staff
from raw import solve
folder=OUT/'production';folder.mkdir(exist_ok=True)

def paths(name):
 p=cli(['path',name,'DecisionQUBO/i64','--limit','all']);(folder/(name+'-paths.json')).write_text(json.dumps(p,indent=2));return p['paths']
def route(name,through):
 p=next(p for p in paths(name) if [s['to']['name'] for s in p['path']]==through)
 f=folder/(name+'-'+str(len(through))+'-route.json');f.write_text(json.dumps(p));return f
sr=route('StaffScheduling',['DecisionQUBO'])
xr=route('ExactCoverBy3Sets',['StaffScheduling','DecisionQUBO'])
xd=route('ExactCoverBy3Sets',['DecisionQUBO'])
mid=folder/'x3c-staff-route.json';mid.write_text(json.dumps(cli(['path','ExactCoverBy3Sets','StaffScheduling','--limit','all'])['paths'][0]))
inv=json.loads((OUT/'inventory.json').read_text())
cases=[('model-canonical',cli(['create','--example','StaffScheduling']),True,sr)]
cases += [('rule-canonical',cli(['create','--example','StaffScheduling','--to','DecisionQUBO/i64']),True,sr)]
cases += [(r['truth']['instance'],json.loads((ROOT/r['truth']['instance']).read_text()),r['truth']['value'],sr) for r in inv if r['truth']['problem']=='StaffScheduling']
cases += [('edge-'+str(i),r['source'],r['truth'],sr) for i,r in enumerate(json.loads((OUT/'staff-edge-results.json').read_text()))]
xt=[json.loads(l) for l in (ROOT/'ground-truth/set/ExactCoverBy3Sets.jsonl').read_text().splitlines()]
xcs=[('x3c-canonical',cli(['create','--example','ExactCoverBy3Sets']),True)]+[(t['instance'],json.loads((ROOT/t['instance']).read_text()),t['value']) for t in xt]
cases += [(label,source,truth,xr) for label,source,truth in xcs]
for idx,(label,source,truth,routefile) in enumerate(cases):
 base_start=time.monotonic();base=cli(['solve','-','--timeout','15'],source);base_secs=time.monotonic()-base_start
 start=time.monotonic();b=cli(['reduce','-','--via',str(routefile)],source);constructed=time.monotonic()
 d=b['target']['data'];q=Q();q.decision=True;q.n=d['inner']['num_vars'];q.q={(i,j):v for i,j,v in d['inner']['entries']};q.c=-d['bound'];q.finish()
 model=source if source['type']=='StaffScheduling' else cli(['reduce','-','--via',str(mid)],source)['target']
 p=staff(model['data']);assert (q.n,q.q,q.c)==(p.n,p.q,p.c)
 # The measured workflow excludes the independent coefficient check and baseline.
 raw_start=time.monotonic();r=solve(q)
 assert r['status'] in ('OPTIMAL','INFEASIBLE'),(label,r['status'])
 assert (r['status']=='OPTIMAL')==truth,(label,truth,r['status'])
 agg=cli(['extract','-','--value='+json.dumps(truth)],b);assert agg['value']==truth
 if truth:
  ext=cli(['extract','-','--config',json.dumps([bool(v) for v in r['bits']])],b);assert ext['evaluation']=='Or(true)'
  ev=cli(['evaluate','-','--config',json.dumps(ext['solution'])],source);assert ev['result']=='Or(true)'
  r['source_witness']=ext['solution']
 else:r['source_witness']=None
 r.update(case=label,source_type=source['type'],truth=truth,source_dimensions=dimensions(source['type'],source['data']) if source['type']=='StaffScheduling' else dict(universe=source['data']['universe_size'],subsets=len(source['data']['subsets'])),source_seconds=base_secs,source_solve=base,construction_seconds=constructed-start,full_seconds=constructed-start+time.monotonic()-raw_start,aggregate=agg,coefficient_equivalence=True)
 (folder/f'case-{idx}-bundle.json').write_text(json.dumps(b))
 with (folder/'results.jsonl').open('a') as f:f.write(json.dumps(r)+'\n')
 print(idx,label,r['status'],round(r['solve_seconds'],3),flush=True)
# Existing X3C direct route is a same-input comparison, not a claimed improvement.
for label,source,truth in xcs:
 start=time.monotonic();b=cli(['reduce','-','--via',str(xd)],source);constructed=time.monotonic();d=b['target']['data'];q=Q();q.decision=True;q.n=d['inner']['num_vars'];q.q={(i,j):v for i,j,v in d['inner']['entries']};q.c=-d['bound'];q.finish();r=solve(q)
 assert r['status'] in ('OPTIMAL','INFEASIBLE') and (r['status']=='OPTIMAL')==truth
 agg=cli(['extract','-','--value='+json.dumps(truth)],b);assert agg['value']==truth
 if truth:assert cli(['extract','-','--config',json.dumps([bool(v) for v in r['bits']])],b)['evaluation']=='Or(true)'
 r.update(case=label,construction_seconds=constructed-start,full_seconds=time.monotonic()-start)
 with (folder/'x3c-direct-results.jsonl').open('a') as f:f.write(json.dumps(r)+'\n')
# Native CLI solver on the rule fixture and a NO fixture, with completed recovery.
for idx in [1,113]:
 b=json.loads((folder/f'case-{idx}-bundle.json').read_text());sol=cli(['solve','-','--solver','brute-force'],b)
 (folder/f'cli-solve-{idx}.json').write_text(json.dumps(sol,indent=2))
 print('native CLI solve',idx,sol,flush=True)
print('ALL PRODUCTION CASES PASS',len(cases),flush=True)
upstream_boundaries.py
import json,itertools,time
from inventory import OUT
from screen import cli
from prototypes import Q,staff
from raw import solve
folder=OUT/'production';route=folder/'ExactCoverBy3Sets-2-route.json';mid=folder/'x3c-staff-route.json'
cases=[(6,[[0,1,2],[0,3,4],[1,4,5]]),(0,[]),(3,[[0,1,2],[0,1,2]])]
for idx,(n,sets) in enumerate(cases):
 truth=any(all(sum(bit for bit,S in zip(bits,sets) if u in S)==1 for u in range(n)) and sum(bits)==n//3 for bits in itertools.product([0,1],repeat=len(sets)))
 source=dict(type='ExactCoverBy3Sets',variant={},data=dict(universe_size=n,subsets=sets))
 t=time.monotonic();base=cli(['solve','-','--timeout','15'],source);base_seconds=time.monotonic()-t
 t=time.monotonic();b=cli(['reduce','-','--via',str(route)],source);construction=time.monotonic()-t
 d=b['target']['data'];q=Q();q.n=d['inner']['num_vars'];q.q={(i,j):v for i,j,v in d['inner']['entries']};q.c=-d['bound'];q.decision=True;q.finish()
 middle=cli(['reduce','-','--via',str(mid)],source)['target'];p=staff(middle['data']);assert (q.n,q.q,q.c)==(p.n,p.q,p.c)
 t=time.monotonic();r=solve(q);assert r['status'] in ['OPTIMAL','INFEASIBLE'] and (r['status']=='OPTIMAL')==truth
 agg=cli(['extract','-','--value='+json.dumps(truth)],b);assert agg['value']==truth
 if truth:
  recovered=cli(['extract','-','--config',json.dumps([bool(x) for x in r['bits']])],b);assert recovered['evaluation']=='Or(true)'
  assert cli(['evaluate','-','--config',json.dumps(recovered['solution'])],source)['result']=='Or(true)'
 r.update(case='upstream-edge-'+str(idx),source_type='ExactCoverBy3Sets',truth=truth,source_dimensions=dict(universe=n,subsets=len(sets)),source_seconds=base_seconds,source_solve=base,construction_seconds=construction,full_seconds=construction+time.monotonic()-t,aggregate=agg,coefficient_equivalence=True)
 (folder/f'upstream-edge-{idx}-bundle.json').write_text(json.dumps(b))
 with (folder/'results.jsonl').open('a') as f:f.write(json.dumps(r)+'\n')
 print(idx,r['status'],r['solve_seconds'])

@isPANN

isPANN commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Agentic Review Report

Verdict: PASS for the reviewed code and feature workflow; no unresolved findings. Final paper and coverage gates remain the implementer's responsibility and were still pending when this report was written.

Review scope: the uncommitted Phase 4 changes relative to 109985f4103d4c4785f60b8a34007580cd7f2434, including the two new staffing rule/test files. Inherited Phase 1–3 changes were excluded. I read and applied simplify and inspected the diff before loading how-to-review; no material simplification was needed. No repository files were edited by the reviewer.

Structural and semantic

PASS. The new edge is StaffScheduling -> DecisionQUBO<i64> with integer multiplicity witnesses and Or -> Or values. Registration, canonical example, native tests, executable parameter-formula coverage, and manual theorem are present. The only shared-code change exposes the existing bounded-binary-weight helper to sibling rules. No generated exports, dependencies, models, or core contracts changed.

I traced the budget/coverage squares, omitted constant, decision threshold, and extraction against the actual source evaluator. The construction preserves multiplicities, duplicate and empty patterns, empty schedule/period collections, arbitrary signed requirements including i64::MIN, zero budgets, and impossible coverage. Successful bounded-block decoding cannot overflow; the conservative source-fold guard and builder's separately checked coefficient sums protect source and target arithmetic. Registered upper bounds cover all branches. The canonical instance has 11 target bits, 40 nonzero off-diagonal terms, constant 21, and satisfying counts [1,1,1].

All ten Phase 4 candidates are accounted for in research.md and measurements.md. Only staffing ships. JobShopScheduling, OpenShopScheduling, FlowShopScheduling, PreemptiveScheduling, SequencingWithDeadlinesAndSetUpTimes, SequencingToMinimizeMaximumCumulativeCost, ProductionPlanning, RegisterSufficiency, and RootedTreeStorageAssignment remain explicitly deferred. Prototype failures and unattempted cases are not presented as verified reductions. Staffing adds a first outgoing route; no source-solver speedup or improvement over the existing direct X3C route is claimed.

Quality and evidence

PASS. The native tests first failed because the direct edge was absent, then passed. They compare independently brute-forced source feasibility with exhaustive target assignments, check every satisfying target's decoded source witness, reject malformed and unsatisfying targets, exercise multiplicity and signed/empty cases, and check typed overflow and both completed decision values. make check passed formatting, clippy, and 7,134 tests/doctests.

I inspected the mathematical certificate and verification scripts/results: 290,847 constructor checks, independent adversary coverage including 7,086 source/target decision comparisons and 51,400 assignment identity/extraction checks, and 124 shared polynomial comparisons with zero disagreements. These supplement the proof rather than establish universal correctness by count.

The production evidence is complete: 219/219 raw target decisions, zero timeouts, consisting of 115 staffing cases (100 corpus, 13 boundaries, two canonical) and 104 incoming X3C cases (100 corpus, one canonical, three generated boundaries). The X3C corpus is all YES; a separate independently enumerated intersecting-triples instance verifies the actual NO chain. Every emitted polynomial matches the verified prototype; completed values and every YES witness recover to the original source. The fixed raw solver sees only polynomial products and the emitted threshold: 15 seconds, one worker, linearization level 2, seed 1. Slowest solve: 13.9043 seconds. The existing direct X3C baseline also completes all 101 canonical/corpus inputs and is materially smaller/faster. The corpus integrity record reports all 32,372 files unchanged.

Feature test

PASS. I ran make cli and used the newly installed /Users/xiweipan/.cargo/bin/pred from scratch for 21 commands: list/show, rule-example creation, inspection, measured path, reduction, default and brute-force source/target solves, exact canonical witness recovery, a collective NO result, both aggregate values, and malformed/unsatisfying witness rejection. Evidence is in reviewer-cli-install.log, reviewer_cli.py, and reviewer-cli/transcript.json.

Separate arithmetic oracle/recovery evidence, not additional raw solves: with no schedule patterns or periods, budget 2,322,860,046 constructs successfully and its 32-bit all-true budget-slack witness recovers the valid empty source witness; budget 2,322,860,047 returns a typed reduction overflow because the final negative coefficient sum exceeds i64, even though individual coefficients and the constant fit.

Findings

No unresolved Critical, Important, or Minor findings.

Resolved Minor: docs/paper/reductions.typ:12295 originally described coefficient-update count as total construction work. The implementer changed the wording to coefficient updates/row bookkeeping and explicitly noted the ordered-map logarithmic factor; I verified the correction.

Not checked here: final paper/coverage results, hardware embedding, or larger-instance performance. The 13.9-second worst case has limited margin under the fixed 15-second gate; conservative arithmetic guards can reject representable special cases.

@isPANN isPANN changed the title Add Phase 4 direct QUBO rule for integer staff scheduling Add staff-scheduling QUBO and tighten direct-reduction bounds Oct 23, 2026
@isPANN
isPANN marked this pull request as ready for review October 27, 2026 12:57
isPANN and others added 14 commits October 27, 2026 10:02
Each ILP rule's num_nonzeros upper bound was num_constraints * num_vars,
a dense-matrix bound that the corpus measured at under half of its
prediction on 55 rules. Derive the bound from each reduce_to's constraint
families instead, summing the terms each family contributes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Canonical examples are sparse, so a constraint family missing from a
per-family nonzero count can go unnoticed. Reduce dense instances of five
rules with the most constraint families and require the measured count to
stay within the declared bound.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
StaffScheduling -> DecisionQUBO assumed 63 bits per worker count, and
SequencingWithReleaseTimesAndDeadlines -> DecisionQUBO assumed every task
may start anywhere in [0, time_horizon]. Neither source exposed the
quantity the construction actually depends on, so the bounds ran three to
four orders of magnitude above the corpus measurements.

Add num_workers_bits to StaffScheduling and max_start_slots to
SequencingWithReleaseTimesAndDeadlines, state both QUBO bounds in them,
and declare the new fields in the two rules that target these models.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sparse-support test kept the 63-bit StaffScheduling budget, so a
regression to the old formula would still pass. Tighten it to the new
prediction, add an SWRTD case, and restate both proofs' size bounds in
the new parameters, including the two incoming rules that declare them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bound K counts selected columns, but try_new accepted any negative
value. Such an instance panicked in parameters(), whose bound must fit
u64, and its ILP reduction cast the bound with `as usize`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The corpus measured 81 non-trivial upper-bound fields at exactly their
prediction on every instance. Of those, 46 fields in 30 rules are exact
by construction: every row family and variable block is emitted
unconditionally, the source invariants (simple graphs, exactly-K
clauses, n divisible by 3, 1 <= k <= n) rule out merged or dropped
terms, and no early return builds a different target.

The rest stay upper bounds: degenerate-input shortcuts, data-dependent
zero coefficients or cancelled literals, and QUBO entries where penalty
and objective terms can cancel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
KSatisfiability<K3>::new_allow_less admits clauses with fewer than three
literals, so 3SAT -> KClique num_vertices and 3SAT -> DecisionMVC
num_edges shrink below their formulas. Return both to upper_bound, pin
the relations with short-clause instances, and lay out the transform
declarations by nesting depth.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The exact claims for HamiltonianCircuit, MaximalIS, MinimumDominatingSet,
MaximumMatching, Kernel, PartitionIntoPathsOfLength2,
PartitionIntoPerfectMatchings and PartitionIntoCliques were checked only
on canonical examples. Pin them on empty, path and complete graphs with
up to three vertices, k = 1 and k = n, and antiparallel arcs.

Also promote MinimumVertexCover -> EnsembleComputation num_subsets, one
subset per edge, and convert the ConsecutiveOnesSubmatrix bound with
try_from now that construction keeps it nonnegative.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxCut<SimpleGraph, i64> accepts negative weights, but the rule rejected
them, so every planned path through it (for example Max2SAT -> MaxCut ->
MinimumMatrixCover) failed on ordinary inputs. Replace each negative edge
{u, v} by an auxiliary vertex joined to v with |w| + 1 and to u with |w|:
every maximum cut separates the auxiliary vertex from v, so the gadget
shifts the cut by the constant 2|w| + 1 and preserves maximum cuts.
Extraction drops the auxiliary bits; num_rows becomes an upper bound.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ConsecutiveOnesSubmatrix stores its column-count bound as usize, so
  serde and the CLI reject negatives by type and the model and ILP rule
  drop their conversions.
- The MaxCut -> MinimumMatrixCover gadget computes |w| + 1 as 1 - w with
  one checked subtraction; module docs and the paper's duplicate
  extraction paragraph now describe the auxiliary vertices.
- The PartitionIntoPathsOfLength2 comment states why its counts are exact.
- The degenerate-graph test covers every valid k; transform layouts are
  consistent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
KSatisfiability<KN> was Satisfiability under another name: it checked no
clause width and its only edges were identity casts into and out of it.
Every path from K2 or K3 paid an extra hop through it.

- Drop the KN variant; K2 and K3 now reduce directly to Satisfiability and
  K3 is the default variant.
- Remove new_allow_less/try_new_allow_less. K2 and K3 always hold exactly K
  literals per clause, matching what serde and the CLI already enforced.
- Delete the short- and empty-clause branches these constructors forced on
  3-SAT rules, and declare 3SAT -> KClique num_vertices and
  3SAT -> DecisionMVC num_edges exact again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e text

SAT -> NAE turns every three-literal clause into a four-literal one, so the
MonochromaticTriangle vertex, edge and triangle counts are equalities and its
two-literal branch was dead. Also remove leftover short- and empty-clause
wording and dead padding in OneInThree, QuadraticCongruences and Kernel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Add a FixedK sub-trait implemented by K1..K5, and bound KSatisfiability
  and the SAT/K-SAT results by it, so KSatisfiability<KN> no longer
  type-checks instead of failing at construction.
- Register each K-SAT aggregate mapping inside the macro that registers its
  witness edge, and take the SAT -> K-SAT clause width from the type.
- Allocate CyclicOrdering's fresh padding variables with allocate_many.
- Drop leftover braces in two tests and a stale QuadraticCongruences note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Removing the KSatisfiability KN variant does not need a new variant trait.
Keep KSatisfiability bounded by KValue with the runtime width check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@GiggleLiu GiggleLiu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agentic Review Report

PR #1217: Add staff-scheduling QUBO and tighten direct-reduction bounds

Review date: 2026-10-29. Fresh-context read-only review of 109985f4103d4c4785f60b8a34007580cd7f2434...3d3b020c0d93f5e27ecce98eaf148321a7eced9d. Used the PR's own stack base, not origin/main. Local implementation evidence is diff-1217.patch; GitHub context is context-1217.txt in this directory.

Verdict: No new blocking finding in this diff. Hold stack integration until #1214 is fixed.

Structural and semantic review

Reviewed StaffScheduling, the replacement OLA and ThreePartition encodings, revised coupling bounds, intentional SCS removal, helper/test updates, and manual changes against the exact #1216 base.

StaffScheduling represents each schedule count in [0,W], plus a budget slack in [0,W] and period surplus in [0,W−r_t]. Bounded binary weights sum to the bound and represent every integer in that range. Its equations are total-count + budget-slack = W and coverage − surplus = r_t. Negative requirements impose no condition; r_t > W gives an explicit NO target. Checked decoded sums and source accumulation limits.

OLA uses n(n−1)/2 comparison variables. For an edge {u,v}, distance equals one plus the number of other vertices between its endpoints; each contribution is the XOR of the relevant comparison bits. The objective is nonnegative on every bit assignment. Triangle penalties with weight m(n−1)+1 therefore force a transitive total order.

ThreePartition enumerates eligible value triples and encodes bounded multiplicities, then enforces the exact count of each distinct value. Extraction consumes distinct indexed copies. Counting incidences ensures n/3 triples. For multiplicity bounds M_t, 3*sum(M_t) <= choose(n,2), giving N <= n(n−1)/6. For a value row v, complementary pairs are disjoint value classes, so its bit degree D_v <= n/2 and sum(D_v) <= 3N. Thus sum(choose(D_v,2)) <= n(n−1)(n−2)/8, validating the coupling bound.

Recomputed tightened bounds for cyclic ordering, shortest superstring, and StackerCrane from actual products. The SCS rule and its direct-QUBO tests are removed as explicitly documented after failure of the runtime acceptance gate; the existing ILP route remains.

Quality review

The replacement encodings have independent brute-force round trips and infeasible/empty/boundary coverage. Integer multiplicities and bounded remainder bits were checked for non-powers of two. The value-aggregation ThreePartition proof does not assume equal items are interchangeable without tracking their indexed copies during extraction. The author corpus claims were inspected but are not substituted for these derivations.

Feature tests and verification limits

The compiled #1218 descendant retains these exact production changes. direct_qubo --test-threads=2 passed all 44 tests, and staffscheduling_qubo --test-threads=2 passed all 4 tests. CLI discovery/example/inspect/direct-path/reduce workflows and independent source/target brute-force solves passed for StaffScheduling, OLA, and ThreePartition. Each extracted source evaluation equals the source optimum/feasibility. The other retained encodings were also exercised in a total of 24 CLI round trips.

Exact #1217 full make/test/doc/coverage commands were not rerun; shared resources were used for the descendant tests, CLI, and numeric boundary probe. GitHub checks were absent from the collected stack metadata; author local checks are recorded only as author claims.

Findings (severity order)

No new blocking finding. The #1214 interval allocation panic remains inherited and blocks stack integration until repaired. The intentional SCS removal is within this PR's stated performance scope, not an unrelated deletion.

GitHub PR Audit — #1217

GATE: PASS

Harm signals (blocking)

  • harm.secrets (security.no-secrets): OK — inspected added text; no credential/key/token or private-data additions found.
  • harm.dangerous-files (git.harm-signals): OK — changed files are scoped source, tests, and manual material; no conflict markers, unexplained binaries/archives, scratch files, or local configuration.
  • harm.generated-artifacts (code.generated-artifacts): OK — no generated example/schema/reduction-graph exports or build/dependency outputs are committed.
  • harm.cache (code.generated-cache): OK — no cache directories added.
  • harm.debug-leftovers (code.debug-leftovers): OK — no debugger, debug print, temporary probe, or WIP instrumentation in added production code.

PR legibility (advisory)

  • pr.description-quality (github.pr-description-quality): OK — body describes scope, purpose, validation, and limits; comments and reviews were read with gh-axi pr view --full --comments --reviews.
  • pr.traceable (github.prs-traceable): OK — purpose and validation are stated and the series/issue context is identifiable.

Harm gate PASS does not clear correctness findings. No tracked file, commit, remote branch, PR comment, review, or merge was modified by this reviewer.

[Critical] Remove KSatisfiability KN variant and short-clause constructors
Declare exact parameter counts that constructions always produce
Tighten ILP num_nonzeros bounds to per-constraint counts
Tighten StaffScheduling and SWRTD QUBO size bounds
Add binary decision ILP to SAT and fix solution validators

@GiggleLiu GiggleLiu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agentic Review Report: stack consolidation

Approved current head 2cd3218cec051e6966e0ea27524a65ba87aefe68 for merging into issue-1213-direct-qubo-phase3 only. This contains the previously reviewed changes and, where applicable, the preceding recorded backend merges. The numeric blockers in #1214 and #1224 are fixed and reviewed at 2226ebec4 and 52bd171a0.

Rechecked inline comments, submitted reviews, PR conversation, linked-issue comments, and Codecov comments. No new unaddressed material issue. Harm gate remains PASS from the independent reviews. The old roadmap draft/hold instructions predate the user's current authorization; deferred rules and #1219/#1220/#1221 remain excluded.

The repaired aggregate passed 7,194 native tests, formatting, all-target/all-feature Clippy, and make paper at d96e7c8b8; the final website gate subsequently passed all 336 browser cases at 77774797e. These are aggregate gates, including the separately reviewed integration fixtures, not a claim that this branch head was independently retested unchanged. Required main-branch CI remains pending. This feature-branch merge preserves the individual PR record.

@GiggleLiu
GiggleLiu merged commit 5e137d3 into issue-1213-direct-qubo-phase3 Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants