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

halo2 backend #151

Merged
merged 3 commits into from
May 25, 2023
Merged

halo2 backend #151

merged 3 commits into from
May 25, 2023

Conversation

leonardoalt
Copy link
Member

@leonardoalt leonardoalt commented Apr 6, 2023

Extracted from the rest of #102. The other PR got a bit too complicated to rebase onto main because of the amount of changes in the main branch and the merge commits into the feature branch.

The author credits are kept in this PR.

Notes from @Schaeff:

  • the number of required extra rows is not trivial to calculate and seems to depend on the exact Halo2 configuration. Doubling the table size seems fine for this PR.
  • This PR removes the wrapping behavior. This means that if this is merged, the first row is not constrained together with the last row. Moreover, witness generation does not use wrapping logic.
  • This PR removes the requirement on the number of rows to be a power of two. This can be enforced by the backend instead: Halo2 just extends to the next power of two (if there is enough space) and say estark should throw.

src/halo2/circuit_data.rs Outdated Show resolved Hide resolved
src/bin/compiler.rs Outdated Show resolved Hide resolved
// | bla_bla_bla | 1 | 1 | bla_bla_bla | |
// | bla_bla_bla | 1 | 1 | bla_bla_bla | |> witness + fixed 2^(k-1)
// | ... | ... | ... | ... | |
// | bla_bla_bla | 1 | 0 | bla_bla_bla | / <- __enable_next == 0 since there's no state transition
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this mechanism now?

Copy link

Choose a reason for hiding this comment

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

HALO2 contains rows that are "poisoned" and are unusable, and PIL/zkWASM does not know about this fact, I have to activate polinomials on usable rows.

.selector
.clone()
.map_or(Expr::Const(BigUint::one()), |expr| {
expression_2_expr(&cd, &expr, int_to_field).0
Copy link
Member

Choose a reason for hiding this comment

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

Why can we ignore the fact that they contain rotations for plookups?

Copy link

Choose a reason for hiding this comment

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

expression_2_expr takes care about rotations and are used in both left and right expressions.
But probably are you talking about something else..

@leonardoalt
Copy link
Member Author

leonardoalt commented Apr 7, 2023

@adria0 I moved the Halo2 PR here because the other one became too complicated to rebase after a lot of changes to main. Chris has some questions above that I can't answer myself, could you answer those? Thanks!

@leonardoalt
Copy link
Member Author

Just pushed the changes to the review comments I could fix.

@chriseth
Copy link
Member

chriseth commented Apr 7, 2023

@adria0 I still don't fully understand the enable_next mechanism. Is it true that this PR removes any constraint that connects the last and the first row? Does this allow invalid proofs to be verified if they rely on that property?

@chriseth
Copy link
Member

chriseth commented Apr 7, 2023

If yes, we could check if we rely on such a wrap-around property.

@leonardoalt
Copy link
Member Author

leonardoalt commented Apr 7, 2023

If yes, we could check if we rely on such a wrap-around property.

We do on the "binary" state machine (for the FACTOR and input byte polys)

@adria0
Copy link

adria0 commented Apr 7, 2023

BTW,

@adria0 I still don't fully understand the enable_next mechanism. Is it true that this PR removes any constraint that connects the last and the first row? Does this allow invalid proofs to be verified if they rely on that property?

Oh, yes, you're right on this @chriseth, since last rows are unusable in HALO2 👍

@adria0
Copy link

adria0 commented Apr 7, 2023

BTW, this set_field_mod is really terrible and can break tests when executed in multithread.
For HALO2 tests, I'm going to change to use a ReentrantMutex<RefCell<BigInt>> that is still a patch but just a little less 😅

pub fn with_field_mod<F: FnOnce()>(n: BigInt, f: F) {
    let _ = ReentrantMutexGuard::map((*FIELD_MOD).lock(), |field_mod| {
        let previous = field_mod.borrow().clone();
        *field_mod.borrow_mut() = n;
        f();
        *FIELD_MOD.lock().borrow_mut() = previous;
        &()
    });
}

@Schaeff Schaeff force-pushed the halo_poc branch 2 times, most recently from 52027d8 to 9709a38 Compare May 12, 2023 13:57
halo2/src/mock_prover.rs Outdated Show resolved Hide resolved
@@ -118,12 +118,6 @@ pub fn generate<'a, T: FieldElement>(
values[col].1.push(v);
}
}
for (col, v) in generator.compute_next_row(0).into_iter().enumerate() {
Copy link
Member

Choose a reason for hiding this comment

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

I think we should still check it, because halo is not the only backend.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe the halo2 backend should not even modify the pil but instead only check that the last witness rows are free. Unconstraining the last rows could be an additional step.

Copy link
Member Author

Choose a reason for hiding this comment

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

We need to make all the compiler built-in stuff independent from wrapping

Copy link
Member Author

Choose a reason for hiding this comment

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

Should this be brought back based on the comments from @chriseth ?

Copy link
Collaborator

Choose a reason for hiding this comment

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

  • If we bring it back, then we are requiring a halo2 program to have correct wrapped values, which it does not need to have. It's estark leaking onto halo2.
  • If we keep it out, we allow an estark program to have incorrect wrapped values, which will fail in estark. It's estark being stricter than powdr.

I prefer the latter until we solve #300 more cleanly.

Copy link
Member Author

Choose a reason for hiding this comment

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

sgtm

Copy link
Member

Choose a reason for hiding this comment

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

Can we simply use this as a flag that is returned from witgen?

@Schaeff Schaeff force-pushed the halo_poc branch 2 times, most recently from bd57835 to e40d65e Compare May 19, 2023 12:08
@Schaeff Schaeff force-pushed the halo_poc branch 2 times, most recently from 8fa7a73 to 1676bf8 Compare May 22, 2023 14:53
@@ -82,6 +87,19 @@ enum Commands {
force: bool,
},

/// Apply the Halo2 workflow on an input file and prover values.
Copy link
Member

Choose a reason for hiding this comment

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

In addition to that, shouldn't we also have an option to create the proof for all the other subcommands that perform witness generation?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Happy to add that in another PR

Copy link
Collaborator

Choose a reason for hiding this comment

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

powdr_cli/src/main.rs Outdated Show resolved Hide resolved
degree: DegreeType,
polys: &Vec<(&str, Vec<T>)>,
) {
let width = f32::ceil(T::modulus().to_arbitrary_integer().bits() as f32 / 64f32) as usize * 8;
Copy link
Member

Choose a reason for hiding this comment

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

Why is this a multiple of 64 bits?

Copy link
Collaborator

Choose a reason for hiding this comment

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

So this is a bit hacky but basically field elements are stored as arrays of u64, so this line gets the number of bytes to read for one field element.

// double the row count in order to make space for the cells introduced by the backend
// TODO: use a precise count of the extra rows needed to avoid using so many rows

let k = 1 + f32::log2(circuit.plaf.info.num_rows as f32).ceil() as u32;
Copy link
Member

Choose a reason for hiding this comment

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

Do we really need to convert to floating point just so that we have a ceil function? (also at other places)

Copy link
Member

Choose a reason for hiding this comment

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

Oh and can you use a descriptive name instead of k please?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Done

// | bla_bla_bla | 1 | 1 | bla_bla_bla | |
// | bla_bla_bla | 1 | 1 | bla_bla_bla | |> witness + fixed 2^(k-1)
// | ... | ... | ... | ... | |
// | bla_bla_bla | 1 | 0 | bla_bla_bla | / <- __enable_next == 0 since there's no state transition
Copy link
Member

Choose a reason for hiding this comment

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

Could we fix this so that __enable_next has the same number above and below the ...? It's not clear when the transition from 1 to 0 happens.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It should be clearer now

PlafH2Circuit { plaf, wit }
}

