|
37 | 37 | from sage.misc.cachefunc import cached_method
|
38 | 38 | from sage.modules.free_module import FreeModule_submodule_with_basis_pid, FreeModule_ambient_pid
|
39 | 39 | from sage.modules.free_module_element import vector
|
| 40 | +from math import prod |
| 41 | +from sage.functions.all import gamma |
| 42 | +from sage.symbolic.constants import pi, e |
| 43 | + |
40 | 44 |
|
41 | 45 | try:
|
42 | 46 | from sage.rings.number_field.number_field_element import OrderElement_absolute
|
@@ -892,3 +896,77 @@ def babai(self, *args, **kwargs):
|
892 | 896 | Alias for :meth:`approximate_closest_vector`.
|
893 | 897 | """
|
894 | 898 | return self.approximate_closest_vector(*args, **kwargs)
|
| 899 | + |
| 900 | + def hadamard_ratio(self, use_reduced_basis=True): |
| 901 | + """ |
| 902 | + Computes the normalized hadamard ratio. The closer this ratio is to 1, |
| 903 | + the more orthogonal the basis is. |
| 904 | +
|
| 905 | + INPUT: |
| 906 | + - ``use_reduced_basis`` -- boolean (default: ``True``); uses reduced basis |
| 907 | + for computing the ratio |
| 908 | +
|
| 909 | + OUTPUT: The normalized hadamard ratio. This ratio lies in (0, 1] |
| 910 | +
|
| 911 | + EXAMPLES:: |
| 912 | +
|
| 913 | + sage: from sage.modules.free_module_integer import IntegerLattice |
| 914 | + sage: L = IntegerLattice([[101, 0, 0, 0], [0, 101, 0, 0], [0, 0, 101, 0], [-28, 39, 45, 1]], lll_reduce=False) |
| 915 | + sage: float(L.hadamard_ratio()) |
| 916 | + 0.351096481348176 |
| 917 | + sage: L.LLL() |
| 918 | + [ 1 -5 2 18] |
| 919 | + [ -5 25 -10 11] |
| 920 | + [-17 -16 -34 -3] |
| 921 | + [-39 -7 23 5] |
| 922 | + sage: float(L.hadamard_ratio()) |
| 923 | + 0.9933322263147489 |
| 924 | + """ |
| 925 | + if use_reduced_basis: |
| 926 | + basis = self.reduced_basis |
| 927 | + else: |
| 928 | + basis = self.basis_matrix() |
| 929 | + |
| 930 | + n = basis.nrows() |
| 931 | + r = self.rank() |
| 932 | + assert r == n |
| 933 | + |
| 934 | + ratio = (self.discriminant().sqrt() / prod([v.norm() for v in basis]))**(1/r) |
| 935 | + assert 0 < ratio <= 1 |
| 936 | + return ratio |
| 937 | + |
| 938 | + def gaussian_heuristic(self, exact_form=False): |
| 939 | + """ |
| 940 | + Computes the Gaussian expected shortest length, also known as the Gaussian |
| 941 | + heuristic. This estimates the expected norm of the shortest non-zero vector |
| 942 | + in the lattice. The heuristic is independent of the chosen basis. |
| 943 | +
|
| 944 | + INPUT: |
| 945 | + - ``exact_form`` -- boolean (default: ``False``); uses exact formulation |
| 946 | + based on gamma function, instead of estimation of the gamma function |
| 947 | +
|
| 948 | + OUTPUT: The Gaussian heuristic described above. |
| 949 | +
|
| 950 | + EXAMPLES:: |
| 951 | +
|
| 952 | + sage: from sage.modules.free_module_integer import IntegerLattice |
| 953 | + sage: L = IntegerLattice([[101, 0, 0, 0], [0, 101, 0, 0], [0, 0, 101, 0], [-28, 39, 45, 1]]) |
| 954 | + sage: float(L.gaussian_heuristic()) |
| 955 | + 15.418206247181422 |
| 956 | +
|
| 957 | + For small `n`, the exact and approximate forms differ significantly: |
| 958 | + sage: float(L.gaussian_heuristic(exact_form=True)) |
| 959 | + 21.375859827168494 |
| 960 | + """ |
| 961 | + basis = self.basis_matrix() |
| 962 | + |
| 963 | + n = basis.nrows() |
| 964 | + r = self.rank() |
| 965 | + assert r == n |
| 966 | + |
| 967 | + D = self.discriminant().sqrt() |
| 968 | + |
| 969 | + if exact_form: |
| 970 | + return (D * gamma(1 + (r/2)))**(1/r) / pi.sqrt() |
| 971 | + else: |
| 972 | + return D**(1/r) * (r/(2*pi*e)).sqrt() |
0 commit comments