From 5fdefe03045cafc712e57e1714821075b8f077be Mon Sep 17 00:00:00 2001 From: Giacomo Borin Date: Fri, 19 Jan 2024 16:23:24 +0100 Subject: [PATCH 1/7] add Quaternion Book to Reference --- src/doc/en/reference/references/index.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 3f207ec0539..5b011f2d79c 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -6316,6 +6316,8 @@ REFERENCES: .. [Voi2012] \J. Voight. Identifying the matrix ring: algorithms for quaternion algebras and quadratic forms, to appear. +.. [Voi2021] \J. Voight. Quaternion algebras, Springer Nature (2021). + .. [VS06] \G.D. Villa Salvador. Topics in the Theory of Algebraic Function Fields. Birkh\"auser, 2006. From b3ca9d914260ae8b6743cb9f3a3481da97860480 Mon Sep 17 00:00:00 2001 From: Giacomo Borin Date: Fri, 19 Jan 2024 16:24:09 +0100 Subject: [PATCH 2/7] Add primitive decomposition and integrality check --- .../algebras/quatalg/quaternion_algebra.py | 125 +++++++++++++++++- 1 file changed, 124 insertions(+), 1 deletion(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 5a615b43978..465684a4e5f 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -56,7 +56,7 @@ from sage.structure.category_object import normalize_names from sage.structure.parent import Parent from sage.matrix.matrix_space import MatrixSpace -from sage.matrix.constructor import diagonal_matrix, matrix +from sage.matrix.constructor import diagonal_matrix, matrix, block_matrix from sage.structure.sequence import Sequence from sage.structure.element import is_RingElement from sage.structure.factory import UniqueFactory @@ -3020,6 +3020,129 @@ def cyclic_right_subideals(self, p, alpha=None): ans.append(J) return ans + + def is_integral(self): + r""" + Check if a quaternion fractional ideal is integral. An ideal in a quaternion algebra is said integral if it is contained in its left order. If the left order is already defined it just check the definition, otherwise it uses one of the alternative definition of Lemma 16.2.8 of [Voi2021]_. + + Returns: + bool: True if the quaternion fractional ideal is integral, False otherwise. + + + EXAMPLES: + + sage: R. = QuaternionAlgebra(QQ, -1,-11) + sage: I = R.ideal([2 + 2*j + 140*k, 2*i + 4*j + 150*k, 8*j + 104*k, 152*k]) + sage: I.is_integral() + True + sage: O = I.left_order() + sage: I.is_integral() + True + sage: I = R.ideal([1/2 + 2*j + 140*k, 2*i + 4*j + 150*k, 8*j + 104*k, 152*k]) + sage: I.is_integral() + False + + + """ + if self.__left_order is not None: + return all([b in self.left_order() for b in self.basis()]) + elif self.__right_order is not None: + return all([b in self.right_order() for b in self.basis()]) + else: + self_square = self**2 + return all([b in self for b in self_square.basis()]) + + + def primitive_decomposition(self): + r""" + Let `I` = ``self``. If `I` is an integral left `\mathcal{O}`-ideal return its decomposition as an equivalent primitive ideal and an integer such that their product is the initial ideal. + Returns: + + `J`: equivalent primitive ideal to `I`, i.e. equivalent ideal not contained in `n\mathcal{O}` for any `n>0` + `g`: smallest integer such that `I \subset g\mathcal{O}` + + EXAMPLES: + sage: A. = QuaternionAlgebra(QQ, -1,-11) + sage: I = A.ideal([1/2 + 1/2*i + 1/2*j + 3/2*k, i + k, j + k, 2*k]) + sage: I.primitive_decomposition() + (Fractional ideal (1/2 + 1/2*i + 1/2*j + 3/2*k, i + k, j + k, 2*k), 1) + sage: J = A.ideal([7/2 + 7/2*i + 49/2*j + 91/2*k, 7*i + 21*k, 35*j + 35*k, 70*k]) + sage: Jequiv, g = J.primitive_decomposition() + sage: Jequiv*g == J + True + sage: Jequiv, g + (Fractional ideal (1/2 + 1/2*i + 7/2*j + 13/2*k, i + 3*k, 5*j + 5*k, 10*k), 7) + + TESTS: + + Checks on random crafted ideals that they decompose as expected. + + sage: for d in ( m for m in range(400, 750) if is_squarefree(m) ): + ....: A = QuaternionAlgebra(d) + ....: O = A.maximal_order() + ....: for _ in range(10): + ....: a = O.random_element() + ....: if not a.is_constant(): # avoids a = 0 + ....: I = a*O + a.reduced_norm()*O + ....: if I.is_integral(): + ....: J,g = I.primitive_decomposition() + ....: assert J*g == I + ....: assert J.is_primitive() + + + + """ + + if not self.is_integral(): + raise ValueError("primitive ideals are defined only for integral ideals") + + I_basis = self.basis_matrix() + O_basis = self.left_order().basis_matrix() + + # Write I in the basis of its left order + M = block_matrix(1,2,[O_basis.transpose(),I_basis.transpose()]).rref()[:,4:] + g = Integer(gcd((gcd(M_row) for M_row in M))) + + # If g is 1 then the ideal is already cyclic + if g == 1: + return self, g + + J = self.scale(1/g) + + return J, g + + + def is_primitive(self): + r""" + Check if the quaternion fractional ideal is primitive. An integral left $O$-ideal for some order $O$ is said primitive if for all integers $n > 1$ $I$ is not contained in $nO$. + + Returns: + bool: True if the quaternion fractional ideal is primitive, False otherwise. + + EXAMPLES: + sage: A. = QuaternionAlgebra(QQ, -1,-11) + sage: I = A.ideal([1/2 + 1/2*i + 1/2*j + 3/2*k, i + k, j + k, 2*k]) + sage: I.is_primitive() + True + sage: (2*I).is_primitive() + False + + + + """ + _,g = self.primitive_decomposition() + return 1 == g + + + + + + + + + + + ####################################################################### # Some utility functions that are needed here and are too # specialized to go elsewhere. From e609e0bc71e31a06c049efbd7b7590a959d004ea Mon Sep 17 00:00:00 2001 From: Giacomo Borin <64214430+giacomoborin@users.noreply.github.com> Date: Fri, 19 Jan 2024 16:51:53 +0100 Subject: [PATCH 3/7] Error in comments --- src/sage/algebras/quatalg/quaternion_algebra.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 465684a4e5f..dd8d1a1e4aa 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -3042,7 +3042,6 @@ def is_integral(self): sage: I.is_integral() False - """ if self.__left_order is not None: return all([b in self.left_order() for b in self.basis()]) @@ -3089,8 +3088,6 @@ def primitive_decomposition(self): ....: assert J*g == I ....: assert J.is_primitive() - - """ if not self.is_integral(): @@ -3099,11 +3096,11 @@ def primitive_decomposition(self): I_basis = self.basis_matrix() O_basis = self.left_order().basis_matrix() - # Write I in the basis of its left order + # Write I in the basis of its left order via rref M = block_matrix(1,2,[O_basis.transpose(),I_basis.transpose()]).rref()[:,4:] g = Integer(gcd((gcd(M_row) for M_row in M))) - # If g is 1 then the ideal is already cyclic + # If g is 1 then the ideal is primitive if g == 1: return self, g @@ -3136,13 +3133,6 @@ def is_primitive(self): - - - - - - - ####################################################################### # Some utility functions that are needed here and are too # specialized to go elsewhere. From 4312c4bb287a090e14c1d7426338473edb158c73 Mon Sep 17 00:00:00 2001 From: Giacomo Borin Date: Sun, 28 Jan 2024 17:27:35 +0100 Subject: [PATCH 4/7] format correction --- .../algebras/quatalg/quaternion_algebra.py | 109 +++++++++--------- 1 file changed, 52 insertions(+), 57 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index dd8d1a1e4aa..d114d06f276 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -3020,24 +3020,24 @@ def cyclic_right_subideals(self, p, alpha=None): ans.append(J) return ans - def is_integral(self): r""" - Check if a quaternion fractional ideal is integral. An ideal in a quaternion algebra is said integral if it is contained in its left order. If the left order is already defined it just check the definition, otherwise it uses one of the alternative definition of Lemma 16.2.8 of [Voi2021]_. + Check if a quaternion fractional ideal is integral. An ideal in a quaternion algebra is + said integral if it is contained in its left order. If the left order is already defined it just + check the definition, otherwise it uses one of the alternative definition of Lemma 16.2.8 of + [Voi2021]_. - Returns: - bool: True if the quaternion fractional ideal is integral, False otherwise. + OUTPUT: a boolean. - - EXAMPLES: + EXAMPLES:: sage: R. = QuaternionAlgebra(QQ, -1,-11) sage: I = R.ideal([2 + 2*j + 140*k, 2*i + 4*j + 150*k, 8*j + 104*k, 152*k]) sage: I.is_integral() - True + True sage: O = I.left_order() sage: I.is_integral() - True + True sage: I = R.ideal([1/2 + 2*j + 140*k, 2*i + 4*j + 150*k, 8*j + 104*k, 152*k]) sage: I.is_integral() False @@ -3051,42 +3051,41 @@ def is_integral(self): self_square = self**2 return all([b in self for b in self_square.basis()]) - def primitive_decomposition(self): r""" - Let `I` = ``self``. If `I` is an integral left `\mathcal{O}`-ideal return its decomposition as an equivalent primitive ideal and an integer such that their product is the initial ideal. - Returns: - - `J`: equivalent primitive ideal to `I`, i.e. equivalent ideal not contained in `n\mathcal{O}` for any `n>0` - `g`: smallest integer such that `I \subset g\mathcal{O}` - - EXAMPLES: - sage: A. = QuaternionAlgebra(QQ, -1,-11) - sage: I = A.ideal([1/2 + 1/2*i + 1/2*j + 3/2*k, i + k, j + k, 2*k]) - sage: I.primitive_decomposition() - (Fractional ideal (1/2 + 1/2*i + 1/2*j + 3/2*k, i + k, j + k, 2*k), 1) - sage: J = A.ideal([7/2 + 7/2*i + 49/2*j + 91/2*k, 7*i + 21*k, 35*j + 35*k, 70*k]) - sage: Jequiv, g = J.primitive_decomposition() - sage: Jequiv*g == J - True - sage: Jequiv, g - (Fractional ideal (1/2 + 1/2*i + 7/2*j + 13/2*k, i + 3*k, 5*j + 5*k, 10*k), 7) - - TESTS: - - Checks on random crafted ideals that they decompose as expected. - - sage: for d in ( m for m in range(400, 750) if is_squarefree(m) ): - ....: A = QuaternionAlgebra(d) - ....: O = A.maximal_order() - ....: for _ in range(10): - ....: a = O.random_element() - ....: if not a.is_constant(): # avoids a = 0 - ....: I = a*O + a.reduced_norm()*O - ....: if I.is_integral(): - ....: J,g = I.primitive_decomposition() - ....: assert J*g == I - ....: assert J.is_primitive() + Let `I` = ``self``. If `I` is an integral left `\mathcal{O}`-ideal return its decomposition + as an equivalent primitive ideal and an integer such that their product is the initial ideal. + + OUTPUTS: and quivalent primitive ideal to `I`, i.e. equivalent ideal not contained in `n\mathcal{O}` for any `n>0`, and the smallest integer such that `I \subset g\mathcal{O}`. + + EXAMPLES:: + + sage: A. = QuaternionAlgebra(QQ, -1,-11) + sage: I = A.ideal([1/2 + 1/2*i + 1/2*j + 3/2*k, i + k, j + k, 2*k]) + sage: I.primitive_decomposition() + (Fractional ideal (1/2 + 1/2*i + 1/2*j + 3/2*k, i + k, j + k, 2*k), 1) + sage: J = A.ideal([7/2 + 7/2*i + 49/2*j + 91/2*k, 7*i + 21*k, 35*j + 35*k, 70*k]) + sage: Jequiv, g = J.primitive_decomposition() + sage: Jequiv*g == J + True + sage: Jequiv, g + (Fractional ideal (1/2 + 1/2*i + 7/2*j + 13/2*k, i + 3*k, 5*j + 5*k, 10*k), 7) + + TESTS:: + + Checks on random crafted ideals that they decompose as expected. + + sage: for d in ( m for m in range(400, 750) if is_squarefree(m) ): + ....: A = QuaternionAlgebra(d) + ....: O = A.maximal_order() + ....: for _ in range(10): + ....: a = O.random_element() + ....: if not a.is_constant(): # avoids a = 0 + ....: I = a*O + a.reduced_norm()*O + ....: if I.is_integral(): + ....: J,g = I.primitive_decomposition() + ....: assert J*g == I + ....: assert J.is_primitive() """ @@ -3108,31 +3107,27 @@ def primitive_decomposition(self): return J, g - def is_primitive(self): r""" - Check if the quaternion fractional ideal is primitive. An integral left $O$-ideal for some order $O$ is said primitive if for all integers $n > 1$ $I$ is not contained in $nO$. - - Returns: - bool: True if the quaternion fractional ideal is primitive, False otherwise. + Check if the quaternion fractional ideal is primitive. An integral left + $O$-ideal for some order $O$ is said primitive if for all integers $n > 1$ + $I$ is not contained in $nO$. - EXAMPLES: - sage: A. = QuaternionAlgebra(QQ, -1,-11) - sage: I = A.ideal([1/2 + 1/2*i + 1/2*j + 3/2*k, i + k, j + k, 2*k]) - sage: I.is_primitive() - True - sage: (2*I).is_primitive() - False + OUTPUT: a boolean. + EXAMPLES:: + sage: A. = QuaternionAlgebra(QQ, -1,-11) + sage: I = A.ideal([1/2 + 1/2*i + 1/2*j + 3/2*k, i + k, j + k, 2*k]) + sage: I.is_primitive() + True + sage: (2*I).is_primitive() + False """ _,g = self.primitive_decomposition() return 1 == g - - - ####################################################################### # Some utility functions that are needed here and are too # specialized to go elsewhere. From f0122591bc2eeca4dc93856f3e4f40fc739fa775 Mon Sep 17 00:00:00 2001 From: Giacomo Borin Date: Sun, 28 Jan 2024 17:57:58 +0100 Subject: [PATCH 5/7] Inserting reviews In this small commit I inserted all the improvements pointed out previously by @yyyyx4 (many thanks!) --- src/sage/algebras/quatalg/quaternion_algebra.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 8b254a39eda..d59652d7527 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -3083,12 +3083,12 @@ def is_integral(self): """ if self.__left_order is not None: - return all([b in self.left_order() for b in self.basis()]) + return self.free_module() <= self.left_order().free_module() elif self.__right_order is not None: - return all([b in self.right_order() for b in self.basis()]) + return self.free_module() <= self.right_order().free_module() else: self_square = self**2 - return all([b in self for b in self_square.basis()]) + return self_square.free_module() <= self.free_module() def primitive_decomposition(self): r""" @@ -3135,11 +3135,11 @@ def primitive_decomposition(self): O_basis = self.left_order().basis_matrix() # Write I in the basis of its left order via rref - M = block_matrix(1,2,[O_basis.transpose(),I_basis.transpose()]).rref()[:,4:] - g = Integer(gcd((gcd(M_row) for M_row in M))) + M = O_basis.solve_left(I_basis) + g = Integer(gcd(M.list())) # If g is 1 then the ideal is primitive - if g == 1: + if g.is_one(): return self, g J = self.scale(1/g) @@ -3165,7 +3165,7 @@ def is_primitive(self): """ _,g = self.primitive_decomposition() - return 1 == g + return g.is_one() ####################################################################### # Some utility functions that are needed here and are too From acdb7ccce887dd148c12b258090ce9b1bf941261 Mon Sep 17 00:00:00 2001 From: Giacomo Borin Date: Mon, 29 Jan 2024 16:53:04 +0100 Subject: [PATCH 6/7] removed block_matrix import --- src/sage/algebras/quatalg/quaternion_algebra.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index d59652d7527..d13e2728a97 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -56,7 +56,7 @@ from sage.structure.category_object import normalize_names from sage.structure.parent import Parent from sage.matrix.matrix_space import MatrixSpace -from sage.matrix.constructor import diagonal_matrix, matrix, block_matrix +from sage.matrix.constructor import diagonal_matrix, matrix from sage.structure.sequence import Sequence from sage.structure.element import is_RingElement from sage.structure.factory import UniqueFactory From 09d352e591046c9ca7c71ed28cb51845df0c1f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 30 Jan 2024 08:53:36 +0100 Subject: [PATCH 7/7] fix doc syntax in quaternion_algebra.py --- src/sage/algebras/quatalg/quaternion_algebra.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index d13e2728a97..37041220171 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -3110,9 +3110,9 @@ def primitive_decomposition(self): sage: Jequiv, g (Fractional ideal (1/2 + 1/2*i + 7/2*j + 13/2*k, i + 3*k, 5*j + 5*k, 10*k), 7) - TESTS:: + TESTS: - Checks on random crafted ideals that they decompose as expected. + Checks on random crafted ideals that they decompose as expected:: sage: for d in ( m for m in range(400, 750) if is_squarefree(m) ): ....: A = QuaternionAlgebra(d) @@ -3125,9 +3125,7 @@ def primitive_decomposition(self): ....: J,g = I.primitive_decomposition() ....: assert J*g == I ....: assert J.is_primitive() - """ - if not self.is_integral(): raise ValueError("primitive ideals are defined only for integral ideals")