optimizing a gate level bcm to the end of the earth and back

Fix cost model to count ALL gate inputs (AND + OR)

- Total cost = AND gate inputs + OR gate inputs
- MaxSAT now optimizes for total cost, not just AND inputs
- Updated default target to 61 (no-sharing baseline)

Results:
No sharing: 61 total (33 AND + 28 OR)
Optimized: 52 total (15 AND + 37 OR)
Savings: 9 gate inputs (15% reduction)

Sharing reduces AND inputs dramatically (33→15) but increases
OR inputs (28→37) since shared terms fan out to more outputs.

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

dunkirk.sh ed0c03f8 5443f07c

verified
+12 -9
+2 -2
bcd_optimization/cli.py
··· 27 27 parser.add_argument( 28 28 "--target", 29 29 type=int, 30 - default=23, 31 - help="Target gate input count to beat (default: 23)", 30 + default=61, 31 + help="Target gate input count to beat (default: 61, the no-sharing baseline)", 32 32 ) 33 33 parser.add_argument( 34 34 "--exact",
+10 -7
bcd_optimization/solver.py
··· 132 132 cost_breakdown = self._compute_cost_breakdown(selected, implicants_by_output) 133 133 134 134 return SynthesisResult( 135 - cost=cost_breakdown.and_inputs, # Primary cost = AND inputs only 135 + cost=cost_breakdown.total, # Total = AND inputs + OR inputs 136 136 implicants_by_output=implicants_by_output, 137 137 shared_implicants=shared, 138 138 method="greedy", ··· 181 181 f"No implicant covers {segment}:{minterm}" 182 182 ) 183 183 184 - # Soft constraints: penalize each implicant by its AND gate cost 185 - # Single-literal terms (direct wires) have 0 AND cost 186 - # Multi-literal terms cost their literal count (AND gate inputs) 184 + # Soft constraints: penalize each implicant by its total gate input cost 185 + # Cost = AND inputs + OR inputs 186 + # - AND inputs: num_literals if >= 2, else 0 (single literals are wires) 187 + # - OR inputs: one per output this implicant covers 187 188 for i, impl in enumerate(self.prime_implicants): 188 189 and_cost = impl.num_literals if impl.num_literals >= 2 else 0 189 - if and_cost > 0: 190 - wcnf.append([-impl_vars[i]], weight=and_cost) 190 + or_cost = len(impl.covered_minterms) # Number of outputs it feeds 191 + total_cost = and_cost + or_cost 192 + if total_cost > 0: 193 + wcnf.append([-impl_vars[i]], weight=total_cost) 191 194 192 195 # Solve 193 196 with RC2(wcnf) as solver: ··· 222 225 cost_breakdown = self._compute_cost_breakdown(selected, implicants_by_output) 223 226 224 227 return SynthesisResult( 225 - cost=cost_breakdown.and_inputs, # Primary cost = AND inputs only 228 + cost=cost_breakdown.total, # Total = AND inputs + OR inputs 226 229 implicants_by_output=implicants_by_output, 227 230 shared_implicants=shared, 228 231 method="maxsat",