diff --git a/bruteforce.py b/bruteforce.py index f18eb59..5d3e9af 100644 --- a/bruteforce.py +++ b/bruteforce.py @@ -1,122 +1,123 @@ from itertools import product from functools import lru_cache # For memoization # Define the area size for each gate type @lru_cache(None) def evaluate_circuit(circuit, inputs): intermediate_outputs = list(inputs[:]) for gate, in1, in2 in circuit: intermediate_out1 = intermediate_outputs[in1] intermediate_out2 = intermediate_outputs[in2] if gate == 'AND': output = intermediate_out1 & intermediate_out2 elif gate == 'OR': output = intermediate_out1 | intermediate_out2 elif gate == 'XOR': output = intermediate_out1 ^ intermediate_out2 elif gate == 'XNOR': output = ~(intermediate_out1 ^ intermediate_out2) & 1 elif gate == 'NAND': output = ~(intermediate_out1 & intermediate_out2) & 1 elif gate == 'NOR': output = ~(intermediate_out1 | intermediate_out2) & 1 elif gate == 'INV': output = ~intermediate_out1 & 1 elif gate == 'ANDNOT': output = intermediate_out1 & (~intermediate_out2 & 1) elif gate == 'ORNOT': output = intermediate_out1 | (~intermediate_out2 & 1) # & 1 to ensure output is 0 or 1 else it will be -1 else: output = 0 intermediate_outputs.append(output) return intermediate_outputs[-1] def brute_force_boolean(target_function, num_inputs, max_gates, max_area): truth_table = list(product([0, 1], repeat=num_inputs)) target_outputs = [target_function(input_comb) for input_comb in truth_table] print("Truth Table and Target Outputs:") print("Inputs\t\tOutput") for inputs, output in zip(truth_table, target_outputs): print(f"{inputs}\t{output}") gate_types = { 'INV': 1160, 'AND': 3472, 'NAND': 2832, 'OR': 3472, 'NOR': 2832, 'XOR': 4632, 'XNOR': 4632, 'ANDNOT': 3472, 'ORNOT': 3472 } best_circuit = None best_area = max_area i = 0 for num_gates in range(1, max_gates + 1): print("num_gates: ", num_gates, " building circuits") for circuit in generate_all_circuits(num_inputs, num_gates, gate_types, best_area): total_area = sum(gate_types[gate] for gate, _, _ in circuit) if(i % 100000 == 0): print(f"Circuit: {i:,} at num_gates: {num_gates} with area: {total_area}") i += 1 if total_area > best_area: continue if all(evaluate_circuit(tuple(circuit), inputs) == target_output for inputs, target_output in zip(truth_table, target_outputs)): if not best_circuit or total_area < best_area: best_circuit = circuit best_area = total_area print("New best circuit found with area", best_area, ":", best_circuit) else: print("Circuit with area", total_area, ":") for gate, in1, in2 in circuit: print(f" Gate: {gate}, Input1: {in1}, Input2: {in2}") return best_circuit, best_area def generate_all_circuits(num_inputs, num_gates, gate_types, best_area): """ circuit: [(gate, in1, in2), ...] where gate is one of the gate types and in1, in2 are indices of the inputs or intermediate outputs """ base_indices = list(range(num_inputs)) def recursive_build(current_circuit, available_indices, current_area, depth): if depth == num_gates: outputs_used = set() for _, in1, in2 in current_circuit: outputs_used.add(in1) outputs_used.add(in2) all_outputs_used = True for i in range(len(current_circuit)-1): if i + num_inputs not in outputs_used: all_outputs_used = False break if all_outputs_used or num_gates == 1: yield current_circuit return for gate, area in gate_types.items(): if current_area + area > best_area: continue for in1 in available_indices: for in2 in available_indices: - if (in1 == in2 and gate != 'INV') or (in1 != in2 and gate == 'INV'): # Skip redundant gates + #skip redundant gate if it is an INV with different inputs, or if it is a gate with the same, or if it is symmetric on a normal gate + if (in1 == in2 and gate != 'INV') or (in1 != in2 and gate == 'INV') or (in1 > in2 and (gate != 'ANDNOT' or gate != 'ORNOT')): # Skip redundant gates continue new_circuit = current_circuit + [(gate, in1, in2)] new_indices = available_indices + [len(available_indices)] yield from recursive_build(new_circuit, new_indices, current_area + area, depth + 1) return recursive_build([], base_indices, 0, 0) # Example: Target function for 2-input XOR -target_function = lambda x: x[0] ^ x[1] -best_circuit, best_area = brute_force_boolean(target_function, num_inputs=2, max_gates=5, max_area=1000000) +target_function = lambda x: x[0] ^ x[1] ^ x[2] +best_circuit, best_area = brute_force_boolean(target_function, num_inputs=3, max_gates=6, max_area=100000000) print("Best circuit:", best_circuit, "with area: ", best_area)