Skip to content

Conversation

@JoeyBF
Copy link
Collaborator

@JoeyBF JoeyBF commented Dec 26, 2025

This PR abstracts away the common logic behind FqVector, FqSlice and FqSliceMut. This leads to simplifications and the possibility of adding more variants in the future.

Currently, my main objective is switching to using FpCow in BidegreeElements to avoid unnecessary clones and make them usable in performance-critical code.

Summary by CodeRabbit

  • Refactor
    • Restructured vector representation for improved flexibility and consistency across vector and slice types.
    • Simplified public API by removing several utility methods and consolidating functionality.
    • Renamed slice method to restrict for clearer semantics.
    • Updated internal constructor patterns while preserving existing core arithmetic and assignment operations.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 26, 2025

📝 Walkthrough

Walkthrough

This pull request refactors the vector and matrix type systems by introducing generic base types (FpVectorBase, FqVectorBase) parameterized by representation strategy and allocation mode, replacing direct struct definitions with type aliases and reorganizing the public API through new constructor names and method relocations.

Changes

Cohort / File(s) Summary
Vector base type restructuring
ext/crates/fp/src/vector/inner.rs
Introduces generic FqVectorBase<const A: bool, R, F> replacing prior concrete FqVector, FqSlice, FqSliceMut structs; adds Repr/ReprMut traits for storage abstraction; consolidates accessors (_new, prime, len, slice, iter, entry, set_entry, limbs, etc.) onto the base type; new type aliases maintain backward compatibility.
FP wrapper enum and aliases
ext/crates/fp/src/vector/fp_wrapper/mod.rs
Introduces public FpVectorBase<const A: bool, R> enum with type aliases FpVector = FpVectorBase<true, Vec<Limb>>, FpSlice<'a> = FpVectorBase<false, &'a [Limb]>, FpSliceMut<'a> = FpVectorBase<false, &'a mut [Limb]>; adds impl blocks for immutable/mutable operations with methods (slice, iter, scale, add_basis_element, etc.); updates From trait implementations.
FP wrapper helpers
ext/crates/fp/src/vector/fp_wrapper/helpers.rs
Refactors helpers from FqVector into new FqVectorBase<A, R, F> with separate impl blocks for immutable (R: Repr) and mutable (R: ReprMut) variants; moves scale_helper, set_entry_helper, add_basis_element_helper; adds add_helper and add_offset_helper under new trait organization.
FQ slice implementations
ext/crates/fp/src/vector/impl_fqslice.rs
Removes public methods (prime, len, is_empty, entry, iter, iter_nonzero, is_zero, slice) and Display impl; introduces restrict method with _new constructor; replaces From<&FqVector> with From<&FqVectorBase<A, R, F>>; simplifies limb-tracking and formatting logic.
FQ slice mutable implementation
ext/crates/fp/src/vector/impl_fqslicemut.rs
Shifts from as_slice()-based to limb-based access patterns; updates From trait to generic FqVectorBase<A, R, F> with ReprMut; adapts internal methods (add, assign, add_masked) to use limb_range accessors; updates copy() to use _new constructor.
FQ vector implementation
ext/crates/fp/src/vector/impl_fqvector.rs
Removes substantial public API surface: is_empty, prime, slice, slice_mut, as_slice, as_slice_mut, add_basis_element, entry, set_entry, iter, iter_nonzero, set_to_zero, scale, is_zero; eliminates Display impl; retains construction, arithmetic (add_offset, add, add_truncate, add_carry), and inspection helpers.
Matrix inner implementation
ext/crates/fp/src/matrix/matrix_inner.rs
Replaces public FpSlice::new() and FpSliceMut::new() calls with private _new() constructors across row accessors and iteration paths (iter, iter_mut, maybe_par_iter_mut); preserves identical parameters and behavior.
Algebra free module
ext/crates/algebra/src/module/free_module.rs
Replaces v.slice(...) with v.restrict(...) in slice_vector method, maintaining computed bounds for generator contribution extraction.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 A vector's heart grows generic and bright,
With base types dancing in parametric light—
From slice to restrict, the API takes flight,
Repr traits unify the stored data's plight!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Unify vector-like structs' directly and clearly describes the main objective of the PR: consolidating shared logic across FqVector, FqSlice, FqSliceMut, and FpVector-based types into unified base types.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ext/crates/fp/src/vector/impl_fqslicemut.rs (1)

38-41: Consider using self.len() directly instead of self.as_slice().len().

