Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def _parse_env_flag(name: str, default: bool = False) -> bool:
if v is None:
return default
v = v.strip().lower()
return v not in ("", "0", "false", "no", "off")
return v not in ("", "0", "false", "no", "off", "release")


# Disables building extensions and subdirectories related to ABC
Expand All @@ -50,7 +50,11 @@ def build_extension(self, ext: CMakeExtension) -> None:
# Using this requires trailing slash for auto-detection & inclusion of
# auxiliary "native" libs

debug = int(os.environ.get("DEBUG", 0)) if self.debug is None else self.debug
debug = (
_parse_env_flag("DEBUG", False)
if self.debug is None
else bool(self.debug)
)
cfg = "Debug" if debug else "Release"

# CMake lets you override the generator - we need to check this.
Expand Down
28 changes: 28 additions & 0 deletions cirbo/core/circuit/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,34 @@ def bfs(
topsort_unvisited=topsort_unvisited,
)

def get_depth(
self,
) -> int:
"""
Computes the logical depth of the circuit.

The depth of a circuit is defined as the length of the longest path from any

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нам разве важно "до любого другого"? Нам тут важно до любого выходного гейта ведь -- до него и нужно замерять?

input gate to any other gate in the circuit. Input gates have depth 0, and every
other gate has depth equal to 1 plus the maximum depth of its operands.

:return: integer value representing the maximum depth of the circuit

"""
gates = self.gates

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Минорное имхо: так стоит делать только тогда, когда получение объекта действительно сложное (например self.gates.values.first.gate.gate_type и так далее). А сейчас gates, во первых, перекрывает импорт модуля gates из-за коллизии имени, а во вторых, мешает понимать что gates это не локальная переменная а часть состояния объекта.

mem: dict[gate.Label, int] = {}

def depth(label: gate.Label) -> int:
if label in mem:
return mem[label]
gate = gates[label]
if gate.gate_type.name == "INPUT":

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нужно сравнивать не имя, а сам тип "gate.INPUT" (где gate это импортированный модуль), например как тут

if self._gates[_input].gate_type == gate.INPUT

mem[label] = 0
else:
mem[label] = 1 + max(depth(op) for op in gate.operands)
return mem[label]

return max(depth(g.label) for g in gates.values())

