diff --git a/ariths_gen/core/arithmetic_circuits/general_circuit.py b/ariths_gen/core/arithmetic_circuits/general_circuit.py index c6571a1..bdf9df8 100644 --- a/ariths_gen/core/arithmetic_circuits/general_circuit.py +++ b/ariths_gen/core/arithmetic_circuits/general_circuit.py @@ -54,8 +54,9 @@ class GeneralCircuit(): self.out = Bus(outname, out_N, out_bus=True, signed=signed) self.components = [] - self.circuit_wires = [] + self._prefixes = [] # TODO rename to fullname and add distinct attr for prefix, name, suffix self.circuit_gates = [] + self.circuit_wires = [] self.signed = signed self.c_data_type = "int64_t" if self.signed is True else "uint64_t" self.pyc = None # Python compiled function @@ -87,12 +88,26 @@ class GeneralCircuit(): def add_component(self, component): """Adds a component into list of circuit's inner subcomponents. + + Additionally it adds all the gates of the component to the circuit's list of gates and all + sbcomponents prefixes to check for naming duplicates which could cause issues in the circuit generation. Args: component: Subcomponent to be added into list of components composing described circuit. """ - prefixes = [c.prefix for c in self.components] - assert component.prefix not in prefixes, f"Component with prefix {component.prefix} already exists in the circuit." + # TODO will be redone in ArithsGen rework + # We should probably check also wire names for especially hierarchical generation + if isinstance(component, TwoInputLogicGate): + if component.disable_generation is False: + self.circuit_gates.append(component) + else: + self.circuit_gates.extend(component.get_circuit_gates()) + for prefix in component._prefixes: + assert prefix not in self._prefixes, f"Component with prefix {prefix} already exists in the circuit." + self._prefixes.extend(component._prefixes) + + assert component.prefix not in self._prefixes, f"Component with prefix {component.prefix} already exists in the circuit." + self._prefixes.append(component.prefix) self.components.append(component) return component @@ -120,7 +135,7 @@ class GeneralCircuit(): return sum(isinstance(c, cls) for c in self.components if isinstance(c, cls) and c.disable_generation is False) else: return sum(isinstance(c, cls) for c in self.components) - + def get_circuit_gates(self, verilog_output: bool = False): """Gets a list of all the logic gates in circuit that should be generated. @@ -227,14 +242,13 @@ class GeneralCircuit(): else: return len(self.circuit_wires)+2 - def get_cgp_wires(self): + def get_circuit_wires(self): """Gets a list of all wires in circuit along with their index position for cgp chromosome generation and stores them inside `self.circuit_wires` list. Constant wire with value 0 has constant index of 0. Constant wire with value 1 has constant index of 1. Other wires indexes start counting from 2 and up. """ - self.circuit_wires = [] circuit_wires_names = [] for input in self.inputs: @@ -262,6 +276,7 @@ class GeneralCircuit(): self.circuit_wires.append( (gate.out, gate.out.name, self.save_wire_id(wire=gate.out))) circuit_wires_names.append(gate.out.name) + def get_circuit_wire_index(self, wire: Wire): """Searches for circuit's wire unique index position within the circuit. Used for cgp chromosome generation. @@ -773,7 +788,7 @@ class GeneralCircuit(): Returns: str: CGP chromosome parameters of described arithmetic circuit. """ - self.circuit_gates = self.get_circuit_gates() + # self.circuit_gates = self.get_circuit_gates() TODO delete return f"{{{sum(input_bus.N for input_bus in self.inputs)},{self.out.N},1,{len(self.circuit_gates)},2,1,0}}" def get_triplets_cgp(self): @@ -789,7 +804,7 @@ class GeneralCircuit(): Returns: str: List of triplets each describing logic function of corresponding two input logic gate and as a whole describe the arithmetic circuit. """ - self.get_cgp_wires() + self.get_circuit_wires() return "".join([g.get_triplet_cgp(a_id=self.get_circuit_wire_index(g.a), out_id=self.get_circuit_wire_index(g.out)) if isinstance(g, OneInputLogicGate) else g.get_triplet_cgp(a_id=self.get_circuit_wire_index(g.a), b_id=self.get_circuit_wire_index(g.b), out_id=self.get_circuit_wire_index(g.out)) for g in self.circuit_gates]) diff --git a/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py b/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py index a985e91..b5f67dc 100644 --- a/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py +++ b/ariths_gen/multi_bit_circuits/approximate_multipliers/recursive_multiplier.py @@ -22,6 +22,28 @@ import math class UnsignedAccurateTwoBitMultiplier(MultiplierCircuit): """Class representing unsigned two-bit accurate multiplier. + ``` + A1B1 A1B0 A0B1 A0B0 + │ │ │ │ │ │ │ │ + ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐ + │AND│ │AND│ │AND│ │AND│ + └─┬─┘ └─┬─┘ └─┬─┘ └─┬─┘ + │ └──┬──┐└┬─┐ │ + │ │ │ │ │ │ + │ │ │ │ │ │ + │ │ ┌▼─▼┐│ │ + │ │ │AND││ │ + │ │ └─┬─┘│ │ + └─────┐ ┌─┼───┘┌─┘ │ + │ │ └──┐ │ │ + ┌◄─►┐ ┌▼─▼┐ ┌▼─▼┐ │ + │AND│ │XOR│ │XOR│ │ + └─┬─┘ └─┬─┘ └─┬─┘ │ + │ │ │ │ + ▼ ▼ ▼ ▼ + O3 O2 O1 O0 + ``` + Description of the __init__ method. Args: @@ -76,6 +98,22 @@ class UnsignedApproximateTwoBitMultiplierM1(MultiplierCircuit): """Class representing unsigned two-bit approximate multiplier variant M1. M1 ax variant defined here: https://ieeexplore.ieee.org/document/8727537 + + ``` + A1B1 A1B0 A0B1 A0B0 + │ │ │ │ │ │ │ │ + ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐ + │AND│ │AND│ │AND│ │AND│ + └─┬─┘ └─┬─┘ └─┬─┘ └─┬─┘ + │ │ └┐ │ + │ └─────┐ │ │ + └──────┐ ┌▼─▼┐ │ + │ │ OR│ │ + │ └─┬─┘ │ + │ │ │ + ▼ ▼ ▼ ▼ + O3=0 O2 O1 O0 + ``` Description of the __init__ method. @@ -127,6 +165,29 @@ class UnsignedApproximateTwoBitMultiplierM2(MultiplierCircuit): """Class representing unsigned two-bit approximate multiplier variant M2. M2 ax variant defined here: https://ieeexplore.ieee.org/document/8727537 + + ``` + A1B1 A1B0 A0B1 + │ │ │ │ │ │ + ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐ + │AND│ │AND│ │AND│ + └─┬─┘ └─┬─┘ └─┬─┘ + │ │┌─────┴┐ + │ ┌┴┼────┐ │ + │ │ │ │ │ + │ ┌▼─▼┐ ┌▼─▼┐ + │ │XOR│ │AND│ + │ └─┬─┘ └─┬─┘ + └─────┐ ┌─────┼──────┤ + │ │ │ │ + ┌▼─▼┐ │ │ + │XOR│ │ │ + └─┬─┘ │ │ + ┌──────┼──────┼──────┤ + │ │ │ │ + ▼ ▼ ▼ ▼ + O3 O2 O1 O0 + ``` Description of the __init__ method. @@ -180,6 +241,33 @@ class UnsignedApproximateTwoBitMultiplierM3(MultiplierCircuit): """Class representing unsigned two-bit approximate multiplier variant M3. M3 ax variant defined here: https://ieeexplore.ieee.org/document/8727537 + + ``` + A1B1 A1B0 A0B1 A0B0 + │ │ │ │ │ │ │ │ + ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐ + │AND│ │AND│ │AND│ │AND│ + └─┬─┘ └─┬─┘ └─┬─┘ └─┬─┘ + │ │ └┐ │ + │ │ │ │ + │ └─────┐ │ │ + │ ┌▼─▼┐ │ + │ │ OR│ │ + │ └─┬─┘ │ + │┌─────┬──────┼──────┤ + ││ │ │ │ + ││ ┌─▼─┐ │ │ + ││ │NOT│ │ │ + ││ └─┬─┘ │ │ + ┌┴┼────┐└┐ │ │ + │ │ │ │ │ │ + ┌▼─▼┐ ┌▼─▼┐ │ │ + │AND│ │AND│ │ │ + └─┬─┘ └─┬─┘ │ │ + │ │ │ │ + ▼ ▼ ▼ ▼ + O3 O2 O1 O0 + ``` Description of the __init__ method. @@ -236,6 +324,22 @@ class UnsignedApproximateTwoBitMultiplierM4(MultiplierCircuit): M4 ax variant defined here: https://ieeexplore.ieee.org/document/8727537 + ``` + A1B1 A1B0 A0B1 A0B0 + │ │ │ │ │ │ │ │ + ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐ ┌▼─▼┐ + │AND│ │AND│ │AND│ │AND│ + └─┬─┘ └─┬─┘ └─┬─┘ └─┬─┘ + │ │ └┐ │ + │ └─────┐ │ │ + └──────┐ ┌▼─▼┐ │ + │ │XOR│ │ + │ └─┬─┘ │ + │ │ │ + ▼ ▼ ▼ ▼ + O3=0 O2 O1 O0 + ``` + Description of the __init__ method. Args: @@ -285,7 +389,13 @@ class SignedApproximateTwoBitMultiplierM4(MultiplierCircuit): class UnsignedRecursiveMultiplier(MultiplierCircuit): """Class representing unsigned recursive multiplier. - TODO + Input bit-vector length N can be any power of two greater than 1 (e.g. 2, 4, 8, ...). + + The internal structure of the recursive multiplier is composed of subsequent two-bit submultipliers provided in the input `submultipliers` list. + The `submultipliers` list should contain the classes of the two-bit submultipliers that will be used for instantiation. If None are provided, accurate two-bit submultipliers are assumed. + + The number of submultipliers required is equal to (N/2)² for N > 2. For N = 2, only one two-bit submultiplier is required. + Description of the __init__ method. Args: diff --git a/ariths_gen/multi_bit_circuits/others/__init__.py b/ariths_gen/multi_bit_circuits/others/__init__.py index de090f3..ecbb4e8 100644 --- a/ariths_gen/multi_bit_circuits/others/__init__.py +++ b/ariths_gen/multi_bit_circuits/others/__init__.py @@ -4,11 +4,18 @@ from ariths_gen.multi_bit_circuits.others.popcount import ( ) from ariths_gen.multi_bit_circuits.others.bit_reduce import ( - BitReduce, AndReduce, OrReduce + BitReduce, + AndReduce, + OrReduce ) from ariths_gen.multi_bit_circuits.others.compare import ( - UnsignedCompareLT, UnsignedCompareLTE, UnsignedCompareGT, UnsignedCompareGTE + UnsignedCompareLT, + UnsignedCompareLTE, + UnsignedCompareGT, + UnsignedCompareGTE ) -from ariths_gen.multi_bit_circuits.others.popcount_compare import PopCountCompare \ No newline at end of file +from ariths_gen.multi_bit_circuits.others.popcount_compare import ( + PopCountCompare +) diff --git a/ariths_gen/multi_bit_circuits/others/bit_reduce.py b/ariths_gen/multi_bit_circuits/others/bit_reduce.py index bd0cd8b..9752c67 100644 --- a/ariths_gen/multi_bit_circuits/others/bit_reduce.py +++ b/ariths_gen/multi_bit_circuits/others/bit_reduce.py @@ -80,10 +80,10 @@ class BitReduce(GeneralCircuit): class OrReduce(BitReduce): - def __init__(self, a: Bus, prefix : str = "", name : str = "orreduce", **kwargs): + def __init__(self, a: Bus, prefix: str = "", name: str = "orreduce", **kwargs): super().__init__(a=a, gate=OrGate, prefix=prefix, name=name, **kwargs) class AndReduce(BitReduce): - def __init__(self, a: Bus, prefix : str = "", name : str = "orreduce", **kwargs): - super().__init__(a=a, gate=AndGate, prefix=prefix, name=name, **kwargs) \ No newline at end of file + def __init__(self, a: Bus, prefix: str = "", name: str = "andreduce", **kwargs): + super().__init__(a=a, gate=AndGate, prefix=prefix, name=name, **kwargs) diff --git a/ariths_gen/multi_bit_circuits/others/compare.py b/ariths_gen/multi_bit_circuits/others/compare.py index 6e92ce1..ab89fd1 100644 --- a/ariths_gen/multi_bit_circuits/others/compare.py +++ b/ariths_gen/multi_bit_circuits/others/compare.py @@ -83,7 +83,7 @@ class UnsignedCompareLTE(GeneralCircuit): Returns true if a <= b """ - def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_lt", **kwargs): + def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_lte", **kwargs): self.a = a self.b = b self.N = max(a.N, b.N) @@ -177,7 +177,7 @@ class UnsignedCompareGTE(GeneralCircuit): for i in reversed(range(self.N)): iA = self.a[i] if i < self.a.N else ConstantWireValue0() iB = self.b[i] if i < self.b.N else ConstantWireValue0() - + i1 = iA i2 = self.add_component(NotGate(iB, f"{self.prefix}_i1_{i}", parent_component=self)).out @@ -188,7 +188,7 @@ class UnsignedCompareGTE(GeneralCircuit): psum = self.add_component(AndGate(pi, psum, f"{self.prefix}_psum_{i}", parent_component=self)).out res[self.N] = psum # or all equal (xor) - + red = self.add_component(OrReduce(res, prefix=f"{self.prefix}_orred", inner_component=True)) - self.out.connect_bus(red.out) \ No newline at end of file + self.out.connect_bus(red.out) diff --git a/ariths_gen/multi_bit_circuits/others/popcount.py b/ariths_gen/multi_bit_circuits/others/popcount.py index 4721bce..16fd141 100644 --- a/ariths_gen/multi_bit_circuits/others/popcount.py +++ b/ariths_gen/multi_bit_circuits/others/popcount.py @@ -68,8 +68,8 @@ class UnsignedPopCount(GeneralCircuit): return a else: half = a.N // 2 - b_in = Bus(N=half, prefix=f"b_inn{depth}A") - c_in = Bus(N=a.N - half, prefix=f"b_inn{depth}B") + b_in = Bus(N=half, prefix=f"b_inn_{branch}_{depth}A") + c_in = Bus(N=a.N - half, prefix=f"b_inn_{branch}_{depth}B") #print(a, half, a.N) @@ -87,4 +87,4 @@ class UnsignedPopCount(GeneralCircuit): sumbus = create_tree(self.a,0, "X") #print(sumbus) - self.out.connect_bus(sumbus ) \ No newline at end of file + self.out.connect_bus(sumbus) diff --git a/ariths_gen/multi_bit_circuits/others/popcount_compare.py b/ariths_gen/multi_bit_circuits/others/popcount_compare.py index 3f26875..6b8570d 100644 --- a/ariths_gen/multi_bit_circuits/others/popcount_compare.py +++ b/ariths_gen/multi_bit_circuits/others/popcount_compare.py @@ -59,7 +59,7 @@ class PopCountCompare(GeneralCircuit): ▼ """ - def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "cmp_lt", **kwargs): + def __init__(self, a: Bus, b: Bus, prefix : str = "", name : str = "popcnt_cmp", **kwargs): self.a = a self.b = b diff --git a/tests/test_popcnt.py b/tests/test_popcnt.py index a877254..6676d8d 100644 --- a/tests/test_popcnt.py +++ b/tests/test_popcnt.py @@ -1,3 +1,9 @@ +import os +import sys +# Add the parent directory to the system path +DIR_PATH = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.join(DIR_PATH, '..')) + from io import StringIO from ariths_gen.core.cgp_circuit import UnsignedCGPCircuit from ariths_gen.wire_components import ( diff --git a/tests/test_popcount_compare.py b/tests/test_popcount_compare.py index 3f02ede..9f30875 100644 --- a/tests/test_popcount_compare.py +++ b/tests/test_popcount_compare.py @@ -1,3 +1,9 @@ +import os +import sys +# Add the parent directory to the system path +DIR_PATH = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.join(DIR_PATH, '..')) + from ariths_gen.wire_components import ( Wire, ConstantWireValue0,