Skip to content

Commit 0429be5

Browse files
Merge pull request #6904 from havarddj/d3-diagrams
Diagram search (using d3.js)
2 parents 094f86d + 13ca758 commit 0429be5

27 files changed

Lines changed: 1102 additions & 74 deletions

File tree

lmfdb/artin_representations/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ class ArtinSearchArray(SearchArray):
528528
jump_egspan = "e.g. 4.5648.6t13.b.a"
529529
jump_knowl = "artin.search_input"
530530
jump_prompt = "Label"
531+
has_diagram = False
531532

532533
def __init__(self):
533534
dimension = TextBox(

lmfdb/belyi/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,7 @@ def labels_page():
888888
class BelyiCommonSearchArray(SearchArray):
889889
jump_knowl = "belyi.search_input"
890890
jump_label = "Label"
891+
has_diagram = False
891892

892893
def __init__(self):
893894
self.deg = TextBox(

lmfdb/bianchi_modular_forms/bianchi_modular_form.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -706,7 +706,8 @@ class BMFSearchArray(SearchArray):
706706
jump_egspan = "e.g. 2.0.4.1-65.2-a (single form) or 2.0.4.1-65.2 (space of forms at a level)"
707707
jump_prompt = "Label"
708708
jump_knowl = "mf.bianchi.search_input"
709-
709+
has_diagram = False
710+
710711
def __init__(self):
711712
field = TextBox(
712713
name='field_label',

lmfdb/classical_modular_forms/main.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ def index():
168168
# hidden_search_type for prev/next buttons
169169
info['search_type'] = search_type = info.get('search_type', info.get('hst', ''))
170170

171-
if search_type in ['List', '', 'Random']:
171+
if search_type in ['List', '', 'Random', 'Diagram']:
172172
return newform_search(info)
173173
elif search_type in ['Spaces', 'RandomSpace']:
174174
return space_search(info)
@@ -931,7 +931,12 @@ def _AL_col(i, p):
931931
},
932932
url_for_label=url_for_label,
933933
bread=get_search_bread,
934-
learnmore=learnmore_list)
934+
learnmore=learnmore_list,
935+
diagram_opts={"x_axis_default": "level",
936+
"y_axis_default": "weight",
937+
"color_default": "dim",
938+
"extra_fields": ["level"],
939+
})
935940
def newform_search(info, query):
936941
newform_parse(info, query)
937942
set_info_funcs(info)
@@ -1231,7 +1236,11 @@ def dimension_space_search(info, query):
12311236
'download':CMF_download().download_spaces},
12321237
url_for_label=url_for_label,
12331238
bread=get_search_bread,
1234-
learnmore=learnmore_list)
1239+
learnmore=learnmore_list,
1240+
diagram_opts={"x_axis_default": "level",
1241+
"y_axis_default": "weight",
1242+
"color_default": "dim",
1243+
})
12351244
def space_search(info, query):
12361245
newspace_parse(info, query)
12371246
set_info_funcs(info)
@@ -1774,10 +1783,12 @@ def search_types(self, info):
17741783
basic = [('', 'List of forms'),
17751784
('Dimensions', 'Dimension table'),
17761785
('Traces', 'Traces table'),
1777-
('Random', 'Random form')]
1786+
('Random', 'Random form'),
1787+
('Diagram', 'Diagram search')]
17781788
spaces = [('Spaces', 'List of spaces'),
17791789
('SpaceDimensions', 'Dimension table'),
1780-
('RandomSpace', 'Random')]
1790+
('RandomSpace', 'Random'),
1791+
('Diagram', 'Diagram search')]
17811792
if info is None:
17821793
return basic
17831794
st = self._st(info)

lmfdb/ecnf/main.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,10 @@ def url_for_label(label):
359359
nf, cond_label, iso_label, number = split_full_label(label.strip())
360360
return url_for("ecnf.show_ecnf", nf=nf, conductor_label=cond_label, class_label=iso_label, number=number)
361361

