optimizing a gate level bcm to the end of the earth and back
at main 225 lines 7.2 kB view raw
1""" 2Multi-level logic synthesis with arbitrary gate sizes. 3 4This module implements synthesis that minimizes total gate inputs 5by allowing multi-input gates (AND, OR, NAND, NOR, XOR, etc.). 6""" 7 8from dataclasses import dataclass 9from typing import Optional 10from pysat.formula import WCNF, CNF 11from pysat.examples.rc2 import RC2 12from pysat.solvers import Solver 13 14from .truth_tables import SEGMENT_NAMES, SEGMENT_MINTERMS 15 16 17@dataclass 18class Gate: 19 """Represents a gate in the circuit.""" 20 gate_type: str # 'AND', 'OR', 'NAND', 'NOR', 'XOR', 'XNOR', 'NOT', 'BUF' 21 inputs: list[str] # Input signal names 22 output: str # Output signal name 23 24 @property 25 def num_inputs(self) -> int: 26 return len(self.inputs) 27 28 29@dataclass 30class Circuit: 31 """A multi-level circuit.""" 32 gates: list[Gate] 33 outputs: dict[str, str] # output_name -> signal_name 34 35 @property 36 def total_gate_inputs(self) -> int: 37 return sum(g.num_inputs for g in self.gates) 38 39 @property 40 def num_gates(self) -> int: 41 return len(self.gates) 42 43 44def evaluate_gate(gate_type: str, inputs: list[int]) -> int: 45 """Evaluate a gate given its type and input values.""" 46 if gate_type == 'BUF': 47 return inputs[0] 48 elif gate_type == 'NOT': 49 return 1 - inputs[0] 50 elif gate_type == 'AND': 51 return 1 if all(inputs) else 0 52 elif gate_type == 'OR': 53 return 1 if any(inputs) else 0 54 elif gate_type == 'NAND': 55 return 0 if all(inputs) else 1 56 elif gate_type == 'NOR': 57 return 0 if any(inputs) else 1 58 elif gate_type == 'XOR': 59 return sum(inputs) % 2 60 elif gate_type == 'XNOR': 61 return 1 - (sum(inputs) % 2) 62 else: 63 raise ValueError(f"Unknown gate type: {gate_type}") 64 65 66def evaluate_circuit(circuit: Circuit, inputs: dict[str, int]) -> dict[str, int]: 67 """Evaluate a circuit on given inputs.""" 68 signals = dict(inputs) 69 70 for gate in circuit.gates: 71 gate_inputs = [signals[i] for i in gate.inputs] 72 signals[gate.output] = evaluate_gate(gate.gate_type, gate_inputs) 73 74 return {name: signals[sig] for name, sig in circuit.outputs.items()} 75 76 77def verify_circuit(circuit: Circuit) -> tuple[bool, list[str]]: 78 """Verify circuit produces correct 7-segment outputs.""" 79 errors = [] 80 81 for digit in range(10): 82 inputs = { 83 'A': (digit >> 3) & 1, 84 'B': (digit >> 2) & 1, 85 'C': (digit >> 1) & 1, 86 'D': digit & 1, 87 "A'": 1 - ((digit >> 3) & 1), 88 "B'": 1 - ((digit >> 2) & 1), 89 "C'": 1 - ((digit >> 1) & 1), 90 "D'": 1 - (digit & 1), 91 } 92 93 outputs = evaluate_circuit(circuit, inputs) 94 95 for seg in SEGMENT_NAMES: 96 expected = 1 if digit in SEGMENT_MINTERMS[seg] else 0 97 actual = outputs.get(seg, -1) 98 if actual != expected: 99 errors.append(f"Digit {digit}, segment {seg}: expected {expected}, got {actual}") 100 101 return len(errors) == 0, errors 102 103 104def factored_synthesis() -> Circuit: 105 """ 106 Alternative synthesis attempting more factoring. 107 108 Uses the same verified expressions but tries to find common factors. 109 """ 110 # Use the same expressions as optimized_synthesis for correctness 111 # Then we can try to factor further 112 return optimized_synthesis() 113 114 115def optimized_synthesis() -> Circuit: 116 """ 117 Optimized synthesis from verified solver output. 118 119 Uses multi-input gates and maximal sharing. 120 Based on verified expressions: 121 a = B'D' + A + CD + BC'D + B'C + CD' 122 b = B'D' + A + C'D' + CD + B' + B'C 123 c = B'D' + A + C'D' + C' + B + BC'D + CD' + BC' 124 d = B'D' + A + BC'D + B'C + CD' 125 e = B'D' + CD' 126 f = A + C'D' + B + BC'D + BC' 127 g = A + BC'D + B'C + CD' + BC' 128 """ 129 gates = [] 130 131 # Shared product terms (AND gates) - each used by multiple outputs 132 # t_bd = B'D' (used by a,b,c,d,e) - 2 inputs 133 gates.append(Gate('AND', ["B'", "D'"], 't_bd')) 134 135 # t_cd2 = CD' (used by a,c,d,e,g) - 2 inputs 136 gates.append(Gate('AND', ['C', "D'"], 't_cd2')) 137 138 # t_cd1 = C'D' (used by b,c,f) - 2 inputs 139 gates.append(Gate('AND', ["C'", "D'"], 't_cd1')) 140 141 # t_cd = CD (used by a,b) - 2 inputs 142 gates.append(Gate('AND', ['C', 'D'], 't_cd')) 143 144 # t_bcd = BC'D (used by a,c,d,f,g) - 3 inputs 145 gates.append(Gate('AND', ['B', "C'", 'D'], 't_bcd')) 146 147 # t_bc1 = B'C (used by a,b,d,g) - 2 inputs 148 gates.append(Gate('AND', ["B'", 'C'], 't_bc1')) 149 150 # t_bc2 = BC' (used by c,f,g) - 2 inputs 151 gates.append(Gate('AND', ['B', "C'"], 't_bc2')) 152 153 # Segment outputs using multi-input OR gates 154 # a = B'D' + A + CD + BC'D + B'C + CD' (6 terms -> 6 OR inputs) 155 gates.append(Gate('OR', ['t_bd', 'A', 't_cd', 't_bcd', 't_bc1', 't_cd2'], 'a')) 156 157 # b = B'D' + A + C'D' + CD + B' + B'C (6 terms -> 6 OR inputs) 158 gates.append(Gate('OR', ['t_bd', 'A', 't_cd1', 't_cd', "B'", 't_bc1'], 'b')) 159 160 # c = B'D' + A + C'D' + C' + B + BC'D + CD' + BC' (8 terms -> 8 OR inputs) 161 gates.append(Gate('OR', ['t_bd', 'A', 't_cd1', "C'", 'B', 't_bcd', 't_cd2', 't_bc2'], 'c')) 162 163 # d = B'D' + A + BC'D + B'C + CD' (5 terms -> 5 OR inputs) 164 gates.append(Gate('OR', ['t_bd', 'A', 't_bcd', 't_bc1', 't_cd2'], 'd')) 165 166 # e = B'D' + CD' (2 terms -> 2 OR inputs) 167 gates.append(Gate('OR', ['t_bd', 't_cd2'], 'e')) 168 169 # f = A + C'D' + B + BC'D + BC' (5 terms -> 5 OR inputs) 170 gates.append(Gate('OR', ['A', 't_cd1', 'B', 't_bcd', 't_bc2'], 'f')) 171 172 # g = A + BC'D + B'C + CD' + BC' (5 terms -> 5 OR inputs) 173 gates.append(Gate('OR', ['A', 't_bcd', 't_bc1', 't_cd2', 't_bc2'], 'g')) 174 175 outputs = {seg: seg for seg in SEGMENT_NAMES} 176 177 return Circuit(gates=gates, outputs=outputs) 178 179 180def count_circuit_inputs(circuit: Circuit) -> dict: 181 """Analyze gate input usage in a circuit.""" 182 stats = { 183 'total_inputs': 0, 184 'by_gate_type': {}, 185 'by_gate_size': {}, 186 } 187 188 for gate in circuit.gates: 189 n = gate.num_inputs 190 stats['total_inputs'] += n 191 192 gt = gate.gate_type 193 stats['by_gate_type'][gt] = stats['by_gate_type'].get(gt, 0) + n 194 stats['by_gate_size'][n] = stats['by_gate_size'].get(n, 0) + 1 195 196 return stats 197 198 199if __name__ == "__main__": 200 print("Factored synthesis:") 201 circuit = factored_synthesis() 202 valid, errors = verify_circuit(circuit) 203 stats = count_circuit_inputs(circuit) 204 print(f" Valid: {valid}") 205 print(f" Gates: {circuit.num_gates}") 206 print(f" Total gate inputs: {stats['total_inputs']}") 207 print(f" By type: {stats['by_gate_type']}") 208 print(f" By size: {stats['by_gate_size']}") 209 if errors: 210 for e in errors[:5]: 211 print(f" ERROR: {e}") 212 213 print() 214 print("Optimized synthesis:") 215 circuit = optimized_synthesis() 216 valid, errors = verify_circuit(circuit) 217 stats = count_circuit_inputs(circuit) 218 print(f" Valid: {valid}") 219 print(f" Gates: {circuit.num_gates}") 220 print(f" Total gate inputs: {stats['total_inputs']}") 221 print(f" By type: {stats['by_gate_type']}") 222 print(f" By size: {stats['by_gate_size']}") 223 if errors: 224 for e in errors[:5]: 225 print(f" ERROR: {e}")