pub(super) struct BitPackedVector<F: Field> {
buf: Vec<u64>,
naive_buf: Vec<F>,
num_elements: usize,
offset: F,
bits_per_element: usize,
}Expand description
A space-efficient representation of an immutable vector of prime field
elements. Particularly useful when all elements have values close to each
other. It provides an interface similar to that of a Vec.
§Encoding method
This struct interpretes elements of the prime field F_p as integers in the
range [0, p-1] and tries to encode each with fewer bits than the default
of sizeof::<F>() * 8 bits.
In particular, when a new bitpacked vector is created, the
BitPackedVector::new method computes the smallest interval [a, b], with
a < b, such that all elements v[i] in the input vector (interpreted as
integers) belong to [a, b], and then instead of storing v[i], it stores
the value (v[i] - a) \in [0, b - a]. If b - a is a small integer,
representing v[i] - a can be done using ceil(log_2(b - a + 1)) bits.
It then stores the encoded values compactly by packing together the
representation of many consecutive elements into a single machine word (when
possible). This encoding can store n elements using a total size of (n * ceil(log_2(b - a + 1)) * word_width) bits.
§Notes
- Currently the implementation uses more storage than the theoretically
optimal mentioned above. This is because:
- If
ceil(log_2(b - a + 1)) > 64, we resort to the standard representation of usingsizeof::<F>()bytes. This is because for our use-case, there are not many instances of vectors needingc \in [65, 256]bits to encode each value. - We round
ceil(log_2(b - a + 1))up to the nearest divisor of 64. This is to simplify the implementation by avoiding the situation where the encoding of an element spans multiple words.
- If
- For optimal performance, the buffer used to store the encoded values
should be using machine words (e.g.
buf: Vec<usize>) instead of always defaulting to 64-bit entries (buf: Vec<u64>) Here we always assume a 64-bit architecture for the simplicity of the implementation.
Fields§
§buf: Vec<u64>The buffer for storing the bit-packed representation.
As noted above, for optimal performance, the type of each element should
be the machine’s word size.
For now, we’re keeping it always to u64 to make it easier to
work with F chunks.
Invariant: For every instance of a BitPackedVector, either
BitPackedVector::buf or BitPackedVector::naive_buf is populated but
NEVER both.
naive_buf: Vec<F>If during initialization it is deduced that the number of bits
needed per element is more than 64, we revert back to a standard
representation. In that case, Self::buf is never used but instead
Self::naive_buf is populated.
Invariant: For every instance of a BitPackedVector, either
BitPackedVector::buf or BitPackedVector::naive_buf is populated but
NEVER both.
num_elements: usizeThe number of field elements stored in this vector.
This is generally different from self.buf.len().
offset: FThe value of the smallest element in the original vector.
This is the value of a such that all elements of the original
vector belong to the interval [a, b] as described above.
bits_per_element: usizeThe number of bits required to represent each element optimally.
This is equal to ceil(log_2(b - a + 1)) as described above.
Implementations§
Source§impl<F: Field> BitPackedVector<F>
impl<F: Field> BitPackedVector<F>
Sourcepub fn get(&self, index: usize) -> Option<F>
pub fn get(&self, index: usize) -> Option<F>
Return the index-th element stored in the array,
or None if index is out of bounds.
pub fn len(&self) -> usize
Sourcepub fn get_bits_per_element(&self) -> usize
pub fn get_bits_per_element(&self) -> usize
Returns the number of bits used to encode each element.
pub fn iter(&self) -> BitPackedIterator<'_, F> ⓘ
Trait Implementations§
Source§impl<F: Clone + Field> Clone for BitPackedVector<F>
impl<F: Clone + Field> Clone for BitPackedVector<F>
Source§fn clone(&self) -> BitPackedVector<F>
fn clone(&self) -> BitPackedVector<F>
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl<F: Debug + Field> Debug for BitPackedVector<F>
impl<F: Debug + Field> Debug for BitPackedVector<F>
Source§impl<'de, F> Deserialize<'de> for BitPackedVector<F>where
F: Field,
impl<'de, F> Deserialize<'de> for BitPackedVector<F>where
F: Field,
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl<F: PartialEq + Field> PartialEq for BitPackedVector<F>
impl<F: PartialEq + Field> PartialEq for BitPackedVector<F>
Source§impl<F> Serialize for BitPackedVector<F>where
F: Field,
impl<F> Serialize for BitPackedVector<F>where
F: Field,
Source§impl<F: Field> Zeroize for BitPackedVector<F>
impl<F: Field> Zeroize for BitPackedVector<F>
impl<F: Field> StructuralPartialEq for BitPackedVector<F>
Auto Trait Implementations§
impl<F> Freeze for BitPackedVector<F>where
F: Freeze,
impl<F> RefUnwindSafe for BitPackedVector<F>where
F: RefUnwindSafe,
impl<F> Send for BitPackedVector<F>
impl<F> Sync for BitPackedVector<F>
impl<F> Unpin for BitPackedVector<F>where
F: Unpin,
impl<F> UnwindSafe for BitPackedVector<F>where
F: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Conv for T
impl<T> Conv for T
§impl<T> FmtForward for T
impl<T> FmtForward for T
§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.§fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.§impl<T> Pointable for T
impl<T> Pointable for T
§impl<T> Tap for T
impl<T> Tap for T
§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read more§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read more§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read more§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read more§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read more§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read more§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.