362+
def ecnf_label_builder(row):
363+
"""Build full ECNF label from database row fields for diagram search."""
364+
return f"{row['field_label']}-{row['conductor_label']}-{row['iso_label']}{row['number']}"
365+
362366
def make_cm_query(cm_disc_str):
363367
cm_list = parse_ints_to_list_flash(cm_disc_str, "CM discriminant", max_val=None)
364368
for d in cm_list:
@@ -475,7 +479,12 @@ def postprocess(self, row, info, query):
475479
'download':ECNFDownloader()},
476480
url_for_label=url_for_label,
477481
learnmore=learnmore_list,
478-
bread=lambda:[('Elliptic curves', url_for(".index")), ('Search results', '.')])
482+
bread=lambda:[('Elliptic curves', url_for(".index")), ('Search results', '.')],
483+
diagram_opts={"x_axis_default": "conductor_norm",
484+
"y_axis_default": "rank",
485+
"color_default": "torsion_order",
486+
"label_builder": ecnf_label_builder,
487+
})
479488
def elliptic_curve_search(info, query):
480489
parse_nf_string(info,query,'field',name="base number field",qfield='field_label')
481490
if query.get('field_label') == '1.1.1.1':

lmfdb/elliptic_curves/elliptic_curve.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,14 @@ def createrecord_code(self, lang, column_names):
526526
shortcuts={'jump':elliptic_curve_jump,
527527
'download':ECDownloader()},
528528
bread=lambda:get_bread('Search results'),
529-
postprocess=ec_postprocess)
529+
postprocess=ec_postprocess,
530+
diagram_opts={
531+
"title": "Elliptic curve diagrams",
532+
"bread": lambda: get_bread("Diagram search"),
533+
"label_builder": lambda r: r["lmfdb_label"],
534+
"x_axis_default": "conductor",
535+
"y_axis_default": "regulator",
536+
})
530537
def elliptic_curve_search(info, query):
531538
parse_rational_to_list(info, query, 'jinv', 'j-invariant')
532539
parse_ints(info, query, 'conductor')

lmfdb/elliptic_curves/test_ell_curves.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,14 @@ def test_underlying_data(self):
219219
# 'ec_localdata' in data and 'tamagawa_number' in data and
220220
# 'ec_galrep' in data and "'modell_image'" in data and
221221
# 'ec_padic' in data and 'unit' in data)
222+
223+
def test_diagram_search(self):
224+
"""
225+
Check that diagram search page loads correctly
226+
"""
227+
# Test with a search that returns results
228+
L = self.tc.get('/EllipticCurve/Q/?conductor=11-100&search_type=Diagram&count=100')
229+
data = L.get_data(as_text=True)
230+
# Check that D3 diagram template elements are present
231+
assert 'my_dataviz' in data
232+
assert 'pointRadius' in data

lmfdb/galois_groups/main.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ def make_order_key(order):
137137
gg_columns = SearchColumns([
138138
LinkCol("label", "gg.label", "Label", url_for_label),
139139
SearchCol("pretty", "gg.simple_name", "Name"),
140+
MathCol("n", "gg.degree", "Degree", short_title="degree", default=False),
140141
MathCol("order", "group.order", "Order", align="right"),
141142
MathCol("parity", "gg.parity", "Parity", align="right"),
142143
CheckCol("solv", "group.solvable", "Solvable"),
@@ -152,7 +153,8 @@ def make_order_key(order):
152153
lambda sibs, bnd, cache: WebGaloisGroup(None, {"siblings":sibs, "bound_siblings":bnd}).otherrep_list(givebound=False, cache=cache),
153154
apply_download=lambda s, b, c: [s, b])
154155
],
155-
db_cols=["bound_siblings", "abstract_label", "label", "name", "order", "parity", "pretty", "siblings", "solv", "subfields", "nilpotency", "num_conj_classes","auts"])
156+
157+
db_cols=["bound_siblings", "abstract_label", "label", "name", "n", "order", "parity", "pretty", "siblings", "solv", "subfields", "nilpotency", "num_conj_classes", "auts"])
156158
#gg_columns.below_download = r"<p>Results are complete for degrees $\leq 23$.</p>"
157159