Since FqSliceMut has a len() method through the base type, self.as_slice().len() creates an unnecessary intermediate FqSlice. This is a minor inefficiency but inconsistent with similar patterns elsewhere in the file (e.g., line 412).

🔎 Proposed fix
     pub fn add_offset(&mut self, other: FqSlice<'_, F>, c: FieldElement<F>, offset: usize) {
-        self.slice_mut(offset, self.as_slice().len())
+        self.slice_mut(offset, self.len())
             .add(other.slice(offset, other.len()), c)
     }
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 81c8622 and 0e27e28.

📒 Files selected for processing (8)
  • ext/crates/algebra/src/module/free_module.rs
  • ext/crates/fp/src/matrix/matrix_inner.rs
  • ext/crates/fp/src/vector/fp_wrapper/helpers.rs
  • ext/crates/fp/src/vector/fp_wrapper/mod.rs
  • ext/crates/fp/src/vector/impl_fqslice.rs
  • ext/crates/fp/src/vector/impl_fqslicemut.rs
  • ext/crates/fp/src/vector/impl_fqvector.rs
  • ext/crates/fp/src/vector/inner.rs
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-12-11T08:35:31.031Z
Learnt from: JoeyBF
Repo: SpectralSequences/sseq PR: 205
File: ext/crates/fp/src/limb.rs:11-28
Timestamp: 2025-12-11T08:35:31.031Z
Learning: In Rust 2024 edition (Cargo.toml edition = 2024), the mem::size_of, size_of_val, align_of, and align_of_val functions from std::mem are in the prelude and do not require explicit imports. During reviews, these can be used unqualified when the project targets edition = 2024. Verify the crate's Cargo.toml edition setting before assuming unqualified use, and avoid adding extraneous imports for these helpers.

Applied to files:

  • ext/crates/algebra/src/module/free_module.rs
  • ext/crates/fp/src/vector/impl_fqslicemut.rs
  • ext/crates/fp/src/vector/impl_fqslice.rs
  • ext/crates/fp/src/vector/impl_fqvector.rs
  • ext/crates/fp/src/vector/fp_wrapper/helpers.rs
  • ext/crates/fp/src/vector/inner.rs
  • ext/crates/fp/src/matrix/matrix_inner.rs
  • ext/crates/fp/src/vector/fp_wrapper/mod.rs
📚 Learning: 2025-12-14T08:23:53.399Z
Learnt from: JoeyBF
Repo: SpectralSequences/sseq PR: 207
File: ext/crates/fp/src/simd/x86_64/avx512.rs:367-379
Timestamp: 2025-12-14T08:23:53.399Z
Learning: In Rust 2024 edition, do not remove explicit unsafe blocks inside unsafe fn bodies. The lint unsafe_op_in_unsafe_fn is enabled by default, treating the body of an unsafe fn as safe and requiring each unsafe operation to be wrapped in an explicit unsafe { ... } block. Do not suggest removing these blocks in code using edition = "2024"; ensure unsafe operations are enclosed in explicit unsafe blocks and review for unnecessary nesting.

Applied to files:

  • ext/crates/algebra/src/module/free_module.rs
  • ext/crates/fp/src/vector/impl_fqslicemut.rs
  • ext/crates/fp/src/vector/impl_fqslice.rs
  • ext/crates/fp/src/vector/impl_fqvector.rs
  • ext/crates/fp/src/vector/fp_wrapper/helpers.rs
  • ext/crates/fp/src/vector/inner.rs
  • ext/crates/fp/src/matrix/matrix_inner.rs
  • ext/crates/fp/src/vector/fp_wrapper/mod.rs
📚 Learning: 2025-12-15T22:15:17.779Z
Learnt from: JoeyBF
Repo: SpectralSequences/sseq PR: 207
File: ext/crates/fp/benches/mul.rs:19-28
Timestamp: 2025-12-15T22:15:17.779Z
Learning: In the fp crate, matrices over F_2 (prime=2) are stored with 1 bit per matrix entry. This means an M×N matrix uses M*N bits, i.e. M*N/8 bytes. For example, an 8192×8192 F_2 matrix uses (8192*8192)/8 = 8,388,608 bytes = 8 MB. Use this packing assumption when analyzing memory usage and bench tests, and rely on the crate's API for bit-packed access rather than byte-per-entry storage.

