Skip to content

Commit 917cac0

Browse files
committed
Merge remote-tracking branch 'upstream/main' into ai/t02-ecq-conductor-mult
# Conflicts: # lmfdb/tests/test_utils.py # lmfdb/utils/completeness.py
2 parents 47e923a + 5dde4cd commit 917cac0

15 files changed

Lines changed: 104 additions & 159 deletions

File tree

lmfdb/app.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -788,7 +788,6 @@ def WhiteListedRoutes():
788788
'alive',
789789
'api',
790790
'bigpicture',
791-
'callback_ajax',
792791
'citation',
793792
'contact',
794793
'editorial-board',

lmfdb/elliptic_curves/isog_class.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,9 @@ def perm(i): return next(c for c in self.curves if c['Cnumber'] == i+1)['lmfdb_n
175175
if self.cm:
176176
# set CM field for Properties box.
177177
D = integer_squarefree_part(ZZ(self.cm))
178-
coeffs = [(1-D)//4,-1,1] if D % 4 == 1 else [-D,0,1]
178+
# int() everything so that the query list is not a mix of Sage Integers
179+
# and Python ints, which psycopg cannot adapt
180+
coeffs = [int((1-D)//4),-1,1] if D % 4 == 1 else [int(-D),0,1]
179181
lab = db.nf_fields.lucky({'coeffs': coeffs}, projection='label')
180182
self.CMfield = field_pretty(lab)
181183
else:

lmfdb/elliptic_curves/test_ell_curves.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,16 @@ def test_isogeny_class(self):
135135
L = self.tc.get('/EllipticCurve/Q/11/a/')
136136
assert '[0, -1, 1, 0, 0]' in L.get_data(as_text=True)
137137

138+
def test_cm_isogeny_class(self):
139+
# The CM field shown in the Properties box is looked up in nf_fields
140+
# by coefficient list; this 500ed when that list mixed Sage Integers
141+
# with Python ints (see #7129). 27.a and 32.a exercise the two
142+
# branches computing the coefficient list.
143+
L = self.tc.get('/EllipticCurve/Q/27/a/')
144+
assert r'\Q(\sqrt{-3})' in L.get_data(as_text=True)
145+
L = self.tc.get('/EllipticCurve/Q/32/a/')
146+
assert r'\Q(\sqrt{-1})' in L.get_data(as_text=True)
147+
138148
def test_dl_qexp(self):
139149
L = self.tc.get('/EllipticCurve/Q/download_qexp/66.c3/100')
140150
assert '0,1,1,1,1,-4,1,-2,1,1,-4,1,1,4,-2,-4,1,-2,1,0,-4,-2,1,-6,1,11,4,1,-2,10,-4,-8,1,1,-2,8,1,-2,0,4,-4,2,-2,4,1,-4,-6,-2,1,-3,11,-2,4,4,1,-4,-2,0,10,0,-4,-8,-8,-2,1,-16,1,-12,-2,-6,8,2,1,-6,-2,11,0,-2,4,10,-4,1,2,4,-2,8,4,10,1,10,-4,-8,-6,-8,-2,0,1,-2,-3,1,11' in L.get_data(as_text=True)

lmfdb/groups/abstract/main.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,12 @@ def index():
796796
if search_type in legacy_searches:
797797
endpoint, new_search_type = legacy_searches[search_type]
798798
args = request.args.to_dict(flat=False)
799-
args.pop("search_type", None)
799+
# Drop both mode parameters: the destination route encodes the
800+
# object type, and SearchWrapper falls back to hst when there is
801+
# no explicit search_type, so a stale hidden hst would otherwise
802+
# override the mode we just resolved.
803+
for key in ("search_type", "hst"):
804+
args.pop(key, None)
800805
if new_search_type is not None:
801806
args["search_type"] = [new_search_type]
802807
return redirect(url_for(endpoint, **args), 307)

lmfdb/groups/abstract/test_browse_page.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ def test_index_page(self):
1919
def test_legacy_search_urls(self):
2020
r"""
2121
Check that old search URLs redirect to the new landing pages without
22-
dropping their filters.
22+
dropping their filters. The hidden hst field records the previous
23+
search mode, so it must not survive the redirect: SearchWrapper falls
24+
back to hst when there is no explicit search_type.
2325
"""
2426
cases = [
2527
("/Groups/Abstract/?search_type=Subgroups&ambient=128.207",
@@ -34,11 +36,25 @@ def test_legacy_search_urls(self):
3436
{"dim": ["3"], "search_type": ["RandomComplexCharacter"]}),
3537
("/Groups/Abstract/?search_type=ConjugacyClasses&group=12.4",
3638
"/Groups/Abstract/ConjugacyClasses", {"group": ["12.4"]}),
39+
# The legacy mode may only be present in the hidden hst field.
40+
("/Groups/Abstract/?hst=Subgroups&ambient=128.207",
41+
"/Groups/Abstract/Subgroups", {"ambient": ["128.207"]}),
42+
("/Groups/Abstract/?hst=RandomSubgroup&ambient=128.207",
43+
"/Groups/Abstract/Subgroups",
44+
{"ambient": ["128.207"], "search_type": ["RandomSubgroup"]}),
45+
# An explicit search_type must keep winning over a stale hst.
46+
("/Groups/Abstract/?search_type=Subgroups&hst=RandomSubgroup&ambient=128.207",
47+
"/Groups/Abstract/Subgroups", {"ambient": ["128.207"]}),
48+
("/Groups/Abstract/?search_type=ComplexCharacters&hst=RandomComplexCharacter&dim=3",
49+
"/Groups/Abstract/ComplexCharacters", {"dim": ["3"]}),
50+
# Repeated values of a non-routing parameter are preserved.
51+
("/Groups/Abstract/?search_type=Subgroups&order=8&order=16",
52+
"/Groups/Abstract/Subgroups", {"order": ["8", "16"]}),
3753
]
3854
for source, expected_path, expected_query in cases:
3955
response = self.tc.get(source)
40-
target = urlsplit(response.location)
4156
assert response.status_code == 307
57+
target = urlsplit(response.location)
4258
assert target.path == expected_path
4359
assert parse_qs(target.query) == expected_query
4460

lmfdb/modular_curves/test_modular_curves.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ def test_cusp_orbits(self):
135135
assert "Cusp orbits" in L.get_data(as_text=True)
136136
assert r"$8^{6}\cdot16^{3}$" in L.get_data(as_text=True)
137137

138+
L = self.tc.get("/ModularCurve/Q/17.136.6.a.1", follow_redirects=True)
139+
assert r"$8^{1}$" in L.get_data(as_text=True)
140+
138141
def test_modcrv_label(self):
139142
L = self.tc.get("/ModularCurve/Q/48.576.21-48.bqz.1.2",follow_redirects=True)
140143
assert "Cummins and Pauli (CP) label" in L.get_data(as_text=True)

lmfdb/modular_curves/web_curve.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ def get_bread(tail=[]):
3939
tail = [(tail, " ")]
4040
return base + tail
4141

42-
def showexp(c, wrap=True):
43-
if c == 1:
42+
def showexp(c, wrap=True, include_one=False):
43+
if c == 1 and not include_one:
4444
return ""
4545
elif wrap:
4646
return f"$^{{{c}}}$"
@@ -543,7 +543,7 @@ def cusp_widths_display(self):
543543
def cusp_orbits_display(self):
544544
if not self.cusp_orbits:
545545
return ""
546-
return "$" + r"\cdot".join(f"{w}{showexp(n, wrap=False)}" for w, n in self.cusp_orbits) + "$"
546+
return "$" + r"\cdot".join(f"{w}{showexp(n, wrap=False, include_one=True)}" for w, n in self.cusp_orbits) + "$"
547547

548548
@lazy_attribute
549549
def cm_discriminant_list(self):

lmfdb/tests/generate_snippet_tests.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@
3939
comment_dict = {'magma': '//', 'sage': '#', 'sage_gap': '#',
4040
'gp': '\\\\', 'pari': '\\\\', 'oscar': '#', 'gap': '#'}
4141

42+
# To ensure output is fully deterministic and the log files don't change between runs,
43+
# we set the random seed to 1 before each test run
44+
SEED = 1
45+
seed_dict = {'sage': f'set_random_seed({SEED}); gap.set_seed({SEED}); libgap.set_seed({SEED})',
46+
'sage_gap': f'set_random_seed({SEED}); gap.set_seed({SEED}); libgap.set_seed({SEED})',
47+
'magma': f'SetSeed({SEED});',
48+
'oscar': f'import Random; Random.seed!({SEED}); Oscar.set_seed!({SEED}); Oscar.randseed!({SEED});',
49+
'gap': f'Reset(GlobalMersenneTwister, {SEED}); Reset(GlobalRandomSource, {SEED});',
50+
'gp': f'setrand({SEED});',
51+
}
4252

4353
def _setup_test_dir(yaml_file_path=None):
4454
""" Return dictionary with pair(s) 'yaml-file-path': 'test-file-path'.
@@ -55,7 +65,8 @@ def _setup_test_dir(yaml_file_path=None):
5565
raise Exception("Please run in same directory as test.sh")
5666

5767
if yaml_file_path is None:
58-
code_paths = lmfdb_dir.rglob("code*.yaml")
68+
# Ensure snippet files are evaluated in the same order every time (for deterministic output)
69+
code_paths = sorted(lmfdb_dir.rglob("code*.yaml"))
5970
else:
6071
code_paths = [Path(yaml_file_path)]
6172
assert code_paths[0].exists(), f"Specified path {yaml_file_path} does not exist"
@@ -150,6 +161,16 @@ def _eval_code_file(data, lang, proc, logfile):
150161
"""
151162
cmt = comment_dict[lang]
152163
lines = [l for l in data.splitlines() if l != '' and cmt not in l[:len(cmt)+1]]
164+
165+
# Reset the random state before every snippet file (to ensure log files are generated deterministically)
166+
seed_cmd = seed_dict.get(lang)
167+
if seed_cmd is not None:
168+
try:
169+
proc.run_command(seed_cmd, timeout=60)
170+
except Exception as exc:
171+
# Raise error if unable to set the random seed
172+
raise RuntimeError(f"Error: could not reset random state in {lang} with {seed_cmd!r}") from exc
173+
153174
with logfile.open('w') as f:
154175
proc.child.logfile = f
155176
proc.run_command(cmt + " snippet evaluation file generated by generate_snippet_tests.py")
@@ -160,6 +181,8 @@ def _eval_code_file(data, lang, proc, logfile):
160181
print("Timeout while running line:")
161182
print(line)
162183

184+
proc.child.logfile = None
185+
163186
# Matches ANSI escape sequences (colour codes etc.), see e.g. https://en.wikipedia.org/wiki/ANSI_escape_code
164187
# E.g. this sometimes occurs in the Gap snippet log files
165188
ANSI_ESCAPE_RE = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')

lmfdb/tests/snippet_tests/ecnf/code-81.1-CMa1-oscar.log

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ with equation
1212
julia> conductor(E)
1313
Ideal of maximal order of number field of degree 2 over QQ
1414
of norm 81
15-
with 9-normal generators [81, 9*_a - 9]
15+
of minimum 9
16+
with 3-normal generators [9, 9*_a - 9]
1617

1718
julia> norm(conductor(E))
1819
81

lmfdb/tests/snippet_tests/elliptic_curves/code-37.a1-magma.log

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ Mapping from: Abelian Group isomorphic to Z
99
Defined on 1 generator (free) to Set of points of E with coordinates in Rational Field given by a rule [no inverse]
1010
true true
1111
magma> Generators(E);
12-
[ (0 : -1 : 1) ]
12+
[ (0 : 0 : 1) ]
1313
true true
1414
magma> TorsionSubgroup(E);
1515
Abelian Group of order 1
1616
magma> IntegralPoints(E);
17-
[ (-1 : 0 : 1), (0 : -1 : 1), (1 : -1 : 1), (2 : 2 : 1), (6 : -15 : 1) ]
18-
[ <(-1 : 0 : 1), 1>, <(0 : -1 : 1), 1>, <(1 : -1 : 1), 1>, <(2 : 2 : 1), 1>, <(6 : -15 : 1), 1> ]
17+
[ (-1 : -1 : 1), (0 : 0 : 1), (1 : 0 : 1), (2 : -3 : 1), (6 : 14 : 1) ]
18+
[ <(-1 : -1 : 1), 1>, <(0 : 0 : 1), 1>, <(1 : 0 : 1), 1>, <(2 : -3 : 1), 1>, <(6 : 14 : 1), 1> ]
1919
magma> Conductor(E);
2020
37
2121
magma> Discriminant(E);
@@ -42,7 +42,7 @@ magma> Order(TorsionSubgroup(E));
4242
1
4343
magma> MordellWeilShaInformation(E);
4444
[ 1, 1 ]
45-
[ (0 : -1 : 1) ]
45+
[ (0 : 0 : 1) ]
4646
[
4747
<2, [ 0, 0 ]>
4848
]

0 commit comments

Comments
 (0)