158160
def gg_postprocess(res, info, query):
@@ -180,7 +182,15 @@ def gg_postprocess(res, info, query):
180182
url_for_label=url_for_label,
181183
postprocess=gg_postprocess,
182184
learnmore=learnmore_list,
183-
bread=lambda: get_bread([("Search results", ' ')]))
185+
bread=lambda: get_bread([("Search results", ' ')]),
186+
diagram_opts={
187+
"title": "Galois group diagram search",
188+
"bread": lambda: get_bread(breads=[("Diagram search", " ")]),
189+
"x_axis_default": "n",
190+
"y_axis_default": "num_conj_classes",
191+
"color_default": "nilpotency",
192+
}
193+
)
184194
def galois_group_search(info, query):
185195
if info.get('jump','').strip():
186196
jump_list = ["1T1", "2T1", "3T1", "4T1", "4T2", "5T1", "6T1", "7T1",
@@ -230,6 +240,7 @@ def galois_group_search(info, query):
230240

231241
_set_show_subs(info)
232242

243+
233244
def yesno(val):
234245
if val:
235246
return 'yes'

lmfdb/genus2_curves/main.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,14 @@ class G2C_download(Downloader):
635635
columns=g2c_columns,
636636
bread=lambda: get_bread("Search results"),
637637
learnmore=learnmore_list,
638-
url_for_label=lambda label: url_for(".by_label", label=label),
638+
url_for_label=url_for_curve_label,
639+
diagram_opts={
640+
"title": "Genus 2 curve diagram search",
641+
"bread": lambda: get_bread("Diagram search"),
642+
"x_axis_default": "cond",
643+
"y_axis_default": "regulator",
644+
"color_default": "two_selmer_rank",
645+
},
639646
)
640647
def genus2_curve_search(info, query):
641648
parse_ints(info, query, "abs_disc", "absolute discriminant")
@@ -945,6 +952,7 @@ def g2c_code_download(**args):
945952
response.headers['Content-type'] = 'text/html'
946953
return response
947954

955+
948956
class G2CSearchArray(SearchArray):
949957
noun = "curve"
950958

lmfdb/groups/abstract/main.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,7 @@ def index():
779779
info["search_type"] = search_type = info.get(
780780
"search_type", info.get("hst", "")
781781
)
782-
if search_type in ["List", "", "Random"]:
782+
if search_type in ["List", "", "Random", "Diagram"]:
783783
return group_search(info)
784784
elif search_type in ["Subgroups", "RandomSubgroup"]:
785785
info["search_array"] = SubgroupSearchArray()
@@ -790,6 +790,7 @@ def index():
790790
elif search_type in ["ConjugacyClasses"]: # no random since lots of groups with cc don't have characters also computed
791791
info["search_array"] = ConjugacyClassSearchArray()
792792
return conjugacy_class_search(info)
793+
793794
info["stats"] = GroupStats()
794795
info["count"] = 50
795796
info["order_list"] = ["1-64", "65-127", "128", "129-255", "256", "257-383", "384", "385-511", "513-1000", "1001-1500", "1501-2000", "2001-"]
@@ -1405,6 +1406,10 @@ def group_postprocess(res, info, query):
14051406
# credit=lambda:credit_string,
14061407
url_for_label=url_for_label,
14071408
postprocess=group_postprocess,
1409+
diagram_opts={
1410+
"title": "Abstract group diagrams",
1411+
"bread": lambda: get_bread([("Diagram search", "")]),
1412+
},
14081413
)
14091414
def group_search(info, query={}):
14101415
group_parse(info, query)
@@ -2565,6 +2570,11 @@ def download_group(**args):
25652570
#strIO.seek(0)
25662571
#return send_file(strIO, attachment_filename=filename, as_attachment=True, add_etags=False)
25672572

2573+
@abstract_page.route("/search_diagram/")
2574+
def browseDiagram():
2575+
info = to_dict(request.args, search_array=GroupsSearchArray())
2576+
info["search_type"] = "Diagram"
2577+
return group_search(info)
25682578

25692579
class GroupsSearchArray(SearchArray):
25702580
noun = "group"

0 commit comments

Comments
 (0)