Skip to content

Conversation

@jnsiemer
Copy link
Member

Description
This PR implements...

  • a PSF with perturbation sampling according to MP12
  • relocates and renames the function compute_s to compute the short basis of G to short_basis_gadget and puts it into the gadget_classical file
  • adds benchmarks for the classical PSFs

Testing

  • I added basic working examples (possibly in doc-comment)

Checklist:

  • I have performed a self-review of my own code
    • The code provides good readability and maintainability s.t. it fulfills best practices like talking code, modularity, ...
      • The chosen implementation is not more complex than it has to be
    • My code should work as intended and no side effects occur (e.g. memory leaks)
    • The doc comments fit our style guide
    • I have credited related sources if needed

@jnsiemer jnsiemer self-assigned this Aug 15, 2025
Copy link
Member

@Marvin-Beckmann Marvin-Beckmann left a comment

Choose a reason for hiding this comment

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

looks good, just some minor comments and stuff that needs fixing

Comment on lines 268 to 271
fn samp_d(&self) -> MatZ {
let m = &self.gp.n * &self.gp.k + &self.gp.m_bar;
MatZ::sample_d_common(&m, &self.gp.n, &self.s).unwrap()
}
Copy link
Member

Choose a reason for hiding this comment

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

The s here should be the s the same one coming from the preimage sampling. The preimage sampling however has a factor of s*r and not just s

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch

Comment on lines 318 to 320
// Sample perturbation p <- D_{ZZ^m, r * √Σ_p} - not correct for now. √Σ_p := as √Σ_2
let vec_p =
MatZ::sample_d_common_non_spherical(&self.gp.n, mat_sqrt_sigma_2, &self.r).unwrap();
Copy link
Member

Choose a reason for hiding this comment

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

What exactly do you mean with not correct?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is an old comment - have removed that part / revised the comment

Comment on lines 131 to 137
let sigma_2: MatQ = normalization_factor
* self.r.pow(2).unwrap()
* (&mat_sigma_p
- MatQ::identity(mat_sigma_p.get_num_rows(), mat_sigma_p.get_num_columns()));

// Compute √Σ_2
sigma_2.cholesky_decomposition_flint()
Copy link
Member

Choose a reason for hiding this comment

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

the factors of r and normalization can also be added after the cholesky decomposition is computed as a linear factor.
This should/could speedup the computation time/precision of the cholesky decomposition, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I've tried this out. It might increase precision, but it has a negative impact on performance as the cholesky_decomposition_flint uses f64 and thus, compresses the MatQ to some extend. If we multiply with any f64 or Q afterwards, that compression factor is becoming worse again. There might be a sweet spot between both, but I've chosen performance over precision for now

///
/// let (a, td) = psf.trap_gen();
///
/// let cov_mat = psf.s.pow(2).unwrap() * &psf.r * MatQ::identity(a.get_num_columns(), a.get_num_columns());
Copy link
Member

Choose a reason for hiding this comment

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

This should be r**2, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, this is just a different covariance matrix with a different s. I've addressed it more explicitly now

///
/// Parameters:
/// - `mat_r`: The trapdoor matrix `R`
/// - `mat_sigma`: The covariance matrix `Σ` to sample [`Self::samp_p`] with
Copy link
Member

Choose a reason for hiding this comment

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

you are referring to matrix as Sigma_2 in the description - maybe rename this to sigma_2 or sth similar so it is clear that they are the same

Copy link
Member Author

Choose a reason for hiding this comment

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

I've left it this way. Σ and Σ_p and Σ_2 are different matrices.
Σ is the covariance matrix as described.
We need Σ to compute Σ_p as described in Algorithm 3, MP12.
Last but not least, we pass Σ_p into Algorithm 1, Pei10, to sample the perturbation, which requires Σ_2, which needs to be computed from Σ_p. At the end, we only need to know sqrt of Σ_2 to perform Algorithm 3, MP12 out of those 3. Thus, we precompute Σ_2 from the covariance matrix Σ and store it

/// - `short_basis_gadget`: The short basis of the corresponding gadget-matrix - just required to speed up the algorithm
/// - `short_basis_gadget_gso`: The GSO of the short basis of the gadget-matrix - just required to speed up the algorithm
///
/// Returns a discrete Gaussian sampled preimage of `u` under `G` with Gaussian parameter `r * √(b^2 + 1)`.
Copy link
Member

Choose a reason for hiding this comment

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

what is b? Not necessarily clear from the documentation

Copy link
Member Author

Choose a reason for hiding this comment

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

Made it more explicit in the doc comment

Copy link
Member

Choose a reason for hiding this comment

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

The name of this file might not be enough to tell what it is related to. Maybe gpv_with_perturbation or gpv_perturbation or sth?

Copy link
Member Author

Choose a reason for hiding this comment

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

I've named it mp_perturbation now as it doesn't have anything to do with GPV anymore and it is already in the psf folder. So, people know what to expect.

/// - if Σ_2 is not positive definite.
pub fn compute_sqrt_sigma_2(&self, mat_r: &MatZ, mat_sigma: &MatQ) -> MatQ {
// Normalization factor according to MP12, Section 2.3
let normalization_factor = 1.0 / (2.0 * Q::PI);
Copy link
Member

Choose a reason for hiding this comment

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

this seems to be what was missing in my previous implementation attempt.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes

@jnsiemer jnsiemer merged commit 211bbd7 into dev Aug 29, 2025
2 checks passed
@jnsiemer jnsiemer deleted the psf_perturbation branch August 29, 2025 12:00
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.

3 participants