Skip to content
This repository has been archived by the owner on Nov 4, 2024. It is now read-only.

Python Supercircuit #125

Merged
merged 43 commits into from
Sep 25, 2023
Merged

Python Supercircuit #125

merged 43 commits into from
Sep 25, 2023

Conversation

qwang98
Copy link
Collaborator

@qwang98 qwang98 commented Sep 10, 2023

Will be PR'ed to main.

@leolara Ready for review and the MiMC7 example is fully functional.

My technical approach

  • Pass over JSON of sub-circuit AST to Rust. Obtain uuid.
  • Pass over JSON of sub-circuit TraceWitness to Rust using the same uuid.
  • halo2_mock_prover function takes vector of uuids and compiles super circuit before passing it to the mock prover.

@qwang98 qwang98 changed the title initial commit Python Supercircuit WIP Sep 10, 2023
@qwang98 qwang98 requested a review from leolara September 10, 2023 22:32
@qwang98 qwang98 changed the title Python Supercircuit WIP Python Supercircuit Sep 10, 2023
@qwang98 qwang98 marked this pull request as ready for review September 10, 2023 22:33
Comment on lines 436 to 441

def enable(self: Lookup, enable_annotation: str, enable_expr: Expr):
enable = ASTConstraint(enable_annotation, enable_expr)
if self.enable is None:
enabler = ASTConstraint(enable_annotation, enable_expr)
if self.enabler is None:
for constraint, _ in self.exprs:
constraint = self.multiply_constraints(enable, constraint)
self.enable = enable
constraint = self.multiply_constraints(enabler, constraint)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Called enabler here so that it doesn't clash with the function name.

@qwang98 qwang98 force-pushed the steve/python-supercircuit-v3 branch from 2305e64 to 38b0a5e Compare September 10, 2023 22:43
Comment on lines 102 to 175
self.tables.add(table)
if self.super_circuit is None:
raise SyntaxError(
"Circuit: new_table() is only available for Circuit with initiated super_circuit field."
)
self.super_circuit.tables[table.uuid] = table
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Note that here I opted to store the tables under supercircuit and then circuit/steptype both have reference to the supercircuit, so they can reference the tables as well.