fn expression_2_expr<T: FieldElement>(
Copy link
Member

Choose a reason for hiding this comment

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

Please document the bool return value

Copy link
Member

Choose a reason for hiding this comment

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

We could also use contains_next_ref from the witgen utils.

Copy link
Collaborator

Choose a reason for hiding this comment

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

done, I moved contains_next_ref (and similar functions) to an impl on Expression and used that

// Identity check failure on the first row is not fatal. We will proceed with
// "unknown", report zero and re-check the wrap-around against the zero values at the end.
if identity_failed && next_row != 0 {
if identity_failed {
Copy link
Member Author

Choose a reason for hiding this comment

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

What's the effect of this change? Is next_row expected to never be 0?

Copy link
Collaborator

Choose a reason for hiding this comment

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

No: before this change, as far as I understand a failed identity on the first row was somehow not an issue.
I suspect this change is correct even outside the context of this PR, because since we introduced the EvalResult I can't see how we can recover from a failed identity (the message below should also be adjusted).

powdr_cli/src/main.rs Outdated Show resolved Hide resolved
halo2/src/mock_prover.rs Outdated Show resolved Hide resolved
Copy link
Member Author

@leonardoalt leonardoalt left a comment

Choose a reason for hiding this comment

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

As the original author of the PR I actually can't approve it, but lgtm!
Maybe @chriseth can take another look and approve/merge if all is fine.
I think after merging this we should add some negative tests too.

panic!();
});

let query_callback = |query: &str| -> Option<Bn254Field> {
Copy link
Member

Choose a reason for hiding this comment

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

It would be nice to reduce code duplication here, especially since I'm changing the query format from time to time...

Copy link
Member

Choose a reason for hiding this comment

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

I'm also fine with moving this to witgen and have one "default" query callback that takes a vector of numbers, but also the option for the user to change it if they do custom pil.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Haha yes an outdated query format made me waste an hour when working on this PR. I think having a default one makes sense. I opened #302 for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants