Skip to content

Commit

Permalink
optimize circuit size for irregular subcircuits
Browse files Browse the repository at this point in the history
  • Loading branch information
siq1 committed Sep 10, 2024
1 parent 37897a1 commit eee73d5
Show file tree
Hide file tree
Showing 30 changed files with 776 additions and 549 deletions.
1 change: 0 additions & 1 deletion expander_compiler/src/builder/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,6 @@ where
.map(|x| builder.in_to_out[*x])
.collect(),
num_inputs: circuit.num_inputs,
num_hint_inputs: circuit.num_hint_inputs,
};
Ok((new_circuit, builder))
}
Expand Down
5 changes: 0 additions & 5 deletions expander_compiler/src/builder/final_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,6 @@ impl<'a, C: Config> Builder<'a, C> {
instructions: fin_insns,
constraints,
num_inputs: self.in_circuit.num_inputs,
num_hint_inputs: self.in_circuit.num_hint_inputs,
})
}
}
Expand Down Expand Up @@ -276,7 +275,6 @@ mod tests {
constraints: vec![3],
outputs: vec![],
num_inputs: 2,
num_hint_inputs: 0,
},
);
assert_eq!(root.validate(), Ok(()));
Expand Down Expand Up @@ -306,7 +304,6 @@ mod tests {
constraints: vec![5],
outputs: vec![5],
num_inputs: 4,
num_hint_inputs: 0,
},
);
assert_eq!(root.validate(), Ok(()));
Expand All @@ -329,7 +326,6 @@ mod tests {
seed: 0,
num_circuits: RandomRange { min: 1, max: 10 },
num_inputs: RandomRange { min: 1, max: 10 },
num_hint_inputs: RandomRange { min: 0, max: 10 },
num_instructions: RandomRange { min: 1, max: 10 },
num_constraints: RandomRange { min: 0, max: 10 },
num_outputs: RandomRange { min: 1, max: 10 },
Expand Down Expand Up @@ -371,7 +367,6 @@ mod tests {
seed: 0,
num_circuits: RandomRange { min: 1, max: 20 },
num_inputs: RandomRange { min: 1, max: 3 },
num_hint_inputs: RandomRange { min: 0, max: 2 },
num_instructions: RandomRange { min: 30, max: 50 },
num_constraints: RandomRange { min: 0, max: 5 },
num_outputs: RandomRange { min: 1, max: 3 },
Expand Down
6 changes: 1 addition & 5 deletions expander_compiler/src/builder/final_build_opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ impl<C: Config> Builder<C> {
}
max_layer
}
VarSpec::RandomLinear(_) => panic!("unexpected situation"),
}
}

