Skip to content

Commit

Permalink
wip: cairo1 compiler
Browse files Browse the repository at this point in the history
  • Loading branch information
feltroidprime committed Jun 24, 2024
1 parent df4f544 commit b8cd1be
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 45 deletions.
10 changes: 3 additions & 7 deletions hydra/extension_field_modulo_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@
POSEIDON_OUTPUT_S1_INDEX = 4


# Represents the state of the accumulation of the equation c_i * X_i(Z)*Y_i(z) = c_i*Q_i*P + c_i*R_i inside the circuit.
# Only store ci*X_i(Z)*Y_i(z) (as Emulated Field Element) and ci*R_i (as Polynomial)
# Represents the state of the accumulation of the equation
# c_i * Π(Pi(z)) = c_i*Q_i*P + c_i*R_i inside the circuit.
# Only store ci*Π(Pi(z)) (as Emulated Field Element) and ci*R_i (as Polynomial)
@dataclass(slots=True)
class EuclideanPolyAccumulator:
lhs: ModuloCircuitElement
Expand Down Expand Up @@ -279,11 +280,6 @@ def extf_div(
extension_degree: int,
acc_index: int = 0,
) -> list[ModuloCircuitElement]:
if extension_degree == 2:
return self.fp2_div(X, Y)
else:
assert len(X) == len(Y) == extension_degree

x_over_y = nondeterministic_extension_field_div(
X, Y, self.curve_id, extension_degree
)
Expand Down
151 changes: 118 additions & 33 deletions hydra/modulo_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,21 +375,21 @@ def add(
b: ModuloCircuitElement,
) -> ModuloCircuitElement:

if a is None:
return b
elif b is None:
return a
else:
assert (
type(a) == type(b) == ModuloCircuitElement
), f"Expected ModuloElement, got {type(a)}, {a} and {type(b)}, {b}"
# if a is None:
# return b
# elif b is None:
# return a
# else:
assert (
type(a) == type(b) == ModuloCircuitElement
), f"Expected ModuloElement, got {type(a)}, {a} and {type(b)}, {b}"

instruction = ModuloCircuitInstruction(
ModBuiltinOps.ADD, a.offset, b.offset, self.values_offset
)
return self.write_element(
a.emulated_felt + b.emulated_felt, WriteOps.BUILTIN, instruction
)
instruction = ModuloCircuitInstruction(
ModBuiltinOps.ADD, a.offset, b.offset, self.values_offset
)
return self.write_element(
a.emulated_felt + b.emulated_felt, WriteOps.BUILTIN, instruction
)

def double(self, a: ModuloCircuitElement) -> ModuloCircuitElement:
return self.add(a, a)
Expand Down Expand Up @@ -419,17 +419,23 @@ def sub(self, a: ModuloCircuitElement, b: ModuloCircuitElement):

def inv(self, a: ModuloCircuitElement):
instruction = ModuloCircuitInstruction(
ModBuiltinOps.MUL, a.offset, self.values_offset, self.get_constant(1).offset
ModBuiltinOps.MUL,
a.offset,
self.values_offset,
self.get_constant(1).offset,
)
return self.write_element(a.felt.__inv__(), WriteOps.BUILTIN, instruction)

def div(self, a: ModuloCircuitElement, b: ModuloCircuitElement):
instruction = ModuloCircuitInstruction(
ModBuiltinOps.MUL, b.offset, self.values_offset, a.offset
)
return self.write_element(
a.felt * b.felt.__inv__(), WriteOps.BUILTIN, instruction
)
if self.compilation_mode == 0:
instruction = ModuloCircuitInstruction(
ModBuiltinOps.MUL, b.offset, self.values_offset, a.offset
)
return self.write_element(
a.felt * b.felt.__inv__(), WriteOps.BUILTIN, instruction
)
elif self.compilation_mode == 1:
return self.mul(a, self.inv(b))

def fp2_mul(self, X: list[ModuloCircuitElement], Y: list[ModuloCircuitElement]):
# Assumes the irreducible poly is X^2 + 1.
Expand All @@ -452,18 +458,28 @@ def fp2_square(self, X: list[ModuloCircuitElement]):

def fp2_div(self, X: list[ModuloCircuitElement], Y: list[ModuloCircuitElement]):
assert len(X) == len(Y) == 2
x_over_y = nondeterministic_extension_field_div(X, Y, self.curve_id, 2)
x_over_y = self.write_elements(x_over_y, WriteOps.WITNESS)
# x_over_y = d0 + i * d1
# y = y0 + i * y1
# x = x_over_y*y = d0*y0 - d1*y1 + i * (d0*y1 + d1*y0)
self.sub_and_assert(
a=self.mul(x_over_y[0], Y[0]), b=self.mul(x_over_y[1], Y[1]), c=X[0]
)
self.add_and_assert(
a=self.mul(x_over_y[0], Y[1]), b=self.mul(x_over_y[1], Y[0]), c=X[1]
)
return x_over_y
if self.compilation_mode == 0:
x_over_y = nondeterministic_extension_field_div(X, Y, self.curve_id, 2)
x_over_y = self.write_elements(x_over_y, WriteOps.WITNESS)
# x_over_y = d0 + i * d1
# y = y0 + i * y1
# x = x_over_y*y = d0*y0 - d1*y1 + i * (d0*y1 + d1*y0)
self.sub_and_assert(
a=self.mul(x_over_y[0], Y[0]), b=self.mul(x_over_y[1], Y[1]), c=X[0]
)
self.add_and_assert(
a=self.mul(x_over_y[0], Y[1]), b=self.mul(x_over_y[1], Y[0]), c=X[1]
)
return x_over_y
elif self.compilation_mode == 1:
# Todo : consider passing as calldata if possible.
t0 = self.mul(Y[0], Y[0])
t1 = self.mul(Y[1], Y[1])
t0 = self.add(t0, t1)
t1 = self.inv(t0)
inv0 = self.mul(Y[0], t1)
inv1 = self.neg(self.mul(Y[1], t1))
return self.fp2_mul(X, [inv0, inv1])

