Conversation
How to use the Graphite Merge QueueAdd the label merge-ready to this PR to add it to the merge queue. You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
cf15184 to
327fb17
Compare
327fb17 to
3e8aed8
Compare
| if let Some(pattern_op) = op | ||
| && pattern_op != expr_op { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
The syntax in this if let statement is incorrect. Rust doesn't support combining if let with && in this manner. This should be rewritten as:
if let Some(pattern_op) = op {
if pattern_op != expr_op {
return false;
}
}This issue appears in multiple places in the pattern matching code.
| if let Some(pattern_op) = op | |
| && pattern_op != expr_op { | |
| return false; | |
| } | |
| if let Some(pattern_op) = op { | |
| if pattern_op != expr_op { | |
| return false; | |
| } | |
| } |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
| |bindings| { | ||
| // This would be optimized to a single AND constraint | ||
| // For now, return a marked expression | ||
| Expr::Binary { | ||
| op: BinOp::Xor, | ||
| left: Box::new(bindings["a"].clone()), | ||
| right: Box::new(Expr::Binary { | ||
| op: BinOp::And, | ||
| left: Box::new(Expr::Unary { | ||
| op: UnOp::Not, | ||
| expr: Box::new(bindings["b"].clone()), | ||
| }), | ||
| right: Box::new(bindings["c"].clone()), | ||
| }), | ||
| } |
There was a problem hiding this comment.
The current implementation returns the original expression structure rather than applying the optimization described in the design document. For true optimization, this should transform the expression into the optimized form: (b ^ 0xFFFFFFFFFFFFFFFF) & c ^ (a ^ result), which would compile to a single AND constraint instead of three.
| |bindings| { | |
| // This would be optimized to a single AND constraint | |
| // For now, return a marked expression | |
| Expr::Binary { | |
| op: BinOp::Xor, | |
| left: Box::new(bindings["a"].clone()), | |
| right: Box::new(Expr::Binary { | |
| op: BinOp::And, | |
| left: Box::new(Expr::Unary { | |
| op: UnOp::Not, | |
| expr: Box::new(bindings["b"].clone()), | |
| }), | |
| right: Box::new(bindings["c"].clone()), | |
| }), | |
| } | |
| |bindings| { | |
| // Apply optimization to reduce to a single AND constraint | |
| // Transform a ^ ((~b) & c) into (b ^ 0xFFFFFFFFFFFFFFFF) & c ^ (a ^ result) | |
| let not_b = Expr::Binary { | |
| op: BinOp::Xor, | |
| left: Box::new(bindings["b"].clone()), | |
| right: Box::new(Expr::Literal(Literal::Integer(u64::MAX))), | |
| }; | |
| Expr::Binary { | |
| op: BinOp::Xor, | |
| left: Box::new(Expr::Binary { | |
| op: BinOp::And, | |
| left: Box::new(not_b), | |
| right: Box::new(bindings["c"].clone()), | |
| }), | |
| right: Box::new(bindings["a"].clone()), | |
| } |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
4ee621d to
9172d40
Compare
| } | ||
| } | ||
| Expr::Unary { op, expr: inner } => { | ||
| self.apply_rule_recursive(rule, inner).map(|new_inner| { |
There was a problem hiding this comment.
The apply_rule_recursive method has inconsistent behavior between expression types. For unary expressions, it always returns a new expression when a rule applies to the inner expression, but for binary expressions, it only returns a new expression when the inner expressions actually change. This inconsistency could lead to infinite recursion or unnecessary object creation.
Consider updating the unary case to match the binary pattern:
Expr::Unary { op, expr: inner } => {
if let Some(new_inner) = self.apply_rule_recursive(rule, inner) {
Some(Expr::Unary {
op: *op,
expr: Box::new(new_inner),
})
} else {
None
}
}Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
8d1ac0d to
8f94ef2
Compare
| pub fn xor_many<T: BitType>(exprs: &[Expr<T>]) -> Expr<T> { | ||
| exprs.iter() | ||
| .skip(1) | ||
| .fold(exprs[0].clone(), |acc, e| xor(&acc, e)) | ||
| } |
There was a problem hiding this comment.
The xor_many function will panic if called with an empty slice since it unconditionally accesses exprs[0] on line 20. Consider handling the empty case explicitly, either by:
pub fn xor_many<T: BitType>(exprs: &[Expr<T>]) -> Expr<T> {
if exprs.is_empty() {
// Return zero or panic with descriptive message
return constant(0); // or panic!("Cannot XOR empty slice")
}
exprs.iter()
.skip(1)
.fold(exprs[0].clone(), |acc, e| xor(&acc, e))
}This prevents unexpected runtime failures when the function is called with an empty collection.
| pub fn xor_many<T: BitType>(exprs: &[Expr<T>]) -> Expr<T> { | |
| exprs.iter() | |
| .skip(1) | |
| .fold(exprs[0].clone(), |acc, e| xor(&acc, e)) | |
| } | |
| pub fn xor_many<T: BitType>(exprs: &[Expr<T>]) -> Expr<T> { | |
| if exprs.is_empty() { | |
| return constant(0); | |
| } | |
| exprs.iter() | |
| .skip(1) | |
| .fold(exprs[0].clone(), |acc, e| xor(&acc, e)) | |
| } | |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
| if let Some(pattern_op) = op | ||
| && pattern_op != expr_op { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
The syntax in this pattern matching logic is incorrect. The current code uses if let Some(pattern_op) = op && pattern_op != expr_op which combines a pattern match with a boolean condition in an invalid way. This should be rewritten as either:
if let Some(pattern_op) = op {
if pattern_op != expr_op {
return false;
}
}Or more concisely:
if let Some(pattern_op) = op && *pattern_op != *expr_op {
return false;
}This issue appears in multiple places in the pattern matching code and would cause compilation errors.
| if let Some(pattern_op) = op | |
| && pattern_op != expr_op { | |
| return false; | |
| } | |
| if let Some(pattern_op) = op { | |
| if pattern_op != expr_op { | |
| return false; | |
| } | |
| } |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
| //! SHA-256 implementation using Beamish expression system | ||
|
|
||
| use beamish::*; | ||
| use beamish::types::U32; |
There was a problem hiding this comment.
The import statement in the SHA256 example uses beamish::*, but the crate name in Cargo.toml is defined as binius-beamish. This will cause compilation errors. The import should be changed to binius_beamish::* to match the actual crate name. This pattern appears in several example files and should be corrected throughout.
| use beamish::types::U32; | |
| use binius_beamish::types::U32; |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
1da1977 to
a62c973
Compare
| fn fmt_str(node: &ExprNode) -> String { | ||
| format!("{}", node) | ||
| } |
There was a problem hiding this comment.
The fmt_str function calls format!("{}", node) which could lead to infinite recursion since Display for ExprNode calls fmt_node, which in turn may call fmt_str again. Consider using a direct approach to avoid this potential issue:
fn fmt_str(node: &ExprNode) -> String {
let mut s = String::new();
let mut f = fmt::Formatter::new(&mut s);
fmt_node(node, &mut f).unwrap();
s
}Or alternatively, implement a separate formatting path specifically for this helper function.
| fn fmt_str(node: &ExprNode) -> String { | |
| format!("{}", node) | |
| } | |
| fn fmt_str(node: &ExprNode) -> String { | |
| let mut s = String::new(); | |
| let mut f = fmt::Formatter::new(&mut s); | |
| fmt_node(node, &mut f).unwrap(); | |
| s | |
| } | |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
a62c973 to
65fc70c
Compare
| log = "0.4" | ||
| env_logger = "0.11" |
There was a problem hiding this comment.
Dependencies 'log' and 'env_logger' should refer to workspace-wide dependencies declared in the top-level Cargo.toml using the workspace syntax (e.g., log.workspace = true and env_logger.workspace = true) instead of specifying version numbers directly. This ensures consistent dependency versions across the workspace.
Spotted by Diamond (based on custom rule: Irreducible Rust and Cargo)
Is this helpful? React 👍 or 👎 to let us know.
| //! Comprehensive tests for constraint generation | ||
|
|
||
| use binius_beamish::*; | ||
| use binius_beamish::types::{Field64, U32, U64}; | ||
|
|
||
| #[test] | ||
| #[should_panic(expected = "Cannot generate constraints for expression")] | ||
| fn xor_chain_panics() { | ||
| let a = val::<Field64>(0); | ||
| let b = val::<Field64>(1); | ||
| let c = val::<Field64>(2); | ||
| let expr = xor4(&a, &b, &c, &val(3)); | ||
| let _ = to_constraints(&expr); | ||
| } | ||
|
|
||
| #[test] | ||
| #[should_panic(expected = "Cannot generate constraints for expression")] | ||
| fn rotation_xor_panics() { | ||
| let v = val::<U32>(0); | ||
| let expr = xor3(&ror(&v, 7), &ror(&v, 18), &shr(&v, 3)); | ||
| let _ = to_constraints(&expr); | ||
| } | ||
|
|
||
| #[test] | ||
| fn and_operation_succeeds() { | ||
| let a = val::<Field64>(0); | ||
| let b = val::<Field64>(1); | ||
| let expr = and(&a, &b); | ||
| let constraints = to_constraints(&expr); | ||
| assert_eq!(constraints.len(), 1); | ||
| } | ||
|
|
||
| #[test] | ||
| fn or_operation_succeeds() { | ||
| let a = val::<Field64>(0); | ||
| let b = val::<Field64>(1); | ||
| let expr = or(&a, &b); | ||
| let constraints = to_constraints(&expr); | ||
| assert_eq!(constraints.len(), 1); | ||
| } | ||
|
|
||
| #[test] | ||
| fn addition_succeeds() { | ||
| let x = val::<U32>(0); | ||
| let y = val::<U32>(1); | ||
| let expr = add(&x, &y); | ||
| let constraints = to_constraints(&expr); | ||
| assert_eq!(constraints.len(), 2); // Carry propagation + result | ||
| } | ||
|
|
||
| #[test] | ||
| fn multiplication_succeeds() { | ||
| let x = val::<U64>(0); | ||
| let y = val::<U64>(1); | ||
| let expr = mul64(&x, &y); | ||
| let constraints = to_constraints(&expr); | ||
| assert_eq!(constraints.len(), 1); | ||
| } | ||
|
|
||
| #[test] | ||
| fn keccak_chi_succeeds() { | ||
| let a = val::<Field64>(0); | ||
| let b = val::<Field64>(1); | ||
| let c = val::<Field64>(2); | ||
| let expr = keccak_chi(&a, &b, &c); | ||
| let constraints = to_constraints(&expr); | ||
| assert_eq!(constraints.len(), 1); | ||
| } | ||
|
|
||
| #[test] | ||
| fn multiplexer_succeeds() { | ||
| let a = val::<Field64>(0); | ||
| let b = val::<Field64>(1); | ||
| let c = val::<Field64>(2); | ||
| let expr = mux(&a, &b, &c); | ||
| let constraints = to_constraints(&expr); | ||
| assert_eq!(constraints.len(), 1); | ||
| } | ||
|
|
||
| #[test] | ||
| fn xor_with_equality_succeeds() { | ||
| let a = val::<Field64>(0); | ||
| let b = val::<Field64>(1); | ||
| let c = val::<Field64>(2); | ||
| let xor_expr = xor(&a, &b); | ||
| let eq_expr = eq(&c, &xor_expr); | ||
| let constraints = to_constraints(&eq_expr); | ||
| assert_eq!(constraints.len(), 1); | ||
| } | ||
|
|
||
| #[test] | ||
| fn not_with_equality_succeeds() { | ||
| let a = val::<Field64>(0); | ||
| let b = val::<Field64>(1); | ||
| let not_expr = not(&a); | ||
| let eq_expr = eq(&b, ¬_expr); | ||
| let constraints = to_constraints(&eq_expr); | ||
| assert_eq!(constraints.len(), 1); | ||
| } | ||
|
|
||
| #[test] | ||
| fn rotation_with_equality_succeeds() { | ||
| let v = val::<U32>(0); | ||
| let w = val::<U32>(1); | ||
| let rot_expr = ror(&v, 7); | ||
| let eq_expr = eq(&w, &rot_expr); | ||
| let constraints = to_constraints(&eq_expr); | ||
| assert_eq!(constraints.len(), 1); | ||
| } No newline at end of file |
There was a problem hiding this comment.
The test file does not use StdRng::seed_from_u64 for reproducible tests as required by the Pseudo-Random Testing rule. Tests should use seeded random number generation to ensure reproducibility. Additionally, the tests should consider using helper functions from binius_math::test_utils module where appropriate for better test utilities usage.
Spotted by Diamond (based on custom rule: Monbijou Testing Patterns)
Is this helpful? React 👍 or 👎 to let us know.
65fc70c to
35a8a9a
Compare
35a8a9a to
ac0144d
Compare

No description provided.