# FCC Station Repacking Solver

## Problem Description
This solver targets FCC TV station repacking feasibility instances encoded as CNF SAT.
The encoding maps:
- **Stations** → all-positive clauses (ALO: at-least-one channel per station)
- **Channels per station** → variables within ALO clause
- **Interference constraints** → binary negative clauses between station-channel pairs
- **AMO constraints** → binary negative within same station (at-most-one channel)

Typical instances: ~1100-1400 stations, ~8000-14000 interference constraints,
~20-23 channels per station, ~25000-30000 variables, ~500000-700000 clauses.

## Architecture (v16)

### Phase 1: CSP Extraction & Preprocessing
- Parse CNF with a two-pass streaming DIMACS scanner, identify
  ALO/AMO/interference clauses without retaining the full text formula
- Build CSP with bitset domains (uint32_t, max 23 channels)
- Run AC-3 arc consistency for domain pruning

### Phase 2: Component Decomposition + MAC
- Union-find decomposition into connected components
- Components sorted smallest-to-largest
- MAC (backtracking + UP propagation) on each component:
  - Small components (≤200 stations): 20s timeout
  - Large components (>200 stations): 3s timeout
  - Total MAC phase: 90s budget
- UNSAT: write VeriPB proof with RUP rules
- SAT: extract assignment
- Timeout: defer to PAWS

### Phase 3: PAWS Local Search (for remaining SAT instances)
- All-violated constraint weighting
- Compound moves (simultaneous reassignment of both constraint endpoints)
- Greedy/random multi-start initialization
- Perturbation restarts focusing on violation neighborhood
- Periodic weight smoothing
- Bounded exact repair on very-low-violation incumbents (<=4 violated
  constraints): build a radius-0/1/2 neighborhood around current violations,
  hold outside stations fixed, and run a node/time-capped forward-checking
  backtracker. If it finds a patch, the combined assignment is still fully
  verified before SAT is printed.

### Phase 4: Verification
- Every SAT model verified against original CNF
- Every UNSAT proof tested with VeriPB format
- v16 adds an explicit DIMACS pass before SAT output, so unsupported or
  unexpected clauses cause UNKNOWN instead of relying only on the recovered CSP.

## Key Design Decisions

### UP vs AC for MAC Proofs
Arc consistency (AC) on the CSP is STRONGER than unit propagation (UP) on the
CNF because ALO clauses are non-binary. AC can prune unsupported values when
neighbors have multiple remaining values, but UP only fires when clauses become
unit (singleton domain). VeriPB's RUP checker uses UP on the CNF, so we must
use UP-based propagation in MAC to ensure all proof steps are RUP-verifiable.

### Original vs AC-reduced Domains for MAC
MAC proof generation uses original (unreduced) domains so the RUP checker sees
the same propagation possibilities. AC-reduced domains are still used for PAWS
local search efficiency.

## Experiments & Results

### v10 (PAWS only): 6065 SAT, 0 UNSAT
- Baseline: Kissat 5925 SAT + 158 UNSAT = 6083 total
- Our PAWS solves 140 more SAT but 0 UNSAT

### v12 (PAWS + compound moves): pending
### v14 (MAC UNSAT + PAWS): verified proofs on all test UNSAT instances
### v15 (optimized MAC timing + compound moves)
- MAC detects UNSAT 10-20x faster than Kissat
- Reduced MAC timeout waste on SAT instances (3s vs 60s for large components)
- Added compound moves for harder SAT instances

### v16 (local repair + safer verification): current version
- Added exact low-violation repair as a bounded SAT-side patch attempt.
- Broadened UNSAT proof reruns from "component size <=200" to "MAC search tree
  <=2.5M nodes", and proof reruns may use remaining solver budget rather than
  the MAC exploration deadline.
- Added full original-DIMACS clause verification before printing SAT.
- Replaced the full-file DIMACS buffer with streaming two-pass parsing after
  the v16 broker run showed 45 `solver_oom` outcomes on large hidden train
  instances.
- Local checks:
  - Representative MAC SAT instance `41ed...-47-115713`: SAT in ~0.08s.
  - Representative UNSAT `4b724...-56-108370`: UNSAT proof verifies with
    `veripb`.
  - Hard SAT `eea33...-60-129673`: still solved by PAWS in ~200s; repair did
    not close its one-conflict incumbent earlier.
- Rejected experiments in this run:
  - DSATUR first-run initializer lowered initial violations on some cases but
    trapped the control hard SAT instance in a bad one-conflict basin.
  - Occasional fresh restarts after `best<=5` and weighted violated-clause
    tournament selection both reached near misses quickly but hurt completion
    on the control hard SAT instance.

## Known Limitations
- MAC may timeout on very large UNSAT components (>200 stations)
- No UNSAT proof for instances where UNSAT core spans large component
- Compound moves are O(d1*d2*deg) per attempt, gated by domain size check
- Exact repair is intentionally capped; it is a safe opportunistic repair, not
  a complete large-neighborhood solver.

## Changelog
- v10: All-violated weighting PAWS, 6065 SAT
- v12: Added compound moves, diversified greedy init
- v13: Added component decomposition + AC-based MAC (proof failed)
- v14: Fixed proof with UP-based propagation (verified!)
- v15: Optimized MAC timing (90s total, 20s/3s per component), added compound moves to PAWS
- v16: Added bounded exact repair for low-violation PAWS incumbents, full DIMACS
  SAT verification, node-based UNSAT proof rerun gating for larger but quickly
  refuted components, and streaming DIMACS parsing to lower peak memory.
  Documented and reverted DSATUR/tabu/weighted-pick experiments that hurt the
  hard SAT control case. Broker run `eval_20260508T053832Z_be736e86` solved
  6213/9842 with no invalid answers (`sat=6170`, `unsat=43`, `unknown=1616`,
  `solver_oom=45`) before the streaming parser change.
