From 739d5fafcef4c9c8ab9eb9b351fc6a61589b0485 Mon Sep 17 00:00:00 2001
From: honzastor <jan.klhufek@gmail.com>
Date: Mon, 8 Apr 2024 21:37:34 +0200
Subject: [PATCH] Added documentation to Recursive multiplier and hopefully
 fixed some issues with popcount output generation.

---
 .../arithmetic_circuits/general_circuit.py    |  31 +++--
 .../recursive_multiplier.py                   | 112 +++++++++++++++++-
 .../multi_bit_circuits/others/__init__.py     |  13 +-
 .../multi_bit_circuits/others/bit_reduce.py   |   6 +-
 .../multi_bit_circuits/others/compare.py      |   8 +-
 .../multi_bit_circuits/others/popcount.py     |   6 +-
 .../others/popcount_compare.py                |   2 +-
 tests/test_popcnt.py                          |   6 +
 tests/test_popcount_compare.py                |   6 +
 9 files changed, 167 insertions(+), 23 deletions(-)

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,