Applied to files:

  • ext/crates/fp/src/vector/impl_fqslicemut.rs
  • ext/crates/fp/src/vector/impl_fqslice.rs
  • ext/crates/fp/src/vector/impl_fqvector.rs
  • ext/crates/fp/src/vector/fp_wrapper/helpers.rs
  • ext/crates/fp/src/vector/inner.rs
  • ext/crates/fp/src/matrix/matrix_inner.rs
  • ext/crates/fp/src/vector/fp_wrapper/mod.rs
📚 Learning: 2025-12-20T04:55:22.617Z
Learnt from: JoeyBF
Repo: SpectralSequences/sseq PR: 207
File: ext/crates/fp/src/matrix/matrix_inner.rs:75-89
Timestamp: 2025-12-20T04:55:22.617Z
Learning: In reviews of code under ext/crates/fp/, treat AVec::from_iter(0, data) as using AVec's default alignment, not 0-byte alignment. Do not assume a reduced alignment or override of AVec's inherent alignment guarantees. If code relies on a specific alignment, verify it via AVec's type alias and its documentation, and avoid making changes that could alter alignment semantics. Ensure tests or comments reflect that 0 is a default alignment flag and not a zero-byte alignment.

Applied to files:

  • ext/crates/fp/src/vector/impl_fqslicemut.rs
  • ext/crates/fp/src/vector/impl_fqslice.rs
  • ext/crates/fp/src/vector/impl_fqvector.rs
  • ext/crates/fp/src/vector/fp_wrapper/helpers.rs
  • ext/crates/fp/src/vector/inner.rs
  • ext/crates/fp/src/matrix/matrix_inner.rs
  • ext/crates/fp/src/vector/fp_wrapper/mod.rs
🧬 Code graph analysis (5)
ext/crates/fp/src/vector/impl_fqslicemut.rs (3)
ext/crates/fp/src/vector/inner.rs (4)
  • fq (68-70)
  • len (76-78)
  • start (150-152)
  • end (154-156)
ext/crates/fp/src/vector/iter.rs (1)
  • len (110-112)
ext/crates/fp/src/vector/impl_fqslice.rs (1)
  • from (42-44)
ext/crates/fp/src/vector/impl_fqslice.rs (3)
ext/crates/fp/src/vector/inner.rs (4)
  • start (150-152)
  • end (154-156)
  • len (76-78)
  • _new (54-66)
ext/crates/fp/src/vector/fp_wrapper/mod.rs (3)
  • from (194-196)
  • from (253-255)
  • from (259-261)
ext/crates/fp/src/vector/impl_fqslicemut.rs (1)
  • from (445-447)
ext/crates/fp/src/vector/inner.rs (5)
ext/crates/fp/src/matrix/matrix_inner.rs (8)
  • iter (380-382)
  • iter (1412-1419)
  • prime (166-168)
  • prime (1388-1390)
  • new (47-49)
  • new (1209-1224)
  • fmt (430-445)
  • fmt (449-451)
ext/crates/fp/src/field/element.rs (3)
  • field (45-47)
  • new (41-43)
  • fmt (110-112)
ext/crates/algebra/src/algebra/algebra_trait.rs (1)
  • prime (42-42)
ext/crates/fp/src/vector/impl_fqslicemut.rs (2)
  • new (168-202)
  • new (288-321)
ext/crates/fp/src/vector/fp_wrapper/mod.rs (1)
  • fmt (180-190)
ext/crates/fp/src/matrix/matrix_inner.rs (1)
ext/crates/fp/src/vector/inner.rs (4)
  • _new (54-66)
  • limb_range (172-174)
  • start (150-152)
  • end (154-156)
ext/crates/fp/src/vector/fp_wrapper/mod.rs (2)
ext/crates/fp/src/vector/inner.rs (8)
  • iter (101-103)
  • len (76-78)
  • slice (85-94)
  • as_slice (96-98)
  • iter_nonzero (105-107)
  • limbs (158-160)
  • slice_mut (311-321)
  • as_slice_mut (215-217)
ext/crates/fp/src/vector/impl_fqslicemut.rs (1)
  • from (445-447)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: webserver (beta)
  • GitHub Check: test (stable)
  • GitHub Check: test (beta)
  • GitHub Check: webserver (nightly)
  • GitHub Check: miri
  • GitHub Check: lint
  • GitHub Check: test (nightly)
🔇 Additional comments (16)
ext/crates/fp/src/matrix/matrix_inner.rs (2)

368-376: LGTM: Row accessor constructor updates are consistent.