def sub_and_assert(
self, a: ModuloCircuitElement, b: ModuloCircuitElement, c: ModuloCircuitElement
Expand Down Expand Up @@ -618,6 +634,75 @@ def compile_circuit(
code += "}\n"
return code

def compile_circuit_cairo_1(
self,
function_name: str = None,
) -> str:
dw_arrays = self.values_segment.get_dw_lookups()
name = function_name or self.values_segment.name
function_name = f"get_{name}_circuit"
if self.generic_circuit:
code = (
f"fn {function_name}(mut input: Array<u384>,curve_index:usize)->Array<u384>"
+ "{"
+ "\n"
)
else:
code = (
f"fn {function_name}(mut input: Array<u384>)->Array<u384>" + "{" + "\n"
)

def write_stack(
write_ops: WriteOps, code: str, start_index: int
) -> tuple(str, int):
code += f"\n // {write_ops.name} stack\n"
for idx in range(
start_index,
start_index + len(self.values_segment.segment_stacks[write_ops]),
):
code += (
f"\t let in{idx} = CircuitElement::<CircuitInput<{idx}>> {{}};\n"
)
c
return code, start_index + len(
self.values_segment.segment_stacks[write_ops]
)

code, start_index = write_stack(WriteOps.CONSTANT, code, 0)
code, start_index = write_stack(WriteOps.INPUT, code, start_index)
code, start_index = write_stack(WriteOps.COMMIT, code, start_index)
code, start_index = write_stack(WriteOps.WITNESS, code, start_index)
code, start_index = write_stack(WriteOps.FELT, code, start_index)

# elif dw_array_name in ["add_offsets_ptr", "mul_offsets_ptr"]:
# num_instructions = len(dw_values)
# instructions_needed = (
# 8 - (num_instructions % 8)
# ) % 8 # Must be a multiple of 8 (currently)
# for left, right, result in dw_values:
# code += (
# f"\t dw {left};\n" + f"\t dw {right};\n" + f"\t dw {result};\n"
# )
# if instructions_needed > 0:
# first_triplet = dw_values[0]
# for _ in range(instructions_needed):
# code += (
# f"\t dw {first_triplet[0]};\n"
# + f"\t dw {first_triplet[1]};\n"
# + f"\t dw {first_triplet[2]};\n"
# )
# code += "\n"
# elif dw_array_name in ["output_offsets_ptr"]:
# if continuous_output:
# code += f"\t dw {dw_values[0]};\n"
# else:
# for val in dw_values:
# code += f"\t dw {val};\n"

code += "\n"
code += "}\n"
return code

def summarize(self):
add_count, mul_count, assert_eq_count = self.values_segment.summarize()
summary = {
Expand Down
10 changes: 5 additions & 5 deletions hydra/precompiled_circuits/multi_miller_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def compute_doubling_slope(
self.mul(num_tmp[1], self.get_constant(6)),
]
den = self.extf_add(Q[1], Q[1])
return self.extf_div(num, den, 2)
return self.fp2_div(num, den)

def compute_adding_slope(
self,
Expand All @@ -131,7 +131,7 @@ def compute_adding_slope(
# den = xa - xb
num = self.extf_sub(Qa[1], Qb[1])
den = self.extf_sub(Qa[0], Qb[0])
return self.extf_div(num, den, 2)
return self.fp2_div(num, den)

def build_sparse_line(
self,
Expand Down Expand Up @@ -273,7 +273,7 @@ def double_and_add_step(

num = self.extf_add(Qa[1], Qa[1])
den = self.extf_sub(x3, Qa[0])
λ2 = self.extf_neg(self.extf_add(λ1, self.extf_div(num, den, 2)))
λ2 = self.extf_neg(self.extf_add(λ1, self.fp2_div(num, den)))

# compute xr = λ2²-x1-x3
x4 = self.extf_sub(self.extf_sub(self.fp2_square(λ2), Qa[0]), x3)
Expand Down Expand Up @@ -307,7 +307,7 @@ def triple_step(
self.mul(num_tmp[1], self.get_constant(6)),
]
den = self.extf_add(Q[1], Q[1])
λ1 = self.extf_div(num, den, 2)
λ1 = self.fp2_div(num, den)

line1 = self.build_sparse_line(
R0=λ1, # Directly store λ as R0
Expand All @@ -322,7 +322,7 @@ def triple_step(
# compute λ2 = 2y/(x2 − x) − λ1.
# However in https://github.com/Consensys/gnark/blob/7cfcd5a723b0726dcfe75a5fc7249a23d690b00b/std/algebra/emulated/sw_bls12381/pairing.go#L548
# It's coded as x - x2.
λ2 = self.extf_sub(self.extf_div(den, self.extf_sub(Q[0], x2), 2), λ1)
λ2 = self.extf_sub(self.fp2_div(den, self.extf_sub(Q[0], x2)), λ1)

line2 = self.build_sparse_line(
R0=λ2,
Expand Down

0 comments on commit b8cd1be

Please sign in to comment.