diff --git a/cirbo/minimization/simplification/cleanup.py b/cirbo/minimization/simplification/cleanup.py index 2fd2d61..37e5dc2 100644 --- a/cirbo/minimization/simplification/cleanup.py +++ b/cirbo/minimization/simplification/cleanup.py @@ -16,21 +16,27 @@ logger = logging.getLogger(__name__) -def cleanup(circuit: Circuit) -> Circuit: +def cleanup(circuit: Circuit, *, use_heavy: bool = False) -> Circuit: """ Applies several simplification algorithms from this module consecutively in order to simplify provided circuit. :param circuit: the original circuit to be simplified + :param use_heavy: if True, then heavier cleanups (e.g. `MergeEquivalentGates`) + will also be used. :return: new simplified version of the circuit """ + _strategies = [ + RemoveRedundantGates(), + MergeUnaryOperators(), + MergeDuplicateGates(), + ] + + if use_heavy: + _strategies += [MergeEquivalentGates()] + return Transformer.apply_transformers( circuit, - [ - RemoveRedundantGates(), - MergeUnaryOperators(), - MergeDuplicateGates(), - MergeEquivalentGates(), - ], + _strategies, ) diff --git a/cirbo/minimization/simplification/merge_equivalent_gates.py b/cirbo/minimization/simplification/merge_equivalent_gates.py index af4ee71..91cdab9 100644 --- a/cirbo/minimization/simplification/merge_equivalent_gates.py +++ b/cirbo/minimization/simplification/merge_equivalent_gates.py @@ -1,4 +1,5 @@ import collections +import dataclasses import logging import typing as tp @@ -66,6 +67,25 @@ def _find_equivalent_gates_groups(circuit: Circuit) -> list[list[Label]]: return equivalent_groups +@dataclasses.dataclass +class _Keep: + """ + A representative element for the group. + + If holds None, then no elements of the group were reconstructed yet. When the first + element of the group is rebuild, It will be set as Keep. + + """ + + value: tp.Optional[str] = None + + def set_or_get_existing(self, label: str) -> str: + if self.value is None: + self.value = label + + return self.value + + def _replace_equivalent_gates( circuit: Circuit, equivalent_groups: tp.Iterable[tp.Collection[Label]], @@ -92,15 +112,19 @@ def _replace_equivalent_gates( ) continue - _group_iter = iter(group) - keep = next(_group_iter) - for gate_to_replace in _group_iter: + keep = _Keep() + for gate_to_replace in group: _old_to_new_gate[gate_to_replace] = keep - # map gate label to its new name. + # map gate label to its new name and sets Keep when needed. def _get_gate_new_name(_label: Label): nonlocal _old_to_new_gate - return _old_to_new_gate.get(_label, _label) + + if _label not in _old_to_new_gate: + return _label + + _keep = _old_to_new_gate[_label] + return _keep.set_or_get_existing(_label) # rebuild circuit from inputs to outputs with remapping. def _process_gate(_gate: Gate, _: tp.Mapping): diff --git a/tests/cirbo/minimization/simplification/merge_equivalent_gates_test.py b/tests/cirbo/minimization/simplification/merge_equivalent_gates_test.py index 274f20d..8d0037b 100644 --- a/tests/cirbo/minimization/simplification/merge_equivalent_gates_test.py +++ b/tests/cirbo/minimization/simplification/merge_equivalent_gates_test.py @@ -76,10 +76,10 @@ def test_find_equivalent_gates(original_circuit: Circuit, expected_groups): expected_circuit_3.add_gate(Gate('input4', gate.INPUT)) expected_circuit_3.emplace_gate('AND2', gate.AND, ('input1', 'input2')) expected_circuit_3.add_gate(Gate('OR2', gate.OR, ('AND2', 'input3'))) -expected_circuit_3.add_gate(Gate('XOR2', gate.XOR, ('AND2', 'OR2'))) +expected_circuit_3.add_gate(Gate('XOR1', gate.XOR, ('AND2', 'OR2'))) expected_circuit_3.mark_as_output('AND2') -expected_circuit_3.mark_as_output('XOR2') -expected_circuit_3.mark_as_output('XOR2') +expected_circuit_3.mark_as_output('XOR1') +expected_circuit_3.mark_as_output('XOR1') @pytest.mark.parametrize( diff --git a/tutorial/cleaning_majority7_circuit.py b/tutorial/cleaning_majority7_circuit.py index 288d88c..b3df01c 100644 --- a/tutorial/cleaning_majority7_circuit.py +++ b/tutorial/cleaning_majority7_circuit.py @@ -7,5 +7,5 @@ *_, b2 = add_sum_n_bits(ckt, ckt.inputs) ckt.mark_as_output(b2) print(ckt.gates_number()) -ckt = cleanup(ckt) +ckt = cleanup(ckt, use_heavy=True) print(ckt.gates_number()) diff --git a/tutorial/simple_cleanup.py b/tutorial/simple_cleanup.py new file mode 100644 index 0000000..05fbc55 --- /dev/null +++ b/tutorial/simple_cleanup.py @@ -0,0 +1,5 @@ +from cirbo.core import Circuit, Gate +from cirbo.minimization import cleanup + +circ = Circuit.from_bench_file("../data/circuit.bench") +circ = cleanup(circ) \ No newline at end of file