The switch from FpSlice::new/FpSliceMut::new to _new aligns with the new internal constructor visibility pattern introduced in the vector base refactor. The parameters and semantics remain unchanged.


1266-1272: Appropriate use of new slicing APIs.

Line 1266 correctly uses the internal _new constructor for mutable slice construction, while line 1272 uses the new public restrict() method for sub-slicing an immutable slice. This follows the intended API split between internal construction and public sub-slicing.

ext/crates/fp/src/vector/fp_wrapper/helpers.rs (2)

23-41: Clean separation of read vs. write helpers using Repr traits.

The split of helpers into Repr (read-only: entry_helper) and ReprMut (mutable: scale_helper, set_entry_helper, add_basis_element_helper) impl blocks correctly aligns with the trait bounds and follows the new representation abstraction pattern.


72-79: New add_offset_helper is consistent with existing helper pattern.

The helper follows the established pattern of converting F::ElementContainer (u32) to FieldElement<F> before delegating to the underlying method.

ext/crates/fp/src/vector/impl_fqslicemut.rs (1)

442-447: Generic From implementation correctly uses ReprMut bound.

The generalization from From<&'a mut FqVector<F>> to From<&'a mut FqVectorBase<A, R, F>> with R: ReprMut allows conversion from any mutable vector-base representation, aligning with the unified type design.

ext/crates/fp/src/vector/impl_fqvector.rs (2)

5-5: Import path updated to reflect new type alias location.

FqVector<F> is now a type alias to FqVectorBase<true, Vec<Limb>, F> defined in inner.rs, so the import correctly points there.


11-245: FqVector implementation correctly retains owned-vector-specific operations.

Methods that require ownership or Vec-specific operations (construction, resize, capacity management, limb-level arithmetic) are appropriately kept here, while common operations have been moved to FqVectorBase.

ext/crates/fp/src/vector/impl_fqslice.rs (2)

11-21: Appropriate rename from slice() to restrict().

The rename to restrict() clarifies that this method narrows an existing slice rather than creating a slice from an owned vector, avoiding confusion with FqVectorBase::slice(). The implementation correctly uses the internal _new constructor and preserves the lifetime through into_limbs().


41-44: Generic From implementation correctly uses Repr bound.

The generalization allows converting any FqVectorBase with a Repr-bounded storage type to an FqSlice, enabling uniform slice creation from vectors, slices, and other representations.

ext/crates/fp/src/vector/fp_wrapper/mod.rs (3)

48-63: Well-designed enum-based wrapper with generic representation.

The FpVectorBase<const A: bool, R> enum cleanly abstracts over prime variants while the A and R parameters enable the same aligned/representation flexibility as the underlying FqVectorBase. The type aliases provide ergonomic access to the most common configurations.


73-100: Correct trait-bounded impl blocks for read vs. write operations.

The separation into Repr (read: slice, as_slice, iter, iter_nonzero, is_zero, entry, limbs) and ReprMut (write: slice_mut, as_slice_mut, set_to_zero, scale, set_entry, add_basis_element, limbs_mut) impl blocks correctly mirrors the underlying FqVectorBase design.


142-148: FpSlice API aligns with FqSlice changes.

The _new constructor (pub(crate)), restrict (replacing slice), and to_owned methods match the corresponding FqSlice API, maintaining consistency across the Fp/Fq abstraction layers.

ext/crates/fp/src/vector/inner.rs (4)

18-24: Clean trait design for storage representation abstraction.

The Repr and ReprMut traits with blanket implementations over Deref<Target = [Limb]> and DerefMut<Target = [Limb]> respectively enable flexible storage strategies (Vec, slices, Cow, Arc, etc.) while maintaining a uniform interface.


53-65: Alignment validation in constructor is correct.

The assertion at line 57 ensures that when A is true (aligned vectors), the start position is always on a limb boundary. This invariant enables the fast paths in methods like is_zero() and set_to_zero().


176-184: Edge case documentation is appreciated.

The comment explaining the potential underflow for zero-width slices at the start of an FpVector is helpful. The performance trade-off (avoiding saturating_sub for a 10% gain) is reasonable given the rarity of this edge case.


339-363: Display implementation handles both formats correctly.

