Skip to content

Commit

Permalink
add class groups of binary quadratic forms
Browse files Browse the repository at this point in the history
  • Loading branch information
yyyyx4 committed Sep 3, 2023
1 parent 04232a4 commit f2e14da
Show file tree
Hide file tree
Showing 4 changed files with 715 additions and 5 deletions.
1 change: 1 addition & 0 deletions src/doc/en/reference/quadratic_forms/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Quadratic Forms

sage/quadratic_forms/quadratic_form
sage/quadratic_forms/binary_qf
sage/quadratic_forms/bqf_class_group
sage/quadratic_forms/constructions
sage/quadratic_forms/random_quadraticform
sage/quadratic_forms/special_values
Expand Down
2 changes: 2 additions & 0 deletions src/sage/quadratic_forms/all.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from .binary_qf import BinaryQF, BinaryQF_reduced_representatives

from .bqf_class_group import BQFClassGroup

from .ternary_qf import TernaryQF, find_all_ternary_qf_by_level_disc, find_a_ternary_qf_by_level_disc

from .quadratic_form import QuadraticForm, DiagonalQuadraticForm, quadratic_form_from_invariants
Expand Down
124 changes: 119 additions & 5 deletions src/sage/quadratic_forms/binary_qf.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,102 @@ def _pari_init_(self):
"""
return 'Qfb(%s,%s,%s)' % (self._a, self._b, self._c)

@staticmethod
def principal(D):
r"""
Return the principal binary quadratic form of the given discriminant.
EXAMPLES::
sage: BinaryQF.principal(8)
x^2 - 2*y^2
sage: BinaryQF.principal(5)
x^2 + x*y - y^2
sage: BinaryQF.principal(4)
x^2 - y^2
sage: BinaryQF.principal(1)
x^2 + x*y
sage: BinaryQF.principal(-3)
x^2 + x*y + y^2
sage: BinaryQF.principal(-4)
x^2 + y^2
sage: BinaryQF.principal(-7)
x^2 + x*y + 2*y^2
sage: BinaryQF.principal(-8)
x^2 + 2*y^2
TESTS:
Some randomized testing::
sage: D = 1
sage: while D.is_square():
....: D = choice((-4,+4)) * randrange(9999) + randrange(2)
sage: Q = BinaryQF.principal(D)
sage: Q.discriminant() == D # correct discriminant
True
sage: (Q*Q).is_equivalent(Q) # idempotent (hence identity)
True
"""
D = ZZ(D)
D4 = D % 4
if D4 not in (0,1):
raise ValueError('discriminant must be congruent to 0 or 1 modulo 4')
a, b, c = 1, D4, (D4-D)//4
return BinaryQF([1, D4, (D4-D)//4])

Check warning on line 222 in src/sage/quadratic_forms/binary_qf.py

View check run for this annotation

Codecov / codecov/patch

src/sage/quadratic_forms/binary_qf.py#L222

Added line #L222 was not covered by tests

@staticmethod
def random(D):
r"""
Return a somewhat random primitive binary quadratic form of the
given discriminant.
(In the case `D < 0`, only positive definite forms are returned.)
.. NOTE::
No guarantees are being made about the distribution of forms
sampled by this function.
EXAMPLES::
sage: BinaryQF.random(5) # random
448219*x^2 - 597179*x*y + 198911*y^2
sage: BinaryQF.random(-7) # random
10007*x^2 + 10107*x*y + 2552*y^2
TESTS::
sage: D = choice((-4,+4)) * randrange(9999) + randrange(2) or 1
sage: Q = BinaryQF.random(D)
sage: Q.discriminant() == D
True
sage: Q.is_primitive()
True
sage: Q.is_indefinite() or Q.is_positive_definite()
True
"""
D = ZZ(D)
D4 = D % 4
if D4 not in (0,1):
raise ValueError('discriminant must be congruent to 0 or 1 modulo 4')

from sage.misc.prandom import randrange

Check warning on line 260 in src/sage/quadratic_forms/binary_qf.py

View check run for this annotation

Codecov / codecov/patch

src/sage/quadratic_forms/binary_qf.py#L260

Added line #L260 was not covered by tests
from sage.misc.misc_c import prod
from sage.matrix.special import random_matrix
B = (D.abs() or 1) * 99 # kind of arbitrary
while True:
b = randrange(D4, B, 2)
ac = (b**2 - D) // 4
if ac:
break
a = prod(l**randrange(e+1) for l,e in ac.factor())
a = a.prime_to_m_part(gcd([a, b, ac//a]))
c = ac // a
M = random_matrix(ZZ, 2, 2, 'unimodular')
M.rescale_row(0, (-1)**randrange(2))
return BinaryQF([a, b, c]) * M

def __mul__(self, right):
"""
Gauss composition or right action by a 2x2 integer matrix.
Expand Down Expand Up @@ -1589,14 +1685,15 @@ def solve_integer(self, n, *, algorithm="general"):
::
sage: Qs = BinaryQF_reduced_representatives(-23, primitive_only=True) # optional - sage.libs.pari
sage: Qs # optional - sage.libs.pari
sage: # needs sage.libs.pari
sage: Qs = BinaryQF_reduced_representatives(-23, primitive_only=True)
sage: Qs
[x^2 + x*y + 6*y^2, 2*x^2 - x*y + 3*y^2, 2*x^2 + x*y + 3*y^2]
sage: [Q.solve_integer(3) for Q in Qs] # optional - sage.libs.pari
sage: [Q.solve_integer(3) for Q in Qs]
[None, (0, -1), (0, -1)]
sage: [Q.solve_integer(5) for Q in Qs] # optional - sage.libs.pari
sage: [Q.solve_integer(5) for Q in Qs]
[None, None, None]
sage: [Q.solve_integer(6) for Q in Qs] # optional - sage.libs.pari
sage: [Q.solve_integer(6) for Q in Qs]
[(1, -1), (1, -1), (-1, -1)]
TESTS:
Expand Down Expand Up @@ -1695,6 +1792,23 @@ def solve_integer(self, n, *, algorithm="general"):
sol = self.__pari__().qfbsolve(n, flag)
return tuple(map(ZZ, sol)) if sol else None

def form_class(self):
r"""
Return the class of this form modulo equivalence.

Check warning on line 1797 in src/sage/quadratic_forms/binary_qf.py

View check run for this annotation

Codecov / codecov/patch

src/sage/quadratic_forms/binary_qf.py#L1797

Added line #L1797 was not covered by tests
EXAMPLES::
sage: F = BinaryQF([3, -16, 161])
sage: cl = F.form_class(); cl
Class of 3*x^2 + 2*x*y + 140*y^2
sage: cl.parent()
Form Class Group of Discriminant -1676
sage: cl.parent() is BQFClassGroup(-4*419)
True
"""
from sage.quadratic_forms.bqf_class_group import BQFClassGroup
return BQFClassGroup(self.discriminant())(self)


def BinaryQF_reduced_representatives(D, primitive_only=False, proper=True):
r"""
Expand Down
Loading

0 comments on commit f2e14da

Please sign in to comment.