Comment on lines 100 to 139
pub fn chiquito_super_circuit_halo2_mock_prover(rust_ids: Vec<UUID>) {
let mut super_circuit_ctx = SuperCircuitContext::<Fr, ()>::default();

// super_circuit def
let config = config(SingleRowCellManager {}, SimpleStepSelectorBuilder {});
for rust_id in rust_ids.clone() {
let circuit_map_store = rust_id_to_halo2(rust_id);
let (circuit, _, _, _) = circuit_map_store;
let assignment = super_circuit_ctx.sub_circuit_with_ast(config.clone(), circuit);
add_assignment_generator_to_rust_id(assignment, rust_id);
}

let super_circuit = super_circuit_ctx.compile();
let compiled = chiquitoSuperCircuit2Halo2(&super_circuit);

let mut mapping_ctx = MappingContext::default();
for rust_id in rust_ids {
let circuit_map_store = rust_id_to_halo2(rust_id);
let (_, _, assignment_generator, witness) = circuit_map_store;
if let Some(witness) = witness {
mapping_ctx.map_with_witness(&assignment_generator.unwrap(), witness);
}
}

let super_assignments = mapping_ctx.get_super_assignments();

let circuit = ChiquitoHalo2SuperCircuit::new(compiled, super_assignments);

let prover = MockProver::<Fr>::run(10, &circuit, circuit.instance()).unwrap();

let result = prover.verify_par();

println!("result = {:#?}", result);

if let Err(failures) = &result {
for failure in failures.iter() {
println!("{}", failure);
}
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

chiquito_super_circuit_halo2_mock_prover

Comment on lines +40 to +49
def sub_circuit(self: SuperCircuit, sub_circuit: Circuit) -> Circuit:
assert self.mode == SuperCircuitMode.SETUP
if sub_circuit.rust_id != 0:
raise ValueError(
"SuperCircuit: sub_circuit() cannot be called twice on the same circuit."
)
ast_json: str = sub_circuit.get_ast_json()
sub_circuit.rust_id: int = rust_chiquito.ast_to_halo2(ast_json)
self.ast.sub_circuits[sub_circuit.rust_id] = sub_circuit.ast
return sub_circuit
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

sub_circuit Python function.

@qwang98
Copy link
Collaborator Author

qwang98 commented Sep 18, 2023

  1. For the mapping, you should serialise a SuperAssginments. You are doing something complicated with the uuid, just make sure that the AST uuid generated in the python side can be used to index the SuperAssigments.

I'd comment that historically we only serialize a TraceWitness, pass it over to Rust, and then process it with an AssignmentGenerator to obtain Assignments. We don't serialize Assignments directly, as it requires Python versions of Rust functions like assign_step, find_placement, and get_selector_assignment from assignment.rs, which are better left to Rust alone. Therefore, I wonder by saying serialize SuperAssignments, which is a mapping of UUID to Assignments, you actually meant to serialize a mapping UUID to TraceWitness?

That's actually what I did, to serialize a mapping of TraceWitness and then associate it with the corresponding AST under the same UUID. To walk you through the steps, here's the MiMC7 example:

mimc7 = Mimc7SuperCircuit()
mimc7_witnesses = mimc7.gen_witness((F(1), F(2)))
mimc7.halo2_mock_prover(mimc7_witnesses)

We first call gen_witness on the SuperCircuit mimc7, which returns a Dict[UUID, TraceWitness]. This TraceWitness dictionary is then passed to halo2_mock_prover, which serializes each TraceWitness, send it to Rust, and store it under the same UUID as its corresponding AST.

Then in Rust, the chiquito_super_circuit_halo2_mock_prover function converts each TraceWitness to an Assignments, using the AssignmentGenerator with the same UUID. These conversions are all done under a MappingContext, which eventually spits out a SuperAssignments.

Just to clarify, I created a UUID -> CircuitMapStore master mapping in Rust for the Python frontend, where CircuitMapStore is defined as:

type CircuitMapStore = (
    Circuit<Fr, ()>,
    ChiquitoHalo2<Fr>,
    Option<AssignmentGenerator<Fr, ()>>,
    Option<TraceWitness<Fr>>,
);

This is how all corresponding items are grouped under the same UUID.

Again, for your convenience, I'm marking the gen_witness Python function and halo2_mock_prover Python function each as a comment here.

Comment on lines 63 to 73
# called at the outermost level
# generates TraceWitness mapping
def gen_witness(self: SuperCircuit, args: Any) -> Dict[int, TraceWitness]:
self.mode = SuperCircuitMode.Mapping
self.mapping(args)
self.mode = SuperCircuitMode.NoMode
witnesses: Dict[int, TraceWitness] = self.ast.witnesses
del (
self.ast.witnesses
) # so that we can generate different witness mapping in the next gen_witness() call
return witnesses
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

gen_witness Python function for SuperCircuit

Comment on lines 75 to 85
def halo2_mock_prover(self: SuperCircuit, witnesses: Dict[int, TraceWitness]):
for rust_id, witness in witnesses.items():
witness_json: str = witness.get_witness_json()
if rust_id not in self.ast.sub_circuits:
raise ValueError(
f"SuperCircuit.halo2_mock_prover(): TraceWitness with rust_id {rust_id} not found in sub_circuits."
)
rust_chiquito.add_witness_to_rust_id(witness_json, rust_id)
rust_chiquito.super_circuit_halo2_mock_prover(
list(self.ast.sub_circuits.keys())
)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

halo2_mock_prover Python function for SuperCircuit

@qwang98
Copy link
Collaborator Author

qwang98 commented Sep 18, 2023

Ready for review again. Mostly responding to comments. I also merged latest changes. @leolara

@qwang98 qwang98 changed the base branch from steve/python-fixed-gen-lookup-mimc7 to main September 19, 2023 14:49
@qwang98
Copy link
Collaborator Author

qwang98 commented Sep 19, 2023

This PR now contains both fixed gen refactoring from #117 and super circuit related changes.

@leolara Ready for final review.

Changes:

  • Passes over super_witness which is a dict of uuid to trace witness json to halo2_mock_prover for super circuit rather than storing any trace witness on the Rust side. super_witness is parsed in Rust.
  • Removed dict of uuid to lookup table from super circuit. All lookup table will be accessed directly, either from the circuit itself, or from another circuit through imports.
  • Renamed read_only flag to finished_flag and created set_finished_flag method for encapsulation.

self.transition(eq(self.circuit.row + 1, self.circuit.row.next()))

self.add_lookup(
self.circuit.imports.apply(self.circuit.row).apply(self.circuit.c)
Copy link
Collaborator

Choose a reason for hiding this comment

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

@qwang98 I think it makes sense to call it self.circuit.constants_table

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed. I replaced the imports parameter for Circuit as **kwargs, so that the user can pass in arbitrarily named parameters for imports.

self.add(self.mimc7_last_step, x_value, k_value, c_value, row_value)


class Mimc7FirstStep(StepType):
Copy link
Collaborator

Choose a reason for hiding this comment

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

@qwang98 I think it is more clear to put the steps before the circuit

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

@@ -172,6 +162,27 @@ impl<F, TraceArgs> CircuitContext<F, TraceArgs> {
}
}

impl<F: Field + Hash, TraceArgs> CircuitContext<F, TraceArgs> {
/// Sets the fixed generation function for the circuit. The fixed generation function is
Copy link
Collaborator

Choose a reason for hiding this comment

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

@qwang98 this does not "set", but "executes" and sets the assignations

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

Circuit<Fr, ()>,
ChiquitoHalo2<Fr>,
Option<AssignmentGenerator<Fr, ()>>,
// Option<TraceWitness<Fr>>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

@qwang98 remove commented out line

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

circuit_map_store.2 = Some(assignment_generator);
});

println!("Added AssignmentGenerator to rust_id: {:?}", rust_id);
Copy link
Collaborator

Choose a reason for hiding this comment

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

@qwang98 remove println

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Collaborator

@leolara leolara left a comment

Choose a reason for hiding this comment

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

Just a few comments, but it is approved

@qwang98 qwang98 added this pull request to the merge queue Sep 25, 2023
Merged via the queue into main with commit e0faa21 Sep 25, 2023
4 checks passed
@alxkzmn alxkzmn deleted the steve/python-supercircuit-v3 branch August 7, 2024 11:09
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
2 participants