The alternate format ({:#}) provides compact representation suitable for prime fields < 10, while the default format provides a bracketed, comma-separated list. The implementation is consistent with the FpVectorBase Display impl in fp_wrapper/mod.rs.

Comment on lines +367 to 370
v.restrict(
std::cmp::min(v.len(), start),
std::cmp::min(v.len(), start + len),
)
Copy link

@coderabbitai coderabbitai bot Dec 26, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find all uses of .slice( in the codebase
rg -n '\.slice\(' --type=rust | head -50

Repository: SpectralSequences/sseq

Length of output: 4511


🏁 Script executed:

# Check the specific file around lines 198 and 367
cat -n ext/crates/algebra/src/module/free_module.rs | sed -n '190,210p'
cat -n ext/crates/algebra/src/module/free_module.rs | sed -n '360,375p'

Repository: SpectralSequences/sseq

Length of output: 1617


🏁 Script executed:

# Find FpSlice definition to check available methods
rg -n 'impl.*FpSlice' --type=rust -A 10 | head -100

Repository: SpectralSequences/sseq

Length of output: 7329


🏁 Script executed:

# Search for the restrict method definition
rg -n 'fn restrict' --type=rust -B 2 -A 5

Repository: SpectralSequences/sseq

Length of output: 1864


🏁 Script executed:

# Search for the definition of .slice( method
rg -n 'fn slice' --type=rust -B 2 -A 5 ext/crates/fp/

Repository: SpectralSequences/sseq

Length of output: 3272


🏁 Script executed:

# Check the full impl blocks for FpSlice to see all methods
rg -n 'impl.*FpSlice|impl.*FqSlice' --type=rust -A 20 ext/crates/fp/src/vector/

Repository: SpectralSequences/sseq

Length of output: 11424


Update line 198 to use the new .restrict() API.

Line 198 should use input.restrict(input_start, input_end) to be consistent with line 367. The FpSlice::slice() method does not exist; the new unified API is FpSlice::restrict().

🤖 Prompt for AI Agents
ext/crates/algebra/src/module/free_module.rs around lines 367-370 (also update
the use at line ~198): change the old FpSlice::slice(...) call to use the new
FpSlice::restrict(start, end) API; compute start as std::cmp::min(input.len(),
input_start) and end as std::cmp::min(input.len(), input_start + len) and pass
those to input.restrict(start, end) so it mirrors the usage at lines 367–370.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Is it not already using restrict?

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the exact line number where input.slice is used in this file
grep -n 'input.slice' ext/crates/algebra/src/module/free_module.rs

# Also show context around that line
grep -n -C 3 'input.slice' ext/crates/algebra/src/module/free_module.rs

Length of output: 631


@JoeyBF, you're right that line 367 (shown in the diff) is already using restrict() — that change looks good!

However, line 198 still uses the old .slice() method:

let input_slice = input.slice(input_start, input_end);

This should be updated to use .restrict() as well, to match the new unified API used at line 367:

let input_slice = input.restrict(input_start, input_end);

Comment on lines +242 to +252
pub fn scale(&mut self, c: FieldElement<F>) {
assert_eq!(self.fq(), c.field());
let fq = self.fq();

if c == fq.zero() {
self.set_to_zero();
}

if fq.q() == 2 {
return;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing return after set_to_zero() causes redundant operations.

When c == fq.zero(), the method calls set_to_zero() but then continues to execute the rest of the function. For q == 2, this results in an early return (correct). For other primes, it continues to scale the already-zeroed limbs, which is wasteful but not incorrect.

🔎 Proposed fix
         if c == fq.zero() {
             self.set_to_zero();
+            return;
         }
 
         if fq.q() == 2 {
             return;
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub fn scale(&mut self, c: FieldElement<F>) {
assert_eq!(self.fq(), c.field());
let fq = self.fq();
if c == fq.zero() {
self.set_to_zero();
}
if fq.q() == 2 {
return;
}
pub fn scale(&mut self, c: FieldElement<F>) {
assert_eq!(self.fq(), c.field());
let fq = self.fq();
if c == fq.zero() {
self.set_to_zero();
return;
}
if fq.q() == 2 {
return;
}
🤖 Prompt for AI Agents
In ext/crates/fp/src/vector/inner.rs around lines 242 to 252, when c ==
fq.zero() the method calls self.set_to_zero() but does not return, causing the
function to continue and perform redundant scaling; update the control flow so
that after calling self.set_to_zero() the function returns immediately (i.e.,
add a return right after set_to_zero()) so the subsequent logic (including the
existing fq.q() == 2 check) is skipped for the zero scalar.

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.

1 participant