Skip to content

Conversation

@jotabulacios
Copy link
Contributor

@jotabulacios jotabulacios commented Mar 20, 2025

Add Binary Field

Description

This PR introduces binary fields and extends them using a tower-based approach.

It implements arithmetic in GF(2^(2^n)) by representing each field element as a polynomial over GF(2)

We didn't implement IsField or FieldElement in this case because we wanted elements from different field extensions to coexist within the same struct, allowing us to optimize each operation to work simultaneously across all levels.

Operations implemented:

Addition: Implemented as bitwise XOR.
Multiplication: Performed recursively using Karatsuba optimization.
Inversion: Achieved via recursive methods and Fermat's little theorem.
Exponentiation: Implemented using the square-and-multiply algorithm.

Type of change

Please delete options that are not relevant.

  • New feature
  • Bug fix
  • Optimization

Checklist

  • Linked to Github Issue
  • Unit tests added
  • This change requires new documentation.
    • Documentation has been added/updated.
  • This change is an Optimization
    • Benchmarks added/run

@codecov-commenter
Copy link

codecov-commenter commented Mar 20, 2025

Codecov Report

Attention: Patch coverage is 84.38438% with 52 lines in your changes missing coverage. Please review.

Project coverage is 72.12%. Comparing base (d938567) to head (44451d9).

Files with missing lines Patch % Lines
crates/math/src/field/fields/binary/field.rs 84.38% 52 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #984      +/-   ##
==========================================
+ Coverage   72.00%   72.12%   +0.11%     
==========================================
  Files         159      160       +1     
  Lines       34867    35200     +333     
==========================================
+ Hits        25107    25388     +281     
- Misses       9760     9812      +52     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jotabulacios jotabulacios marked this pull request as ready for review March 20, 2025 21:01
@jotabulacios jotabulacios requested a review from a team as a code owner March 20, 2025 21:01
/// Joins the hi and low part making a new element of a bigger level.
/// For example, if a_hi = x and a_low = 1
/// then a = xy + 1.
pub fn join(&self, low: &Self) -> Self {
Copy link
Collaborator

Choose a reason for hiding this comment

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

at some point, we could need something like pack (join many)

Comment on lines 90 to 93
// Equality check
pub fn equals(&self, other: &Self) -> bool {
self.value == other.value()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Just in case, did you consider a custom implementation of Eq instead? If you did, could you add a comment explaining why you opted for a method instead?

Copy link
Contributor

Choose a reason for hiding this comment

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

It was a mistake, we fixed it here. Thank you!

Comment on lines 151 to 153
let high = self.value() >> other.num_bits();
// We concatenate both parts.
let result_value = (high << other.num_bits()) | result_low;
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a bit confused about this. Wouldn't the high bits of other be 0 already, making this essentially the same as XOR of both?

Copy link
Contributor

Choose a reason for hiding this comment

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

You are totally right, thanks for the comment! We fixed it here: 251812a and now the function looks much better.

Copy link
Contributor

@Oppen Oppen left a comment

Choose a reason for hiding this comment

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

I leave this comment as a reminder as what is missing review from me (I need a fresher brain to look at those carefully):

  • mul
  • inv
  • tests

Everything not in that list I just reviewed and looks quite well.

Comment on lines 378 to 382
impl Default for TowerFieldElement {
fn default() -> Self {
Self::new(0, 0)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be easily implemented deriving Default in the struct

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks! We fixed it here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Not necessarily now, but the following would be desirable for benches:

  1. A iai-callgrind one that runs on the CI (we already have a job set up for that);
  2. Comparisons against a different implementation.

Comment on lines 14 to 16
let mut group = c.benchmark_group("Binary TowerField add");
let a = rand_element(num_levels_a);
let b = rand_element(num_levels_b);
Copy link
Contributor

Choose a reason for hiding this comment

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

We should probably use vectors of random elements instead, so we don't accidentally pick a biased pair that performs much better or much worse than average, for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 28f8103

Comment on lines +187 to +188
// Step 3: Compute high_product * x_{n-1}
let shifted_high_product = high_product.mul(x_value);
Copy link
Contributor

Choose a reason for hiding this comment

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

This will probably perform better with a straight shift. That way we avoid not just the multiplications, but the recursion.

Copy link
Contributor

Choose a reason for hiding this comment

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

Don't change it though. We'll see eventually if this is a bottleneck or not.

}

/// Calculate power.
pub fn pow(&self, exp: u32) -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

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

Love this, pretty and concise.

Comment on lines 311 to 317
impl<'a> Mul<&'a TowerFieldElement> for &'a TowerFieldElement {
type Output = TowerFieldElement;

fn mul(self, other: &'a TowerFieldElement) -> TowerFieldElement {
<TowerFieldElement as Mul<TowerFieldElement>>::mul(*self, *other)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe you can omit the lifetimes here, as the reference never outlives the call.
If it asks for impl<'a>, impl <'_> might work instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks! Fixed it here.

Comment on lines 422 to 425
let a = TowerFieldElement::new(00, 1); // 0
let b = TowerFieldElement::new(01, 1); // 1
let c = TowerFieldElement::new(10, 1); // x
let d = TowerFieldElement::new(11, 1); // x + 1
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe you want 0b00 through 0b11 here. The clippy lint was actually warning correctly if that's the case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You a right, fixed in ca2bc78
Thanks!

@MauroToscano MauroToscano enabled auto-merge April 4, 2025 20:58
@MauroToscano MauroToscano disabled auto-merge April 4, 2025 20:58
@klaus993 klaus993 added this pull request to the merge queue Apr 4, 2025
Merged via the queue into main with commit a7b22e1 Apr 4, 2025
8 checks passed
@klaus993 klaus993 deleted the add_binary_field_tower branch April 4, 2025 22:08
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.

8 participants