|
26 | 26 | sage: a.parent()
|
27 | 27 | Ambient free module of rank 10 over the principal ideal domain Integer Ring
|
28 | 28 | """
|
29 |
| -#****************************************************************************** |
| 29 | +# ****************************************************************************** |
30 | 30 | #
|
31 | 31 | # DGS - Discrete Gaussian Samplers
|
32 | 32 | #
|
|
56 | 56 | # The views and conclusions contained in the software and documentation are
|
57 | 57 | # those of the authors and should not be interpreted as representing official
|
58 | 58 | # policies, either expressed or implied, of the FreeBSD Project.
|
59 |
| -#*****************************************************************************/ |
| 59 | +# *****************************************************************************/ |
60 | 60 |
|
61 | 61 | from sage.functions.log import exp
|
62 | 62 | from sage.rings.real_mpfr import RealField
|
@@ -283,7 +283,7 @@ def f_or_hat(x):
|
283 | 283 | # is essentially the same, but I can't figure out how to
|
284 | 284 | # tweak the `.qfrep` call below correctly.
|
285 | 285 | from warnings import warn
|
286 |
| - warn("Note: `_normalisation_factor_zz` has not been properly "\ |
| 286 | + warn("Note: `_normalisation_factor_zz` has not been properly " |
287 | 287 | "implemented for non-spherical distributions.")
|
288 | 288 | import itertools
|
289 | 289 | from sage.functions.log import log
|
@@ -415,7 +415,7 @@ def __init__(self, B, sigma=1, c=0, r=None, precision=None, sigma_basis=False):
|
415 | 415 | - ``precision`` -- bit precision `≥ 53`.
|
416 | 416 |
|
417 | 417 | - ``sigma_basis`` -- (default: False) When set, ``sigma`` is treated as
|
418 |
| - a basis, i.e. the covariance matrix is computed by ``Σ = SSᵀ``. |
| 418 | + a (row) basis, i.e. the covariance matrix is computed by ``Σ = SSᵀ``. |
419 | 419 |
|
420 | 420 | EXAMPLES::
|
421 | 421 |
|
@@ -461,6 +461,16 @@ def __init__(self, B, sigma=1, c=0, r=None, precision=None, sigma_basis=False):
|
461 | 461 | sage: while v not in counter: add_samples(1000)
|
462 | 462 | sage: while abs(m*f(v)*1.0/nf/counter[v] - 1.0) >= 0.1: add_samples(1000)
|
463 | 463 |
|
| 464 | + The sampler supports passing a basis for the covariance. |
| 465 | +
|
| 466 | + sage: n = 3 |
| 467 | + sage: S = Matrix(ZZ, [[2, 0, 0], [-1, 3, 0], [2, -1, 1]]) |
| 468 | + sage: D = DGL(ZZ^n, S, sigma_basis=True) |
| 469 | + sage: D.sigma |
| 470 | + [ 4.00000000000000 -2.00000000000000 4.00000000000000] |
| 471 | + [-2.00000000000000 10.0000000000000 -5.00000000000000] |
| 472 | + [ 4.00000000000000 -5.00000000000000 6.00000000000000] |
| 473 | +
|
464 | 474 | The non-spherical sampler supports offline computation to speed up
|
465 | 475 | sampling. This will be useful when changing the center `c` is supported.
|
466 | 476 | The difference is more significant for larger matrices. For 128x128 we
|
@@ -499,6 +509,8 @@ def __init__(self, B, sigma=1, c=0, r=None, precision=None, sigma_basis=False):
|
499 | 509 | if self.sigma == self.sigma[0, 0]:
|
500 | 510 | self.sigma = self._RR(self.sigma[0, 0])
|
501 | 511 | else:
|
| 512 | + if sigma_basis: |
| 513 | + self.sigma = self.sigma * self.sigma.T |
502 | 514 | if not self.sigma.is_positive_definite():
|
503 | 515 | raise RuntimeError(f"Sigma(={self._sigma}) is not positive definite")
|
504 | 516 | self.is_spherical = False
|
@@ -574,12 +586,10 @@ def _precompute_data(self):
|
574 | 586 | self.B2 = Sigma2.cholesky().T
|
575 | 587 | self.B2_B_inv = self.B2 * self.B_inv
|
576 | 588 | except ValueError:
|
577 |
| - raise ValueError("Σ₂ is not positive definite. Is your "\ |
578 |
| - f"r(={self.r}) too large? It should be at most "\ |
| 589 | + raise ValueError("Σ₂ is not positive definite. Is your " |
| 590 | + f"r(={self.r}) too large? It should be at most " |
579 | 591 | f"{self._maximal_r()}")
|
580 | 592 |
|
581 |
| - |
582 |
| - |
583 | 593 | def __call__(self):
|
584 | 594 | r"""
|
585 | 595 | Return a new sample.
|
@@ -724,7 +734,6 @@ def c(self, c):
|
724 | 734 | self._c = c
|
725 | 735 | self._precompute_data()
|
726 | 736 |
|
727 |
| - |
728 | 737 | def __repr__(self):
|
729 | 738 | r"""
|
730 | 739 | EXAMPLES::
|
@@ -754,7 +763,6 @@ def __repr__(self):
|
754 | 763 | sigma_str = f"Σ =\n{self._sigma}"
|
755 | 764 | return f"Discrete Gaussian sampler with Gaussian parameter {sigma_str}, c={self.c} over lattice with basis\n\n{self.B}"
|
756 | 765 |
|
757 |
| - |
758 | 766 | def _call_in_lattice(self):
|
759 | 767 | r"""
|
760 | 768 | Return a new sample assuming `c ∈ Λ(B)`.
|
@@ -807,7 +815,6 @@ def _call(self):
|
807 | 815 | v = v + z * B[i]
|
808 | 816 | return v
|
809 | 817 |
|
810 |
| - |
811 | 818 | def add_offline_samples(self, cnt=1):
|
812 | 819 | """
|
813 | 820 | Precompute samples from B⁻¹D₁ to be used in :meth:`_call_non_spherical`
|
|
0 commit comments