Skip to content

Conversation

@zemse
Copy link
Contributor

@zemse zemse commented Nov 28, 2024

  • uses MockProver utils inside assert_satisfied_full which enables additional missing checks for things like assert zero expressions.
  • removes duplicate code to check of multiplicity
  • enables MockProver to check for multiplicity of all instances (previously only instance 0 was considered)

@zemse zemse requested a review from hero78119 November 28, 2024 06:17
@hero78119 hero78119 added cleanup Refactors, simplifications, hindsight 20/20 tasks. debugging tool labels Nov 29, 2024
@zemse zemse changed the title lk checks for all instances use assert_satisfied_raw in assert_satisfied_full e2e tests Dec 16, 2024
@zemse zemse requested a review from hero78119 December 27, 2024 09:54
@zemse zemse changed the title use assert_satisfied_raw in assert_satisfied_full e2e tests refactor assert_satisfied_full Dec 27, 2024
@zemse zemse requested a review from hero78119 December 28, 2024 12:06

/// Allow LK Multiplicity's key to be used with `u64` and `GoldilocksExt2`.
pub trait LkMultiplicityKey: Copy + Clone + Debug + Eq + Hash + Send {
/// If key is u64, return Some(u64), otherwise None.
Copy link
Contributor

Choose a reason for hiding this comment

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

The other possibility is GoldilocksExt2?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

LkMultiplicity's key previously was just u64. From the e2e witness we also get case where we want key as GoldilocksExt2. To enable reuse of the same code in MockProver, the trait LkMultiplicityKey helps.

Copy link
Contributor

@matthiasgoergens matthiasgoergens left a comment

Choose a reason for hiding this comment

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

Please see #793 for most of the suggestions that are also in the comments.

}
ROMType::And => {
let (a, b) = AndTable::unpack(key);
format!("Element: {a} < {b}")
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
format!("Element: {a} < {b}")
format!("Element: {a} && {b}")

"constraint system"

let (location, element) = if let Some(key) = key.to_u64() {
let location = if *count > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

That seems pretty hacky?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

count is recorded to print errors in the following fashion:

# when you have Uint::new in construct while Value::new_unchecked in assign
LkMultiplicityError:
2 Lookups of U16 missing in assignments
Element: 0

# when you have Uint::new_unchecked in construct while Value::new in assign
LkMultiplicityError:
2 Lookups of U16 missing in constraint system
Element: 0

But I agree this could be more self explaining. Instead of count being a signed int, it could be an enum, something like-

enum LkMultiplicityErrorCount {
    MissingInAssignments(u64),
    MissingInConstraintSystem(u64),
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes. Perhaps something for the next PR?

}
ROMType::Instruction => format!("PC: {key}"),
(
if *count > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like we use count in a pretty hacky way?

println!("Error: {} constraints not satisfied", errors.len());
println!("======================================================");
if panic_on_error {
panic!("(Unexpected) Constraints not satisfied");
Copy link
Contributor

Choose a reason for hiding this comment

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

If panic_on_error is set, we should probably put the full error message into the panic message? (In that case, we don't need to println the error message, just put it in the panic only.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sometimes the errors can be a lot and it can go beyond the terminal lines limit (if it's set low) and the following default rust panic stderr line would not be easily visible or lost.

thread 'main' panicked at /Users/.../mock_prover.rs:919:9:

That's why the panic message was purposefully kept short summary so that when program terminates, it can be obvious it was a panic and at which line, and dev can look at logs before to learn more details about what caused "(Unexpected) Constraints not satisfied".

But if it doesn't seem helpful, for better code we can put everything in panic.

multiplicity.borrow_mut()[rom_type as usize].insert(key, count);
}

/// Clone inner, expensive operation.
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems all too complicated, but let me think a bit more about it. (We can merge first.)

@zemse
Copy link
Contributor Author

zemse commented Dec 29, 2024

@matthiasgoergens Thanks for review and suggestion PR!

@matthiasgoergens matthiasgoergens mentioned this pull request Dec 30, 2024
matthiasgoergens and others added 2 commits December 30, 2024 19:30
Suggestion for #649
Co-authored-by: sm.wu <hero78119@gmail.com>
Copy link
Collaborator

@hero78119 hero78119 left a comment

Choose a reason for hiding this comment

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

LGTM now 👍
In summary

  • in assert_satisfied_raw, each opcode we extract raw value from rlc expression and checked with expected lookup multiplicity
  • in assert_satisfied_full, the lookup value was in rlc format and so as table, and do rlc to rlc comparison.

some lefted TODO after this PR

  • have better naming on assert_satisfied_raw and assert_satisfied_full. E.g. assert_satisfied_full -> assert_satisfied_zkvm
  • retain this debug message
    // log mismatch error
    let witness = wit_mles
    .get(&circuit_name)
    .map(|mles| {
    mles.iter()
    .map(|mle| E::from(mle.get_base_field_vec()[row as usize]))
    .collect_vec()
    })
    .unwrap();
    let values = input_value_exprs
    .iter()
    .map(|expr| {
    eval_by_expr_with_instance(
    &[],
    &witness,
    &[],
    &instance,
    challenges.as_slice(),
    expr,
    )
    .as_bases()[0]
    })
    .collect_vec();
    tracing::error!(
    "{}: value {:x?} mismatch lk_multiplicity: real {:x} > remaining {:x} in {:?} table",
    lk_input_annotation,
    values,
    input_multiplicity,
    table_multiplicity,
    rom_type,
    );
    => this break down raw value is very useful, e.g. once program table lookup failed we can quickly targeting on the opcode.

@hero78119 hero78119 added this pull request to the merge queue Jan 2, 2025
Merged via the queue into master with commit 4eef780 Jan 2, 2025
4 checks passed
@hero78119 hero78119 deleted the z/mock-prover branch January 2, 2025 02:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cleanup Refactors, simplifications, hindsight 20/20 tasks. debugging tool

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants