Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix!: roll back the compress_selectors logic #322

Merged
merged 30 commits into from
May 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
05c9292
fix: correct the logic of convert selectors to fixed column
guorong009 Apr 29, 2024
a002524
refactor: remove the "create_proof_custom_with_engine"
guorong009 Apr 29, 2024
0152297
chore: clippy
guorong009 Apr 29, 2024
5627fce
chore: add ci for running examples
guorong009 Apr 30, 2024
41e4da2
chore: fix the ci
guorong009 Apr 30, 2024
8f38f79
chore: clean the ci check script
guorong009 Apr 30, 2024
47157ea
fix: roll back wrong updates
guorong009 Apr 30, 2024
151112e
fix!: correct the "compile_circuit" process
guorong009 Apr 30, 2024
61d2d05
chore: update import
guorong009 Apr 30, 2024
0469a7b
chore: fmt
guorong009 Apr 30, 2024
ba65fd8
feat: roll back "compress_selectors" logic
guorong009 May 6, 2024
0125d5f
chore: update the .gitignore
guorong009 May 6, 2024
2034660
fix: update the "layout.rs"
guorong009 May 6, 2024
09f63a2
feat: remove the "compress_selectors" from several utils
guorong009 May 6, 2024
ef7573d
fix: remove the "selectors_to_fixed" field from "ConstraintSystem"
guorong009 May 6, 2024
1688754
feat: remove the "compress_selectors" option & default to "true"
guorong009 May 6, 2024
0fb9792
feat: update the "vk_read" & "pk_read"
guorong009 May 6, 2024
33b6dbd
chore: remove leftovers
guorong009 May 6, 2024
bbc773b
fix: re-enable the "compress_selectors" option
guorong009 May 6, 2024
f614ddd
chore: roll back some needless update
guorong009 May 6, 2024
2ef0805
test: add integration test for "compress_selectors"
guorong009 May 8, 2024
6ad6b4b
chore: fmt + clippy
guorong009 May 8, 2024
df25fee
test: use the custom circuit for integration test
guorong009 May 8, 2024
deed874
chore: fix clippy
guorong009 May 8, 2024
2bcb970
chore: fix error mapping
guorong009 May 8, 2024
379491e
chore: some renamings + some comments
guorong009 May 8, 2024
b8aa324
fix: remove wrong attr & comments
guorong009 May 9, 2024
3190c57
fix!: remove the "create_proof_custom_with_engine"
guorong009 May 9, 2024
46d1a85
chore: some housekeeping
guorong009 May 10, 2024
95c6bac
chore: add detailed comments
guorong009 May 10, 2024
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
22 changes: 22 additions & 0 deletions .github/scripts/run-examples.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/sh

# Get the list of examples from "examples" dir & Cargo.toml
EXAMPLES_WITH_FEATURES=$(awk '/^\[\[example\]\]/ { getline; name=$3; name=substr(name, 2, length(name)-2); getline; if ($1 == "required-features") { features=$NF; gsub(/["\[\]]/, "", features); print name "#" features } }' ./halo2_proofs/Cargo.toml)
EXAMPLES_WITHOUT_FEATURES=$(ls ./halo2_proofs/examples/*.rs | xargs -n1 basename -s .rs)

# Remove examples with features listed in Cargo.toml from examples without features
EXAMPLES_WITHOUT_FEATURES=$(echo "$EXAMPLES_WITHOUT_FEATURES" | grep -vFx "$(echo "$EXAMPLES_WITH_FEATURES" | cut -d '#' -f 1)")

# Combine examples with and without features
EXAMPLES=$(echo "$EXAMPLES_WITH_FEATURES $EXAMPLES_WITHOUT_FEATURES" | tr ' ' '\n' | sort -u | tr '\n' ' ')

# Run the examples
for example in $EXAMPLES; do
if [ "$(echo "$example" | grep '#')" ]; then
name=$(echo $example | cut -d '#' -f 1)
features=$(echo $example | cut -d '#' -f 2)
cargo run --package halo2_proofs --example $name --features $features
else
cargo run --package halo2_proofs --example $example
fi
done
11 changes: 11 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ jobs:
command: test
args: --verbose --release --workspace --no-default-features --features "${{ matrix.features }}"

examples:
name: Run the examples
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions-rs/toolchain@v1
- name: Run examples
run: |
.github/scripts/run-examples.sh

build:
name: Build target ${{ matrix.target }}
runs-on: ubuntu-latest
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ Cargo.lock
.vscode
**/*.html
.DS_Store

layout.png
serialization-test.pk
54 changes: 16 additions & 38 deletions halo2_frontend/src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::plonk::{
permutation,
sealed::{self, SealedPhase},
Advice, Assignment, Circuit, ConstraintSystem, FirstPhase, Fixed, FloorPlanner, Instance,
SecondPhase, SelectorsToFixed, ThirdPhase,
SecondPhase, ThirdPhase,
};
use halo2_middleware::circuit::{Any, CompiledCircuit, Preprocessing};
use halo2_middleware::ff::{BatchInvert, Field};
Expand All @@ -31,36 +31,6 @@ pub mod layouter;

pub use table_layouter::{SimpleTableLayouter, TableLayouter};

/// Compile a circuit, only generating the `Config` and the `ConstraintSystem` related information,
/// skipping all preprocessing data.
/// The `ConcreteCircuit::Config`, `ConstraintSystem<F>` and `SelectorsToFixed` are outputs for the
/// frontend itself, which will be used for witness generation and fixed column assignment.
/// The `ConstraintSystem<F>` can be converted to `ConstraintSystemMid<F>` to be used to interface
/// with the backend.
pub fn compile_circuit_cs<F: Field, ConcreteCircuit: Circuit<F>>(
compress_selectors: bool,
#[cfg(feature = "circuit-params")] params: ConcreteCircuit::Params,
) -> (
ConcreteCircuit::Config,
ConstraintSystem<F>,
SelectorsToFixed,
) {
let mut cs = ConstraintSystem::default();
#[cfg(feature = "circuit-params")]
let config = ConcreteCircuit::configure_with_params(&mut cs, params);
#[cfg(not(feature = "circuit-params"))]
let config = ConcreteCircuit::configure(&mut cs);
let cs = cs;

let (cs, selectors_to_fixed) = if compress_selectors {
cs.selectors_to_fixed_compressed()
} else {
cs.selectors_to_fixed_direct()
};

(config, cs, selectors_to_fixed)
}

/// Compile a circuit. Runs configure and synthesize on the circuit in order to materialize the
/// circuit into its columns and the column configuration; as well as doing the fixed column and
/// copy constraints assignments. The output of this function can then be used for the key
Expand All @@ -81,16 +51,17 @@ pub fn compile_circuit<F: Field, ConcreteCircuit: Circuit<F>>(
> {
let n = 2usize.pow(k);

// After this, the ConstraintSystem should not have any selectors: `verify` does not need them, and `keygen_pk` regenerates `cs` from scratch anyways.
let (config, cs, selectors_to_fixed) = compile_circuit_cs::<_, ConcreteCircuit>(
compress_selectors,
#[cfg(feature = "circuit-params")]
circuit.params(),
);
let mut cs = ConstraintSystem::default();
#[cfg(feature = "circuit-params")]
let config = ConcreteCircuit::configure_with_params(&mut cs, circuit.params());
#[cfg(not(feature = "circuit-params"))]
let config = ConcreteCircuit::configure(&mut cs);
let cs = cs;

if n < cs.minimum_rows() {
return Err(Error::not_enough_rows_available(k));
}

let mut assembly = plonk::keygen::Assembly {
k,
fixed: vec![vec![F::ZERO.into(); n]; cs.num_fixed_columns],
Expand All @@ -109,7 +80,14 @@ pub fn compile_circuit<F: Field, ConcreteCircuit: Circuit<F>>(
)?;

let mut fixed = batch_invert_assigned(assembly.fixed);
let selector_polys = selectors_to_fixed.convert(assembly.selectors);
let (cs, selector_polys) = if compress_selectors {
cs.compress_selectors(assembly.selectors)
} else {
// After this, the ConstraintSystem should not have any selectors: `verify` does not need them, and `keygen_pk` regenerates `cs` from scratch anyways.
let selectors = std::mem::take(&mut assembly.selectors);
cs.directly_convert_selectors_to_fixed(selectors)
};

fixed.extend(selector_polys);

// sort the "copies" for deterministic ordering
Expand Down
3 changes: 1 addition & 2 deletions halo2_frontend/src/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -710,8 +710,7 @@ impl<F: FromUniformBytes<64> + Ord> MockProver<F> {
)?;
}

let (cs, selectors_to_fixed) = prover.cs.selectors_to_fixed_compressed();
let selector_polys = selectors_to_fixed.convert(prover.selectors.clone());
let (cs, selector_polys) = prover.cs.compress_selectors(prover.selectors.clone());
prover.cs = cs;
prover.fixed.extend(selector_polys.into_iter().map(|poly| {
let mut v = vec![CellValue::Unassigned; n];
Expand Down
2 changes: 1 addition & 1 deletion halo2_frontend/src/dev/cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ impl<G: PrimeGroup, ConcreteCircuit: Circuit<G::Scalar>> CircuitCost<G, Concrete
cs.constants.clone(),
)
.unwrap();
let (cs, _) = cs.selectors_to_fixed_compressed();
let (cs, _) = cs.compress_selectors(layout.selectors);

assert!((1 << k) >= cs.minimum_rows());

Expand Down
3 changes: 1 addition & 2 deletions halo2_frontend/src/dev/graph/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,7 @@ impl CircuitLayout {
cs.constants.clone(),
)
.unwrap();
let (cs, selectors_to_fixed) = cs.selectors_to_fixed_compressed();
let selector_polys = selectors_to_fixed.convert::<F>(layout.selectors);
let (cs, selector_polys) = cs.compress_selectors(layout.selectors);
let non_selector_fixed_columns = cs.num_fixed_columns - selector_polys.len();

// Figure out what order to render the columns in.
Expand Down
168 changes: 45 additions & 123 deletions halo2_frontend/src/plonk/circuit/constraint_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,9 +276,6 @@ pub struct ConstraintSystem<F: Field> {
/// fixed column that they were compressed into. This is just used by dev
/// tooling right now.
pub(crate) selector_map: Vec<Column<Fixed>>,
/// Status boolean indicating wether the selectors have been already transformed to fixed columns
/// or not.
selectors_to_fixed: bool,

pub(crate) gates: Vec<Gate<F>>,
pub(crate) advice_queries: Vec<(Column<Advice>, Rotation)>,
Expand Down Expand Up @@ -310,55 +307,6 @@ pub struct ConstraintSystem<F: Field> {
pub(crate) minimum_degree: Option<usize>,
}

/// Helper struct with the parameters required to convert selector assignments into fixed column
/// assignments.
pub struct SelectorsToFixed {
compress: bool,
num_selectors: usize,
max_degree: usize,
degrees: Vec<usize>,
}

impl SelectorsToFixed {
/// Convert selector assignments into fixed column assignments based on the parameters in
/// `SelectorsToFixed`.
pub fn convert<F: Field>(&self, selectors: Vec<Vec<bool>>) -> Vec<Vec<F>> {
// The number of provided selector assignments must be the number we
// counted for this constraint system.
assert_eq!(selectors.len(), self.num_selectors);

if self.compress {
let (polys, _) = compress_selectors::process(
selectors
.into_iter()
.zip(self.degrees.iter())
.enumerate()
.map(
|(i, (activations, max_degree))| compress_selectors::SelectorDescription {
selector: i,
activations,
max_degree: *max_degree,
},
)
.collect(),
self.max_degree,
|| Expression::Constant(F::ZERO),
);
polys
} else {
selectors
.into_iter()
.map(|selector| {
selector
.iter()
.map(|b| if *b { F::ONE } else { F::ZERO })
.collect::<Vec<_>>()
})
.collect()
}
}
}

impl<F: Field> Default for ConstraintSystem<F> {
fn default() -> ConstraintSystem<F> {
ConstraintSystem {
Expand All @@ -371,7 +319,6 @@ impl<F: Field> Default for ConstraintSystem<F> {
advice_column_phase: Vec::new(),
challenge_phase: Vec::new(),
selector_map: vec![],
selectors_to_fixed: false,
gates: vec![],
fixed_queries: Vec::new(),
advice_queries: Vec::new(),
Expand Down Expand Up @@ -638,19 +585,23 @@ impl<F: Field> ConstraintSystem<F> {
});
}

/// Transform this `ConstraintSystem` into an equivalent one that replaces the selector columns
/// by fixed columns applying compression where possible.
/// This will compress selectors together depending on their provided
/// assignments. This `ConstraintSystem` will then be modified to add new
/// fixed columns (representing the actual selectors) and will return the
/// polynomials for those columns. Finally, an internal map is updated to
/// find which fixed column corresponds with a given `Selector`.
///
/// Panics if called twice.
pub fn selectors_to_fixed_compressed(mut self) -> (Self, SelectorsToFixed) {
if self.selectors_to_fixed {
panic!("the selectors have already been transformed to fixed columns");
}
/// Do not call this twice. Yes, this should be a builder pattern instead.
pub fn compress_selectors(mut self, selectors: Vec<Vec<bool>>) -> (Self, Vec<Vec<F>>) {
// The number of provided selector assignments must be the number we
// counted for this constraint system.
assert_eq!(selectors.len(), self.num_selectors);

// Compute the maximal degree of every selector. We only consider the
// expressions in gates, as lookup arguments cannot support simple
// selectors. Selectors that are complex or do not appear in any gates
// will have degree zero.
let mut degrees = vec![0; self.num_selectors];
let mut degrees = vec![0; selectors.len()];
for expr in self.gates.iter().flat_map(|gate| gate.polys.iter()) {
if let Some(selector) = expr.extract_simple_selector() {
degrees[selector.0] = max(degrees[selector.0], expr.degree());
Expand All @@ -660,22 +611,20 @@ impl<F: Field> ConstraintSystem<F> {
// We will not increase the degree of the constraint system, so we limit
// ourselves to the largest existing degree constraint.
let max_degree = self.degree();
let selectors_to_fixed = SelectorsToFixed {
compress: true,
num_selectors: self.num_selectors,
max_degree,
degrees: degrees.clone(),
};

let mut new_columns = vec![];
let (_, selector_assignment) = compress_selectors::process(
(0..self.num_selectors)
let (polys, selector_assignment) = compress_selectors::process(
selectors
.into_iter()
.zip(degrees)
.map(|(i, max_degree)| compress_selectors::SelectorDescription {
selector: i,
activations: vec![],
max_degree,
})
.enumerate()
.map(
|(i, (activations, max_degree))| compress_selectors::SelectorDescription {
selector: i,
activations,
max_degree,
},
)
.collect(),
max_degree,
|| {
Expand Down Expand Up @@ -705,68 +654,41 @@ impl<F: Field> ConstraintSystem<F> {
.map(|a| a.unwrap())
.collect::<Vec<_>>();
self.replace_selectors_with_fixed(&selector_replacements);
self.selectors_to_fixed = true;

(self, selectors_to_fixed)
}

/// This will compress selectors together depending on their provided
/// assignments. This `ConstraintSystem` will then be modified to add new
/// fixed columns (representing the actual selectors) and will return the
/// polynomials for those columns. Finally, an internal map is updated to
/// find which fixed column corresponds with a given `Selector`.
///
/// Do not call this twice. Yes, this should be a builder pattern instead.
#[deprecated(note = "Use `selectors_to_fixed_compressed` instead")]
pub fn compress_selectors(self, selectors: Vec<Vec<bool>>) -> (Self, Vec<Vec<F>>) {
let (cs, selectors_to_fixed) = self.selectors_to_fixed_compressed();
let fixed_polys = selectors_to_fixed.convert(selectors);

(cs, fixed_polys)
(self, polys)
}

/// Transform this `ConstraintSystem` into an equivalent one that replaces the selector columns
/// by fixed columns with a direct mapping.
///
/// Panics if called twice.
pub fn selectors_to_fixed_direct(mut self) -> (Self, SelectorsToFixed) {
if self.selectors_to_fixed {
panic!("the selectors have already been transformed to fixed columns");
}
let selectors_to_fixed = SelectorsToFixed {
compress: false,
num_selectors: self.num_selectors,
max_degree: 0,
degrees: vec![],
};
/// Does not combine selectors and directly replaces them everywhere with fixed columns.
pub fn directly_convert_selectors_to_fixed(
mut self,
selectors: Vec<Vec<bool>>,
) -> (Self, Vec<Vec<F>>) {
// The number of provided selector assignments must be the number we
// counted for this constraint system.
assert_eq!(selectors.len(), self.num_selectors);

let selector_replacements: Vec<_> = (0..self.num_selectors)
.map(|_| {
let (polys, selector_replacements): (Vec<_>, Vec<_>) = selectors
.into_iter()
.map(|selector| {
let poly = selector
.iter()
.map(|b| if *b { F::ONE } else { F::ZERO })
.collect::<Vec<_>>();
let column = self.fixed_column();
let rotation = Rotation::cur();
Expression::Fixed(FixedQuery {
let expr = Expression::Fixed(FixedQuery {
index: Some(self.query_fixed_index(column, rotation)),
column_index: column.index,
rotation,
})
});
(poly, expr)
})
.collect();
.unzip();

self.replace_selectors_with_fixed(&selector_replacements);
self.selectors_to_fixed = true;
(self, selectors_to_fixed)
}

/// Does not combine selectors and directly replaces them everywhere with fixed columns.
#[deprecated(note = "Use `selectors_to_fixed_direct` instead")]
pub fn directly_convert_selectors_to_fixed(
self,
selectors: Vec<Vec<bool>>,
) -> (Self, Vec<Vec<F>>) {
let (cs, selectors_to_fixed) = self.selectors_to_fixed_direct();
let fixed_polys = selectors_to_fixed.convert(selectors);
self.num_selectors = 0;

(cs, fixed_polys)
(self, polys)
}

fn replace_selectors_with_fixed(&mut self, selector_replacements: &[Expression<F>]) {
Expand Down
Loading
Loading