Expand Down Expand Up @@ -691,7 +692,6 @@ fn process_circuit<C: Config>(
outputs,
instructions,
num_inputs: circuit.num_inputs,
num_hint_inputs: circuit.num_hint_inputs,
},
builder,
))
Expand Down Expand Up @@ -757,7 +757,6 @@ mod tests {
constraints: vec![3],
outputs: vec![],
num_inputs: 2,
num_hint_inputs: 0,
},
);
assert_eq!(root.validate(), Ok(()));
Expand Down Expand Up @@ -787,7 +786,6 @@ mod tests {
constraints: vec![5],
outputs: vec![5],
num_inputs: 4,
num_hint_inputs: 0,
},
);
assert_eq!(root.validate(), Ok(()));
Expand All @@ -810,7 +808,6 @@ mod tests {
seed: 0,
num_circuits: RandomRange { min: 1, max: 10 },
num_inputs: RandomRange { min: 1, max: 10 },
num_hint_inputs: RandomRange { min: 0, max: 10 },
num_instructions: RandomRange { min: 1, max: 10 },
num_constraints: RandomRange { min: 0, max: 10 },
num_outputs: RandomRange { min: 1, max: 10 },
Expand Down Expand Up @@ -853,7 +850,6 @@ mod tests {
seed: 0,
num_circuits: RandomRange { min: 1, max: 20 },
num_inputs: RandomRange { min: 1, max: 3 },
num_hint_inputs: RandomRange { min: 0, max: 2 },
num_instructions: RandomRange { min: 30, max: 50 },
num_constraints: RandomRange { min: 0, max: 5 },
num_outputs: RandomRange { min: 1, max: 3 },
Expand Down
3 changes: 0 additions & 3 deletions expander_compiler/src/builder/hint_normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,6 @@ mod tests {
}],
outputs: vec![],
num_inputs: 2,
num_hint_inputs: 0,
},
);
assert_eq!(root.validate(), Ok(()));
Expand All @@ -393,7 +392,6 @@ mod tests {
seed: 0,
num_circuits: RandomRange { min: 1, max: 10 },
num_inputs: RandomRange { min: 1, max: 10 },
num_hint_inputs: RandomRange { min: 0, max: 10 },
num_instructions: RandomRange { min: 1, max: 10 },
num_constraints: RandomRange { min: 0, max: 10 },
num_outputs: RandomRange { min: 1, max: 10 },
Expand Down Expand Up @@ -435,7 +433,6 @@ mod tests {
seed: 0,
num_circuits: RandomRange { min: 1, max: 20 },
num_inputs: RandomRange { min: 1, max: 3 },
num_hint_inputs: RandomRange { min: 0, max: 2 },
num_instructions: RandomRange { min: 30, max: 50 },
num_constraints: RandomRange { min: 0, max: 5 },
num_outputs: RandomRange { min: 1, max: 3 },
Expand Down
3 changes: 1 addition & 2 deletions expander_compiler/src/circuit/ir/common/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,9 @@ where
for (circuit_id, circuit) in self.circuits.iter() {
writeln!(
f,
"Circuit {} numIn={} numHintIn={} numOut={} numCon={}",
"Circuit {} numIn={} numOut={} numCon={}",
circuit_id,
circuit.num_inputs,
circuit.num_hint_inputs,
circuit.outputs.len(),
circuit.constraints.len()
)?;
Expand Down
61 changes: 12 additions & 49 deletions expander_compiler/src/circuit/ir/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ pub trait IrConfig: Debug + Clone + Default + Hash + PartialEq + Eq {
const ALLOW_DUPLICATE_SUB_CIRCUIT_INPUTS: bool;
const ALLOW_DUPLICATE_CONSTRAINTS: bool;
const ALLOW_DUPLICATE_OUTPUTS: bool;
const HAS_HINT_INPUT: bool;
}

pub trait Instruction<C: Config>: Debug + Clone + Hash + PartialEq + Eq {
Expand Down Expand Up @@ -90,7 +89,6 @@ pub struct Circuit<Irc: IrConfig> {
pub constraints: Vec<Irc::Constraint>,
pub outputs: Vec<usize>,
pub num_inputs: usize,
pub num_hint_inputs: usize,
}

#[derive(Default, Debug, Clone, PartialEq, Eq)]
Expand All @@ -102,19 +100,10 @@ pub struct RootCircuit<Irc: IrConfig> {

impl<Irc: IrConfig> Circuit<Irc> {
pub fn get_num_inputs_all(&self) -> usize {
if Irc::HAS_HINT_INPUT {
self.num_inputs + self.num_hint_inputs
} else {
self.num_inputs
}
self.num_inputs
}

fn validate_variable_references(&self, num_public_inputs: usize) -> Result<(), Error> {
if !Irc::HAS_HINT_INPUT && self.num_hint_inputs != 0 {
return Err(Error::InternalError(
"hint input is not allowed".to_string(),
));
}
let mut cur_var_max = self.get_num_inputs_all();
for insn in self.instructions.iter() {
for term in insn.inputs() {
Expand Down Expand Up @@ -196,12 +185,6 @@ pub type EvalOk<Irc> = (
bool,
);

type EvalSubOk<'a, Irc> = (
Vec<<<Irc as IrConfig>::Config as Config>::CircuitField>,
&'a [<<Irc as IrConfig>::Config as Config>::CircuitField],
bool,
);

impl<Irc: IrConfig> RootCircuit<Irc> {
pub fn sub_circuit_graph_vertices(&self) -> HashSet<usize> {
self.circuits.keys().cloned().collect()
Expand Down Expand Up @@ -297,22 +280,7 @@ impl<Irc: IrConfig> RootCircuit<Irc> {

pub fn input_size(&self) -> usize {
// tests of this function are in for_layering
if !Irc::HAS_HINT_INPUT {
return self.circuits[&0].num_inputs;
}
let order = self.topo_order();
let mut sub_hint_size: HashMap<usize, usize> = HashMap::new();
for i in order.iter().rev() {
let circuit = &self.circuits[i];
let mut hint_size = circuit.num_hint_inputs;
for insn in circuit.instructions.iter() {
if let Some((sub_circuit_id, _, _)) = insn.as_sub_circuit_call() {
hint_size += sub_hint_size[&sub_circuit_id];
}
}
sub_hint_size.insert(*i, hint_size);
}
self.circuits[&0].num_inputs + sub_hint_size[&0]
self.circuits[&0].num_inputs
}

pub fn topo_order(&self) -> Vec<usize> {
Expand All @@ -328,11 +296,12 @@ impl<Irc: IrConfig> RootCircuit<Irc> {
inputs: Vec<<Irc::Config as Config>::CircuitField>,
) -> Result<EvalOk<Irc>, Error> {
assert_eq!(inputs.len(), self.input_size());
let (root_input, hint_input) = inputs.split_at(self.circuits[&0].num_inputs);
let (res, rem, cond) =
self.eval_unsafe_sub(&self.circuits[&0], root_input.to_vec(), hint_input)?;
assert_eq!(rem.len(), 0);
Ok((res, cond))
let (res, mut cond) = self.eval_unsafe_sub(&self.circuits[&0], inputs)?;
let (t, res) = res.split_at(self.expected_num_output_zeroes);
for x in t {
cond &= x.is_zero();
}
Ok((res.to_vec(), cond))
}

pub fn eval_unsafe(
Expand All @@ -342,18 +311,14 @@ impl<Irc: IrConfig> RootCircuit<Irc> {
self.eval_unsafe_with_errors(inputs).unwrap()
}

fn eval_unsafe_sub<'a>(
fn eval_unsafe_sub(
&self,
circuit: &Circuit<Irc>,
inputs: Vec<<Irc::Config as Config>::CircuitField>,
hint_inputs: &'a [<Irc::Config as Config>::CircuitField],
) -> Result<EvalSubOk<'a, Irc>, Error> {
) -> Result<EvalOk<Irc>, Error> {
let mut values = vec![<Irc::Config as Config>::CircuitField::zero(); 1];
values.extend(inputs);
let (cur_hint_input, rem_hint_inputs) = hint_inputs.split_at(circuit.num_hint_inputs);
values.extend(cur_hint_input);
let mut cond = true;
let mut hint_inputs = rem_hint_inputs;
for insn in circuit.instructions.iter() {
match insn.eval_unsafe(&values) {
EvalResult::Value(v) => {
Expand All @@ -363,12 +328,10 @@ impl<Irc: IrConfig> RootCircuit<Irc> {
values.append(&mut vs);
}
EvalResult::SubCircuitCall(sub_circuit_id, inputs) => {
let (res, rem, sub_cond) = self.eval_unsafe_sub(
let (res, sub_cond) = self.eval_unsafe_sub(
&self.circuits[&sub_circuit_id],
inputs.iter().map(|&i| values[i]).collect(),
hint_inputs,
)?;
hint_inputs = rem;
values.extend(res);
cond &= sub_cond;
}
Expand All @@ -384,6 +347,6 @@ impl<Irc: IrConfig> RootCircuit<Irc> {
for &o in circuit.outputs.iter() {
res.push(values[o]);
}
Ok((res, hint_inputs, cond))
Ok((res, cond))
}
}
21 changes: 0 additions & 21 deletions expander_compiler/src/circuit/ir/common/opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,12 +263,10 @@ impl<Irc: IrConfig> RootCircuit<Irc> {
}
let is_visited = |x: &Element| visited[vertice_id(x)];
// now we can process each circuit
let mut sub_hint_mask: HashMap<usize, Vec<bool>> = HashMap::new();
let mut new_circuits: HashMap<usize, Circuit<Irc>> = HashMap::new();
for circuit_id in order.iter().rev() {
let circuit = self.circuits.get(circuit_id).unwrap();
let mut var_map: Vec<usize> = vec![EMPTY];
let mut hint_mask: Vec<bool> = Vec::new();
let mut new_instructions: Vec<Irc::Instruction> = Vec::new();
let mut mapped_var_max: usize = 0;
for i in 1..=circuit.get_num_inputs_all() {
Expand All @@ -283,30 +281,16 @@ impl<Irc: IrConfig> RootCircuit<Irc> {
var_map.push(EMPTY);
}
}
for var in var_map
.iter()
.take(circuit.get_num_inputs_all() + 1)
.skip(circuit.num_inputs + 1)
{
hint_mask.push(*var != EMPTY);
}
for (i, insn) in circuit.instructions.iter().enumerate() {
if !is_visited(&Element {
circuit_id: *circuit_id,
id: i,
typ: Insn,
}) {
if let Some((sub_circuit_id, _, _)) = insn.as_sub_circuit_call() {
hint_mask.resize(
sub_hint_mask[&sub_circuit_id].len() + hint_mask.len(),
false,
);
}
var_map.resize(var_map.len() + insn.num_outputs(), EMPTY);
continue;
}
if let Some((sub_circuit_id, inputs, num_outputs)) = insn.as_sub_circuit_call() {
hint_mask.extend(sub_hint_mask[&sub_circuit_id].iter());
let mut new_inputs: Vec<usize> = Vec::new();
let mut new_num_outputs: usize = 0;
for j in 0..inputs.len() {
Expand Down Expand Up @@ -369,12 +353,8 @@ impl<Irc: IrConfig> RootCircuit<Irc> {
num_inputs: (1..=circuit.num_inputs)
.filter(|x| var_map[*x] != EMPTY)
.count(),
num_hint_inputs: (circuit.num_inputs + 1..=circuit.get_num_inputs_all())
.filter(|x| var_map[*x] != EMPTY)
.count(),
},
);
sub_hint_mask.insert(*circuit_id, hint_mask);
}
let mut root_input_mask: Vec<bool> = Vec::new();
for i in 1..=self.circuits[&0].num_inputs {
Expand All @@ -384,7 +364,6 @@ impl<Irc: IrConfig> RootCircuit<Irc> {
typ: Var,
}));
}
root_input_mask.extend(sub_hint_mask[&0].iter());
let mut input_mapping_vec: Vec<usize> = Vec::new();
let mut new_input_size: usize = 0;
for v in root_input_mask.iter() {
Expand Down
9 changes: 1 addition & 8 deletions expander_compiler/src/circuit/ir/common/rand_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ pub struct RandomCircuitConfig {
pub seed: usize,
pub num_circuits: RandomRange,
pub num_inputs: RandomRange,
pub num_hint_inputs: RandomRange,
pub num_instructions: RandomRange,
pub num_constraints: RandomRange,
pub num_outputs: RandomRange,
Expand Down Expand Up @@ -64,16 +63,11 @@ where
}
for (i, circuit_id) in circuit_ids.iter().enumerate().rev() {
let num_inputs = config.num_inputs.random(&mut rnd);
let num_hint_inputs = if Irc::HAS_HINT_INPUT {
config.num_hint_inputs.random(&mut rnd)
} else {
0
};
let num_instructions = config.num_instructions.random(&mut rnd);
let mut num_constraints = config.num_constraints.random(&mut rnd);
let num_outputs = config.num_outputs.random(&mut rnd);
let mut instructions = Vec::with_capacity(num_instructions);
let mut num_vars = num_inputs + num_hint_inputs;
let mut num_vars = num_inputs;
has_constraint[i] = num_constraints > 0;
for _ in 0..num_instructions {
if rnd.gen::<f64>() < config.sub_circuit_prob && i != num_circuits - 1 {
Expand Down Expand Up @@ -145,7 +139,6 @@ where
constraints,
outputs,
num_inputs,
num_hint_inputs,
},
);
}
Expand Down
9 changes: 0 additions & 9 deletions expander_compiler/src/circuit/ir/common/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,18 @@ where
self.constraints.serialize_into(&mut writer)?;
self.outputs.serialize_into(&mut writer)?;
self.num_inputs.serialize_into(&mut writer)?;
if Irc::HAS_HINT_INPUT {
self.num_hint_inputs.serialize_into(&mut writer)?;
}
Ok(())
}
fn deserialize_from<R: Read>(mut reader: R) -> Result<Self, IoError> {
let instructions = Vec::<Irc::Instruction>::deserialize_from(&mut reader)?;
let constraints = Vec::<Irc::Constraint>::deserialize_from(&mut reader)?;
let outputs = Vec::<usize>::deserialize_from(&mut reader)?;
let num_inputs = usize::deserialize_from(&mut reader)?;
let num_hint_inputs = if Irc::HAS_HINT_INPUT {
usize::deserialize_from(&mut reader)?
} else {
0
};
Ok(Circuit {
instructions,
constraints,
outputs,
num_inputs,
num_hint_inputs,
})
}
}
Expand Down
Loading

0 comments on commit eee73d5

Please sign in to comment.