Skip to main content

miden_air/trace/
challenges.rs

1//! Unified bus challenge encoding.
2//!
3//! Provides [`Challenges`], a single struct for encoding multiset/LogUp bus messages
4//! as `alpha + <beta, message>`. This type is used by:
5//!
6//! - **AIR constraints** (symbolic expressions): `Challenges<AB::ExprEF>`
7//! - **Processor aux trace builders** (concrete field elements): `Challenges<E>`
8//! - **Verifier** (`reduced_aux_values`): `Challenges<EF>`
9//!
10//! See [`super::bus_message`] for the standard coefficient index layout.
11
12use core::ops::{AddAssign, Mul};
13
14use miden_core::field::PrimeCharacteristicRing;
15
16use super::MAX_MESSAGE_WIDTH;
17
18/// Encodes multiset/LogUp contributions as **alpha + <beta, message>**.
19///
20/// - `alpha`: randomness base
21/// - `beta_powers`: precomputed powers `[beta^0, beta^1, ..., beta^(MAX_MESSAGE_WIDTH-1)]`
22///
23/// The challenges are derived from permutation randomness:
24/// - `alpha = challenges[0]`
25/// - `beta  = challenges[1]`
26///
27/// Precomputed once and passed by reference to all bus components.
28pub struct Challenges<EF: PrimeCharacteristicRing> {
29    pub alpha: EF,
30    pub beta_powers: [EF; MAX_MESSAGE_WIDTH],
31}
32
33impl<EF: PrimeCharacteristicRing> Challenges<EF> {
34    /// Builds `alpha` and precomputed `beta` powers.
35    pub fn new(alpha: EF, beta: EF) -> Self {
36        let mut beta_powers = core::array::from_fn(|_| EF::ONE);
37        for i in 1..MAX_MESSAGE_WIDTH {
38            beta_powers[i] = beta_powers[i - 1].clone() * beta.clone();
39        }
40        Self { alpha, beta_powers }
41    }
42
43    /// Encodes as **alpha + sum(beta_powers\[i\] * elem\[i\])** with K consecutive elements.
44    #[inline(always)]
45    pub fn encode<BF, const K: usize>(&self, elems: [BF; K]) -> EF
46    where
47        EF: Mul<BF, Output = EF> + AddAssign,
48        BF: Clone,
49    {
50        const { assert!(K <= MAX_MESSAGE_WIDTH, "Message length exceeds beta_powers capacity") };
51        let mut acc = self.alpha.clone();
52        for (i, elem) in elems.iter().enumerate() {
53            acc += self.beta_powers[i].clone() * elem.clone();
54        }
55        acc
56    }
57
58    /// Encodes as **alpha + sum(beta_powers\[layout\[i\]\] * values\[i\])** using sparse positions.
59    #[inline(always)]
60    pub fn encode_sparse<BF, const K: usize>(&self, layout: [usize; K], values: [BF; K]) -> EF
61    where
62        EF: Mul<BF, Output = EF> + AddAssign,
63        BF: Clone,
64    {
65        let mut acc = self.alpha.clone();
66        for i in 0..K {
67            let idx = layout[i];
68            debug_assert!(
69                idx < self.beta_powers.len(),
70                "encode_sparse index {} exceeds beta_powers length ({})",
71                idx,
72                self.beta_powers.len()
73            );
74            acc += self.beta_powers[idx].clone() * values[i].clone();
75        }
76        acc
77    }
78}