def evaluate_full_circuit(
self,
assignment: dict[gate.Label, GateState],
Expand Down
204 changes: 204 additions & 0 deletions cirbo/synthesis/generation/arithmetics/CRT.py

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Наименование модуля (файла) должно быть строчными буквами
  2. Не заметил, он не экспортируется никуда? А используется где-то?

Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import typing as tp

from cirbo.core.circuit import Circuit, gate
from cirbo.synthesis.generation.arithmetics._utils import (
add_gate_from_tt,
reverse_if_big_endian,
)
from cirbo.synthesis.generation.arithmetics.div_mod import add_div_mod
from cirbo.synthesis.generation.arithmetics.multiplication import add_mul_constant

from cirbo.synthesis.generation.arithmetics.summation import add_sum_n_weighted_bits

from cirbo.synthesis.generation.helpers import GenerationBasis


def to_bin(circuit: Circuit, n: int):
"""
Converts an integer constant to circuit labels representing its binary form.

:param circuit: The general circuit.
:param n: Integer constant to convert.
:return: A list of gate labels representing the integer in little-endian format.

"""
label = circuit.inputs[0]
zero = add_gate_from_tt(
circuit,
label,
label,
'0000',
)
one = add_gate_from_tt(
circuit,
label,
label,
'1111',
)
res = []
for i in range(n.bit_length()):
if n & 1 << i:
res.append(one)
else:
res.append(zero)
return res


def extended_euclidean(a, b):
"""
Calculates the greatest common divisor and Bezout coefficients.

:param a: The first integer.
:param b: The second integer.
:return: A tuple ``(gcd, x, y)`` such that ``a * x + b * y == gcd``.

"""
if b == 0:
return a, 1, 0
gcd, x, y = extended_euclidean(b, a % b)
return gcd, y, x - (a // b) * y


def modular_inverse(M_i, m_i):
"""
Calculates the modular inverse of ``M_i`` modulo ``m_i``.

:param M_i: Integer value whose inverse should be found.
:param m_i: Modulus for the inverse calculation.
:return: The value ``x`` such that ``M_i * x == 1 mod m_i``.
:raises ValueError: If the inverse does not exist.

"""
gcd, x, _ = extended_euclidean(M_i, m_i)
if gcd != 1:
raise ValueError(f"Inverse does not exist for {M_i} mod {m_i}")
return x % m_i


def _weighted_bits_to_labels(
circuit: Circuit,
weighted: list[tuple[int, gate.Label]],
) -> list[gate.Label]:
"""
Converts weighted bit labels to a flat little-endian bit list.

:param circuit: The general circuit.
:param weighted: List of pairs where the first element is the bit power and the
second element is the corresponding gate label.
:return: A list of gate labels ordered by bit power in little-endian format.

"""
if not weighted:
return []
max_power = max(p for p, _ in weighted)
ref = weighted[0][1]
zero = add_gate_from_tt(circuit, ref, ref, '0000')
result: list[gate.Label] = [zero] * (max_power + 1)
for power, label in weighted:
result[power] = label
return result


def add_crt(
circuit: Circuit,
input_labels_a: tp.Iterable[gate.Label],
moduls: list[int],
*,
big_endian: bool = False,
basis: tp.Union[str, GenerationBasis] = GenerationBasis.XAIG,
) -> list[gate.Label]:
"""
Reconstructs a number from its residues using the Chinese Remainder Theorem.

:param circuit: The general circuit.
:param input_labels_a: Iterable of gate labels representing concatenated residues.
For each modulus ``m``, the residue occupies ``(m - 1).bit_length()`` bits.
:param moduls: List of pairwise coprime moduli.
:param big_endian: defines how to interpret numbers, big-endian or little-endian
format
:param basis: in which basis should generated function lie. Supported [XAIG, AIG].
:return: A list of gate labels representing the reconstructed number modulo the
product of all moduli.

"""
input_labels_a = list(input_labels_a)

if big_endian:
input_labels_a.reverse()

product = 1
for mod in moduls:
product *= mod
M_i_list = [product // m for m in moduls]
inverse_elements = [modular_inverse(M_i, m_i) for M_i, m_i in zip(M_i_list, moduls)]

pointer = 0
power_bits = []
for i, mod in enumerate(moduls):
bit_len = (mod - 1).bit_length()
res = add_mul_constant(
circuit,
input_labels_a[pointer : pointer + bit_len],
inverse_elements[i] * M_i_list[i],
basis=basis,
)
for j in range(len(res)):
power_bits.append((j, res[j]))
pointer += bit_len

weighted_sum = add_sum_n_weighted_bits(circuit, power_bits, basis=basis)
sum_bits = _weighted_bits_to_labels(circuit, weighted_sum)
product_bits = to_bin(circuit, product)
_, ans = add_div_mod(circuit, sum_bits, product_bits)
return reverse_if_big_endian(ans, big_endian)


def add_crt_calc(
circuit: Circuit,
input_labels_a: tp.Iterable[gate.Label],
moduls: list[int],
factors: list[int],
*,
big_endian: bool = False,
basis: tp.Union[str, GenerationBasis] = GenerationBasis.XAIG,
) -> list[gate.Label]:
"""
Reconstructs a number from its residues using predefined CRT factors.

:param circuit: The general circuit.
:param input_labels_a: Iterable of gate labels representing concatenated residues.
For each modulus ``m``, the residue occupies ``(m - 1).bit_length()`` bits.
:param moduls: List of moduli defining how to split the input labels.
:param factors: Precomputed CRT factors. Each residue is multiplied by the factor
with the same index, and the last element is used as the final modulus.
:param big_endian: defines how to interpret numbers, big-endian or little-endian
format
:param basis: in which basis should generated function lie. Supported [XAIG, AIG].
:return: A list of gate labels representing the reconstructed number reduced by the
final modulus from ``factors``.

"""
input_labels_a = list(input_labels_a)

if big_endian:
input_labels_a.reverse()

pointer = 0
power_bits = []
for i, mod in enumerate(moduls):
bit_len = (mod - 1).bit_length()
res = add_mul_constant(
circuit,
input_labels_a[pointer : pointer + bit_len],
factors[i],
basis=basis,
)
for j in range(len(res)):
power_bits.append((j, res[j]))
pointer += bit_len

weighted_sum = add_sum_n_weighted_bits(circuit, power_bits, basis=basis)
sum_bits = _weighted_bits_to_labels(circuit, weighted_sum)
product_bits = to_bin(circuit, factors[-1])
_, ans = add_div_mod(circuit, sum_bits, product_bits)
return reverse_if_big_endian(ans, big_endian)
40 changes: 38 additions & 2 deletions cirbo/synthesis/generation/arithmetics/__init__.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,43 @@
"""Subpackage defines plenty of methods useful for generation of small arithmetic
circuits by several methods."""

from .div_mod import add_div_mod, generate_div_mod
from .div_mod import (
add_div_mod,
add_div_predefined,
add_mod_predefined,
generate_div_mod,
)
from .equality import add_equal, generate_equal
from .multiplication import (
add_mul,
add_mul_alter,
add_mul_constant,
add_mul_dadda,
add_mul_karatsuba,
add_mul_karatsuba_with_efficient_sum,
add_mul_log_depth_sum,
add_mul_pow2_m1,
add_mul_wallace,
add_smul_dadda,
add_smul_wallace,
generate_mul,
MulMode,
)
from .sqrt import add_sqrt, generate_sqrt
from .square import add_square, add_square_pow2_m1, generate_square, SquareMode
from .square import (
add_square,
add_square_dadda,
add_square_pow2_m1,
generate_square,
SquareMode,
)
from .subtraction import (
add_sub2,
add_sub3,
add_sub_two_numbers,
add_sub_two_numbers_log_depth,
add_subtract_with_compare,
add_subtract_with_compare_log_depth,
generate_sub_two_numbers,
)
from .summation import (
Expand All @@ -29,31 +46,42 @@
add_sum_n_bits,
add_sum_n_bits_easy,
add_sum_n_weighted_bits,
add_sum_n_weighted_bits_log_depth,
add_sum_n_weighted_bits_naive,
add_sum_pow2_m1,
add_sum_two_numbers,
add_sum_two_numbers_log_depth,
add_sum_two_numbers_log_depth_brent_kung,
add_sum_two_numbers_log_depth_krapchenko,
add_sum_two_numbers_with_shift,
generate_sum_n_bits,
generate_sum_weighted_bits_efficient,
generate_sum_weighted_bits_naive,
mdfa_sum_weighted_bits,
)


__all__ = [
# div_mod.py
'generate_div_mod',
'add_div_mod',
'add_div_predefined',
'add_mod_predefined',
# equality.py
'add_equal',
'generate_equal',
# multiplication.py
'add_mul',
'add_mul_karatsuba_with_efficient_sum',
'add_mul_karatsuba',
'add_mul_log_depth_sum',
'add_mul_alter',
'add_mul_dadda',
'add_mul_wallace',
'add_mul_pow2_m1',
'add_smul_dadda',
'add_smul_wallace',
'add_mul_constant',
'generate_mul',
'MulMode',
# sqrt.py
Expand All @@ -62,13 +90,16 @@
# square.py
'add_square',
'add_square_pow2_m1',
'add_square_dadda',
'generate_square',
'SquareMode',
# subtraction.py
'add_sub2',
'add_sub3',
'add_sub_two_numbers',
'add_sub_two_numbers_log_depth',
'add_subtract_with_compare',
'add_subtract_with_compare_log_depth',
'generate_sub_two_numbers',
# summation.py
'generate_sum_n_bits',
Expand All @@ -78,9 +109,14 @@
'add_sum_n_bits_easy',
'add_sum_pow2_m1',
'add_sum_two_numbers',
'add_sum_two_numbers_log_depth',
'add_sum_two_numbers_log_depth_brent_kung',
'add_sum_two_numbers_log_depth_krapchenko',
'add_sum_two_numbers_with_shift',
'add_sum_n_weighted_bits',
'add_sum_n_weighted_bits_log_depth',
'add_sum_n_weighted_bits_naive',
'generate_sum_weighted_bits_efficient',
"generate_sum_weighted_bits_naive",
'mdfa_sum_weighted_bits',
]
Loading
Loading