From b2198db7b46ee43e28e6e07b7b365d7224d5e8b5 Mon Sep 17 00:00:00 2001 From: David Roe Date: Wed, 2 Oct 2024 02:54:07 -0400 Subject: [PATCH 01/18] Update abvar page to show info when hyp_count is known but not jacobian_count --- lmfdb/abvar/fq/templates/show-abvarfq.html | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lmfdb/abvar/fq/templates/show-abvarfq.html b/lmfdb/abvar/fq/templates/show-abvarfq.html index ac15e8ada0..2bf2716fb6 100644 --- a/lmfdb/abvar/fq/templates/show-abvarfq.html +++ b/lmfdb/abvar/fq/templates/show-abvarfq.html @@ -149,20 +149,27 @@

Jacobians and polarizations

{% if cl.has_principal_polarization == 1 %} {% if cl.has_jacobian == -1 %} - This isogeny class is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}, but does not contain a {{KNOWL('ag.jacobian',title='Jacobian')}}. + This isogeny class is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}, but does not contain a {{KNOWL('ag.jacobian',title='Jacobian')}}. {% elif cl.jacobian_count is not none %} - This isogeny class contains the {{KNOWL('ag.jacobian',title='Jacobians' if cl.jacobian_count != 1 else 'Jacobian')}} of {{ cl.jacobian_count }} curve{% if cl.jacobian_count != 1 %}s{% endif %} ({% if cl.jacobian_count == 1 %}which is {% if cl.hyp_count == 0 %}not {% endif %}hyperelliptic{% else %}of which {% if cl.hyp_count == 1 %}1 is{% elif cl.hyp_count == cl.jacobian_count %}all are{% else %}{{ cl.hyp_count }} are{% endif %} hyperelliptic{% endif %}), and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}:{{ cl.curve_display() | safe }} + This isogeny class contains the {{KNOWL('ag.jacobian',title='Jacobians' if cl.jacobian_count != 1 else 'Jacobian')}} of {{ cl.jacobian_count }} curve{% if cl.jacobian_count != 1 %}s{% endif %} ({% if cl.jacobian_count == 1 %}which is {% if cl.hyp_count == 0 %}not {% endif %}hyperelliptic{% else %}of which {% if cl.hyp_count == 1 %}1 is{% elif cl.hyp_count == cl.jacobian_count %}all are{% else %}{{ cl.hyp_count }} are{% endif %} hyperelliptic{% endif %}), and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}:{{ cl.curve_display() | safe }} + {% elif cl.hyp_count == 0 and cl.has_jacobian == 1 %} + {# This case currently does not happen #} + This isogeny class contains a {{KNOWL('ag.jacobian',title='Jacobian')}}, and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}, but does not contain the Jacobian of a hyperelliptic curve. + {% elif cl.hyp_count == 0 and cl.has_jacobian == 0 %} + This isogeny class is {{KNOWL('av.princ_polarizable', title = 'principally polarizable')}} and contains no Jacobian of a hyperelliptic curve, but it is unknown whether it contains a {{KNOWL('ag.jacobian',title='Jacobian')}} of a non-hyperelliptic curve. + {% elif cl.hyp_count >= 1 %} + This isogeny class contains the {{KNOWL('ag.jacobian',title='Jacobians' if cl.hyp_count != 1 else 'Jacobian')}} of {{ cl.hyp_count }} hyperelliptic curve{% if cl.hyp_count != 1 %}s{% endif %}, and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}, but it is unknown how many Jacobians of non-hyperelliptic curves it contains:{{ cl.curve_display() | safe }} {% elif cl.has_jacobian == 1 %} - This isogeny class contains a {{KNOWL('ag.jacobian',title='Jacobian')}}, and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}. + This isogeny class contains a {{KNOWL('ag.jacobian',title='Jacobian')}}, and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}. {% else %} - This isogeny class is {{KNOWL('av.princ_polarizable', title = 'principally polarizable')}}, but it is unknown whether it contains a {{KNOWL('ag.jacobian',title='Jacobian')}}. + This isogeny class is {{KNOWL('av.princ_polarizable', title = 'principally polarizable')}}, but it is unknown whether it contains a {{KNOWL('ag.jacobian',title='Jacobian')}}. {% endif %} {% elif cl.has_principal_polarization == -1 %} -This isogeny class is not {{KNOWL('av.princ_polarizable', title = 'principally polarizable')}}, and therefore does not contain a {{KNOWL('ag.jacobian',title='Jacobian')}}. + This isogeny class is not {{KNOWL('av.princ_polarizable', title = 'principally polarizable')}}, and therefore does not contain a {{KNOWL('ag.jacobian',title='Jacobian')}}. {% elif cl.has_jacobian == -1 %} -This isogeny class does not contain a {{KNOWL('ag.jacobian',title='Jacobian')}}, and it is unknown whether it is {{KNOWL('av.princ_polarizable', title = 'principally polarizable')}}. + This isogeny class does not contain a {{KNOWL('ag.jacobian',title='Jacobian')}}, and it is unknown whether it is {{KNOWL('av.princ_polarizable', title = 'principally polarizable')}}. {% else %} -It is unknown whether this isogeny class contains a {{KNOWL('ag.jacobian',title='Jacobian')}} or whether it is {{KNOWL('av.princ_polarizable', title = 'principally polarizable')}}. + It is unknown whether this isogeny class contains a {{KNOWL('ag.jacobian',title='Jacobian')}} or whether it is {{KNOWL('av.princ_polarizable', title = 'principally polarizable')}}. {% endif %}

From 2d766cadda204edc63ad19a7d9350c0625af4291 Mon Sep 17 00:00:00 2001 From: David Roe Date: Thu, 17 Oct 2024 00:13:27 -0400 Subject: [PATCH 02/18] Move hence --- lmfdb/abvar/fq/templates/show-abvarfq.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmfdb/abvar/fq/templates/show-abvarfq.html b/lmfdb/abvar/fq/templates/show-abvarfq.html index 2bf2716fb6..ddca2c3478 100644 --- a/lmfdb/abvar/fq/templates/show-abvarfq.html +++ b/lmfdb/abvar/fq/templates/show-abvarfq.html @@ -151,7 +151,7 @@

Jacobians and polarizations

{% if cl.has_jacobian == -1 %} This isogeny class is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}, but does not contain a {{KNOWL('ag.jacobian',title='Jacobian')}}. {% elif cl.jacobian_count is not none %} - This isogeny class contains the {{KNOWL('ag.jacobian',title='Jacobians' if cl.jacobian_count != 1 else 'Jacobian')}} of {{ cl.jacobian_count }} curve{% if cl.jacobian_count != 1 %}s{% endif %} ({% if cl.jacobian_count == 1 %}which is {% if cl.hyp_count == 0 %}not {% endif %}hyperelliptic{% else %}of which {% if cl.hyp_count == 1 %}1 is{% elif cl.hyp_count == cl.jacobian_count %}all are{% else %}{{ cl.hyp_count }} are{% endif %} hyperelliptic{% endif %}), and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}:{{ cl.curve_display() | safe }} + This isogeny class contains the {{KNOWL('ag.jacobian',title='Jacobians' if cl.jacobian_count != 1 else 'Jacobian')}} of {{ cl.jacobian_count }} curve{% if cl.jacobian_count != 1 %}s{% endif %}, and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}. {% if cl.jacobian_count == 1 %}The curve whose Jacobian lies within this class is {% if cl.hyp_count == 0 %}not {% endif %}hyperelliptic{% else %}Of the curves whose Jacobians lie within this class, {% if cl.hyp_count == 1 %}1 is{% elif cl.hyp_count == cl.jacobian_count %}all are{% else %}{{ cl.hyp_count }} are{% endif %} hyperelliptic{% endif %}. The Jacobians of the following curves lie within this class:{{ cl.curve_display() | safe }} {% elif cl.hyp_count == 0 and cl.has_jacobian == 1 %} {# This case currently does not happen #} This isogeny class contains a {{KNOWL('ag.jacobian',title='Jacobian')}}, and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}, but does not contain the Jacobian of a hyperelliptic curve. From e428490eb07c8a4307c2ab08545027a82629ecb8 Mon Sep 17 00:00:00 2001 From: David Roe Date: Fri, 8 Nov 2024 15:38:24 -0500 Subject: [PATCH 03/18] Add _safe_execute to knowl database that waits for one second on an error in case database is being reloaded --- lmfdb/knowledge/knowl.py | 115 +++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 52 deletions(-) diff --git a/lmfdb/knowledge/knowl.py b/lmfdb/knowledge/knowl.py index 0539f8f1d5..52f37bba7e 100644 --- a/lmfdb/knowledge/knowl.py +++ b/lmfdb/knowledge/knowl.py @@ -142,6 +142,18 @@ def __init__(self): self.cached_defines_timestamp = 0 self.cached_titles = {} + def _safe_execute(self, query, values=None): + # Every 20 minutes we reload the knowl database on production + # using a dump from beta. If this query is run during the time + # that restore happens, we could trigger an error. The restore + # takes about 0.6 seconds, so if we hit an error we wait + # 1 second and try again. + try: + return list(self._execute(query, values)) + except Exception: + time.sleep(1) + return list(self._execute(query, values)) + @property def titles(self): now = time.time() @@ -170,9 +182,9 @@ def get_knowl(self, ID, fields = ['id'] + self._default_fields if timestamp is not None: selecter = SQL("SELECT {0} FROM kwl_knowls WHERE id = %s AND timestamp = %s LIMIT 1").format(SQL(", ").join(map(Identifier, fields))) - cur = self._execute(selecter, [ID, timestamp]) - if cur.rowcount > 0: - return dict(zip(fields, cur.fetchone())) + L = self._safe_execute(selecter, [ID, timestamp]) + if L: + return dict(zip(fields, L[0])) else: return None @@ -180,26 +192,26 @@ def get_knowl(self, ID, beta = is_beta() selecter = SQL("SELECT {0} FROM kwl_knowls WHERE id = %s AND status >= %s ORDER BY timestamp DESC LIMIT 1").format(SQL(", ").join(map(Identifier, fields))) if not beta: - cur = self._execute(selecter, [ID, 1]) - if cur.rowcount > 0: - return dict(zip(fields, cur.fetchone())) - cur = self._execute(selecter, [ID, -2 if allow_deleted else 0]) - if cur.rowcount > 0: - return dict(zip(fields, cur.fetchone())) + L = self._safe_execute(selecter, [ID, 1]) + if L: + return dict(zip(fields, L[0])) + L = self._safe_execute(selecter, [ID, -2 if allow_deleted else 0]) + if L: + return dict(zip(fields, L[0])) def get_all_knowls(self, fields=None, types=[2, 1,0,-1,-2]): if fields is None: fields = ['id'] + self._default_fields selecter = SQL("SELECT DISTINCT ON (id) {0} FROM kwl_knowls WHERE status >= %s AND type = ANY(%s) ORDER BY id, timestamp DESC").format(SQL(", ").join(map(Identifier, fields))) - cur = self._execute(selecter, [0, types]) - return [dict(zip(fields, res)) for res in cur] + L = self._safe_execute(selecter, [0, types]) + return [dict(zip(fields, res)) for res in L] def get_all_defines(self): selecter = SQL("SELECT DISTINCT ON (id) id, defines FROM kwl_knowls WHERE status >= 0 AND type = 0 AND cardinality(defines) > 0 ORDER BY id, timestamp DESC") - cur = self._execute(selecter) + L = self._safe_execute(selecter) # This should be fixed in the data return [{k: (v if k == 'id' else [normalize_define(t) for t in v]) - for k, v in zip(['id', 'defines'], res)} for res in cur] + for k, v in zip(['id', 'defines'], res)} for res in L] #FIXME shouldn't I be allowed to search on id? or something? def search(self, category="", filters=[], types=[], keywords="", author=None, sort=[], projection=['id', 'title'], regex=False): @@ -268,8 +280,8 @@ def search(self, category="", filters=[], types=[], keywords="", author=None, so else: sort = SQL("") selecter = SQL("SELECT {0} FROM ({1}) knowls WHERE {2}{3}").format(sqlfields, selecter, secondary_restrictions, sort) - cur = self._execute(selecter, values) - return [{k:res[i] for k,i in projfields} for res in cur] + L = self._safe_execute(selecter, values) + return [{k:res[i] for k,i in projfields} for res in L] def save(self, knowl, who, most_recent=None, minor=False): """who is the ID of the user, who wants to save the knowl""" @@ -310,8 +322,8 @@ def get_history(self, limit=25): """ cols = ("id", "title", "timestamp", "last_author") selecter = SQL("SELECT {0} FROM kwl_knowls WHERE status >= %s AND type != %s ORDER BY timestamp DESC LIMIT %s").format(SQL(", ").join(map(Identifier, cols))) - cur = self._execute(selecter, [0, -2, limit]) - return [dict(zip(cols, res)) for res in cur] + L = self._safe_execute(selecter, [0, -2, limit]) + return [dict(zip(cols, res)) for res in L] def get_comment_history(self, limit=25): """ @@ -319,25 +331,24 @@ def get_comment_history(self, limit=25): """ # We want to select the oldest version of each comment but the newest version of each knowl selecter = SQL("WITH k AS (SELECT DISTINCT ON (id) id, title, timestamp, last_author FROM kwl_knowls WHERE status >= %s AND type != %s ORDER BY id, timestamp DESC), c AS (SELECT id, timestamp, last_author, source FROM (SELECT DISTINCT ON (id) id, timestamp, last_author, source FROM kwl_knowls WHERE status >= %s AND type = %s ORDER BY id, timestamp) ci ORDER BY timestamp DESC LIMIT %s) SELECT k.id, k.title, k.timestamp, k.last_author, c.id, c.timestamp, c.last_author FROM k, c WHERE k.id = c.source ORDER BY c.timestamp DESC") - cur = self._execute(selecter, [0, -2, 0, -2, limit]) - return [dict(zip(["knowl_id", "knowl_title", "knowl_timestamp", "knowl_author", "comment_id", "comment_timestamp", "comment_author"], res)) for res in cur] + L = self._safe_execute(selecter, [0, -2, 0, -2, limit]) + return [dict(zip(["knowl_id", "knowl_title", "knowl_timestamp", "knowl_author", "comment_id", "comment_timestamp", "comment_author"], res)) for res in L] def get_edit_history(self, ID): selecter = SQL("SELECT timestamp, last_author, content, status FROM kwl_knowls WHERE status >= %s AND id = %s ORDER BY timestamp") - cur = self._execute(selecter, [0, ID]) - return [dict(zip(["timestamp", "last_author", "content", "status"], rec)) for rec in cur] + L = self._safe_execute(selecter, [0, ID]) + return [dict(zip(["timestamp", "last_author", "content", "status"], rec)) for rec in L] def get_comments(self, ID): # Note that the subselect is sorted in ascending order by timestamp selecter = SQL("SELECT id, last_author, timestamp FROM (SELECT DISTINCT ON (id) id, last_author, timestamp FROM kwl_knowls WHERE type = %s AND source = %s AND status >= 0 ORDER BY id, timestamp) knowls ORDER BY timestamp DESC") - cur = self._execute(selecter, [-2, ID]) - return list(cur) + return self._safe_execute(selecter, [-2, ID]) def get_column_descriptions(self, table): fields = ['id'] + self._default_fields selecter = SQL("SELECT {0} FROM (SELECT DISTINCT ON (id) {0} FROM kwl_knowls WHERE id LIKE %s AND type = %s AND status >= %s ORDER BY id, timestamp) knowls ORDER BY id").format(SQL(", ").join(map(Identifier, fields))) - cur = self._execute(selecter, [f"columns.{table}.%", 2, 0]) - return {rec[0].split(".")[-1]: Knowl(rec[0], data=dict(zip(fields, rec))) for rec in cur} + L = self._safe_execute(selecter, [f"columns.{table}.%", 2, 0]) + return {rec[0].split(".")[-1]: Knowl(rec[0], data=dict(zip(fields, rec))) for rec in L} def set_column_description(self, table, col, description): uid = db.login() @@ -359,10 +370,10 @@ def drop_column(self, table, col): def get_table_description(self, table): fields = ['id'] + self._default_fields - selecter = SQL("SELECT {0} FROM (SELECT DISTINCT ON (id) {0} FROM kwl_knowls WHERE id = %s AND type = %s AND status >= %s ORDER BY id, timestamp) knowls ORDER BY id").format(SQL(", ").join(map(Identifier, fields))) - rec = self._execute(selecter, [f"tables.{table}", 2, 0]).fetchone() - if rec: - return Knowl(rec[0], data=dict(zip(fields, rec))) + selecter = SQL("SELECT {0} FROM (SELECT DISTINCT ON (id) {0} FROM kwl_knowls WHERE id = %s AND type = %s AND status >= %s ORDER BY id, timestamp) knowls ORDER BY id LIMIT 1").format(SQL(", ").join(map(Identifier, fields))) + L = self._safe_execute(selecter, [f"tables.{table}", 2, 0]) + if L: + return Knowl(rec[0], data=dict(zip(fields, L[0]))) def set_table_description(self, table, description): uid = db.login() @@ -402,9 +413,9 @@ def review(self, knowl, who, set_beta=False): def _set_referrers(self, knowls): kids = [k.id for k in knowls] selecter = SQL("SELECT id, links FROM (SELECT DISTINCT ON (id) id, links FROM kwl_knowls WHERE status >= %s AND type != %s ORDER BY id, timestamp DESC) knowls WHERE links && %s") - cur = self._execute(selecter, [0, -2, kids]) + L = self._safe_execute(selecter, [0, -2, kids]) referrers = {k.id: [] for k in knowls} - for refid, links in cur: + for refid, links in L: for kid in links: if kid in referrers: referrers[kid].append(refid) @@ -420,13 +431,13 @@ def needs_review(self, days): time = now - tdelta fields = ['id'] + self._default_fields selecter = SQL("SELECT {0} FROM (SELECT DISTINCT ON (id) {0} FROM kwl_knowls WHERE timestamp >= %s AND status >= %s AND (type = 1 OR type = -1) ORDER BY id, timestamp DESC) knowls WHERE status = 0 ORDER BY timestamp DESC").format(SQL(", ").join(map(Identifier, fields))) - cur = self._execute(selecter, [time, 0]) - knowls = [Knowl(rec[0], data=dict(zip(fields, rec))) for rec in cur] + L = self._safe_execute(selecter, [time, 0]) + knowls = [Knowl(rec[0], data=dict(zip(fields, rec))) for rec in L] kids = [k.id for k in knowls] selecter = SQL("SELECT DISTINCT ON (id) id, content FROM kwl_knowls WHERE status = 1 AND id = ANY(%s) ORDER BY id, timestamp DESC") - cur = self._execute(selecter, [kids]) - reviewed = {rec[0]:rec[1] for rec in cur} + L = self._safe_execute(selecter, [kids]) + reviewed = {rec[0]:rec[1] for rec in L} for k in knowls: k.reviewed_content = reviewed.get(k.id) @@ -439,7 +450,7 @@ def stale_knowls(self): SQL(", ").join(SQL("a.{0}").format(Identifier(col)) for col in fields), SQL(", ").join(SQL("b.{0}").format(Identifier(col)) for col in fields), SQL(", ").join(map(Identifier, fields))) - data = list(self._execute(selecter)) + data = self._safe_execute(selecter) knowls = [Knowl(rec[0], data=dict(zip(fields, rec))) for rec in data] for knowl, rec in zip(knowls, data): D = dict(zip(fields, rec[len(fields):])) @@ -470,20 +481,20 @@ def ids_referencing(self, knowlid, old=False, beta=None): if not beta: # Have to make sure we do display references where the most recent positively reviewed knowl does reference this, but the most recent beta does not. selecter = SQL("SELECT id FROM (SELECT DISTINCT ON (id) id, links FROM kwl_knowls WHERE status > %s AND type != %s ORDER BY id, timestamp DESC) knowls WHERE links @> %s") - cur = self._execute(selecter, values) - good_ids = [rec[0] for rec in cur] + L = self._safe_execute(selecter, values) + good_ids = [rec[0] for rec in L] # Have to make sure that we don't display knowls as referencing this one when the most recent positively reviewed knowl doesn't but the most recent beta knowl does. selecter = SQL("SELECT id FROM (SELECT DISTINCT ON (id) id, links FROM kwl_knowls WHERE status > %s AND type != %s ORDER BY id, timestamp DESC) knowls WHERE NOT (links @> %s)") - cur = self._execute(selecter, values) - bad_ids = [rec[0] for rec in cur] + L = self._safe_execute(selecter, values) + bad_ids = [rec[0] for rec in L] # We also need new knowls that have never been reviewed selecter = SQL("SELECT id FROM (SELECT DISTINCT ON (id) id, links FROM kwl_knowls WHERE status >= %s AND type != %s ORDER BY id, timestamp DESC) knowls WHERE links @> %s") - cur = self._execute(selecter, values) + L = self._safe_execute(selecter, values) if not beta and not old: - new_ids = [rec[0] for rec in cur if rec[0] not in bad_ids] + new_ids = [rec[0] for rec in L if rec[0] not in bad_ids] return sorted(set(new_ids + good_ids)) else: - return [rec[0] for rec in cur] + return [rec[0] for rec in L] def orphans(self, old=False, beta=None): """ @@ -505,10 +516,10 @@ def filter_from_matches(pattern): # Find references in the codebase filter_from_matches(link_finder_re.pattern) selecter = SQL("SELECT DISTINCT ON (id) id, links, cat, title FROM kwl_knowls WHERE status >= %s ORDER BY id, timestamp DESC") - cur = self._execute(selecter, [0]) + L = self._safe_execute(selecter, [0]) categories = {} titles = {} - for rec in cur: + for rec in L: categories[rec[0]] = rec[2] titles[rec[0]] = rec[3] for link in rec[1]: @@ -660,7 +671,7 @@ def actually_rename(self, knowl, new_name=None): def rename_hyphens(self, execute=False): selecter = SQL("SELECT DISTINCT ON (id) id FROM kwl_knowls WHERE id LIKE %s") - bad_names = [rec[0] for rec in db._execute(selecter, ['%-%'])] + bad_names = [rec[0] for rec in db._safe_execute(selecter, ['%-%'])] if execute: for kid in bad_names: new_kid = kid.replace('-', '_') @@ -679,7 +690,7 @@ def broken_links_knowls(self): """ selecter = SQL("SELECT id, link FROM (SELECT DISTINCT ON (id) id, UNNEST(links) AS link FROM kwl_knowls WHERE status >= 0 ORDER BY id, timestamp DESC) knowls WHERE (SELECT COUNT(*) FROM kwl_knowls kw WHERE kw.id = link) = 0") results = defaultdict(list) - for kid, link in self._execute(selecter): + for kid, link in self._safe_execute(selecter): results[kid].append(link) return [(kid, results[kid]) for kid in sorted(results)] @@ -727,9 +738,9 @@ def is_locked(self, knowlid, delta_min=10): tdelta = timedelta(minutes=delta_min) time = now - tdelta selecter = SQL("SELECT username, timestamp FROM kwl_locks WHERE id = %s AND timestamp >= %s LIMIT 1") - cur = self._execute(selecter, (knowlid, time)) - if cur.rowcount > 0: - return dict(zip(["username", "timestamp"], cur.fetchone())) + L = self._execute(selecter, (knowlid, time)) + if L: + return dict(zip(["username", "timestamp"], L[0])) def set_locked(self, knowl, username): """ @@ -761,8 +772,8 @@ def get_categories(self): Returns a dictionary giving the count of (not deleted) knowls within each category. """ selecter = SQL("SELECT cat, COUNT(*) FROM (SELECT DISTINCT ON (id) cat FROM kwl_knowls WHERE type = %s AND status >= 0) knowls GROUP BY cat") - cur = self._execute(selecter, [0]) - return {res[0]: res[1] for res in cur} + L = self._safe_execute(selecter, [0]) + return {res[0]: res[1] for res in L} def remove_author(self, kid, uid): """ From 9fab9c8ad563551463a5011637d7c4ffe04435f2 Mon Sep 17 00:00:00 2001 From: David Roe Date: Fri, 8 Nov 2024 16:25:13 -0500 Subject: [PATCH 04/18] Fix bug found by linter --- lmfdb/knowledge/knowl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmfdb/knowledge/knowl.py b/lmfdb/knowledge/knowl.py index 52f37bba7e..0222ed58db 100644 --- a/lmfdb/knowledge/knowl.py +++ b/lmfdb/knowledge/knowl.py @@ -373,7 +373,7 @@ def get_table_description(self, table): selecter = SQL("SELECT {0} FROM (SELECT DISTINCT ON (id) {0} FROM kwl_knowls WHERE id = %s AND type = %s AND status >= %s ORDER BY id, timestamp) knowls ORDER BY id LIMIT 1").format(SQL(", ").join(map(Identifier, fields))) L = self._safe_execute(selecter, [f"tables.{table}", 2, 0]) if L: - return Knowl(rec[0], data=dict(zip(fields, L[0]))) + return Knowl(L[0][0], data=dict(zip(fields, L[0]))) def set_table_description(self, table, description): uid = db.login() From 1107c804a9aa4716c6edb47edc40bf1aabf26321 Mon Sep 17 00:00:00 2001 From: David Roe Date: Sat, 9 Nov 2024 22:07:50 -0500 Subject: [PATCH 05/18] Add test to check that knowls are syncing between dev and prod --- lmfdb/knowledge/main.py | 2 +- lmfdb/tests/test_dynamic_knowls.py | 35 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/lmfdb/knowledge/main.py b/lmfdb/knowledge/main.py index 7764f8fd1d..ec260ca9a9 100644 --- a/lmfdb/knowledge/main.py +++ b/lmfdb/knowledge/main.py @@ -689,7 +689,7 @@ def save_form(): flash(Markup("Knowl successfully created. Note that a knowl with this id existed previously but was deleted; its history has been restored.")) k.title = new_title k.content = new_content - k.timestamp = datetime.now() + k.timestamp = datetime.utcnow() k.status = 0 k.save(who=who) if NEWID: diff --git a/lmfdb/tests/test_dynamic_knowls.py b/lmfdb/tests/test_dynamic_knowls.py index 83d0245a3a..456e78dfb2 100644 --- a/lmfdb/tests/test_dynamic_knowls.py +++ b/lmfdb/tests/test_dynamic_knowls.py @@ -39,3 +39,38 @@ def test_galois_module_knowl(self): def test_galois_alias_knowl(self): L = self.tc.get('/knowledge/show/nf.galois_group.name', follow_redirects=True) assert '11T6' in L.get_data(as_text=True) + + def test_prod_knowl_sync(self): + # This test checks that the script at https://github.com/edgarcosta/lmfdb-gce/blob/master/server_scripts/update_knowls_and_userdb.sh is successully syncing the knowl databases on beta.lmfdb.org and www.lmfdb.org + # It will be run as part of the CI for PRs against dev or prod. + + from lmfdb import db + if db.config.postgresql_options["host"] == "proddb.lmfdb.xyz": + # Create a different connection to devmirror to compare timestamps + from lmfdb.utils.config import Configuration + from psycopg2.sql import SQL + from datetime import timedelta, datetime + dev_config = Configuration() + # Modify configuration to connect to devmirror + for D in [dev_config.default_args["postgresql"], dev_config.postgresql_options, dev_config.options["postgresql"]]: + D["host"] = "devmirror.lmfdb.xyz" + D["port"] = 5432 + D["dbname"] = "lmfdb" + D["user"] = "lmfdb" + D["password"] = "lmfdb" + from psycodict.database import PostgresDatabase + dev_db = PostgresDatabase(dev_config) + + # Updates happen every 20 minutes, so we only compare knowls older than that (plus a buffer). + cutoff = datetime.utcnow() - timedelta(minutes=30) + + t_query = SQL("SELECT timestamp FROM kwl_knowls WHERE timestamp < %s LIMIT 1") + dev_t = dev_db._execute(t_query, [cutoff]).fetchone()[0] + prod_t = db._execute(t_query, [cutoff]).fetchone()[0] + + cnt_query = SQL("SELECT COUNT(*) FROM kwl_knowls WHERE timestamp < %s") + dev_cnt = dev_db._execute(cnt_query, [cutoff]).fetchone()[0] + prod_cnt = db._execute(cnt_query, [cutoff]).fetchone()[0] + + # The timestamps and counts should be the same + assert dev_cnt == prod_cnt and dev_t == prod_t From 1222454788949d175aa53d4ab85b30dfa7f840bb Mon Sep 17 00:00:00 2001 From: David Roe Date: Sat, 9 Nov 2024 22:25:45 -0500 Subject: [PATCH 06/18] Working on updating browse page --- lmfdb/abvar/fq/templates/abvarfq-index.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lmfdb/abvar/fq/templates/abvarfq-index.html b/lmfdb/abvar/fq/templates/abvarfq-index.html index ec1bcfb5bd..67c1145fac 100644 --- a/lmfdb/abvar/fq/templates/abvarfq-index.html +++ b/lmfdb/abvar/fq/templates/abvarfq-index.html @@ -15,6 +15,9 @@

Browse

By {{ KNOWL('ag.base_field', 'base field cardinality') }}: {% for q in info.q_ranges %} {{q}}  {% endfor %} + + By {{ KNOWL('av.fq.p_rank', '$p$-rank') }}: + {% for r in [0] + info.stats.gs %} {{r}}  {% endfor %} Some interesting isogeny classes or a random isogeny class From 6325a7e3f3cb57f67e93f2ae85d938a91d68d93f Mon Sep 17 00:00:00 2001 From: David Roe Date: Sat, 9 Nov 2024 22:37:09 -0500 Subject: [PATCH 07/18] Update language for Jacobians/principally polarizable --- lmfdb/abvar/fq/templates/show-abvarfq.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lmfdb/abvar/fq/templates/show-abvarfq.html b/lmfdb/abvar/fq/templates/show-abvarfq.html index ddca2c3478..2be2514998 100644 --- a/lmfdb/abvar/fq/templates/show-abvarfq.html +++ b/lmfdb/abvar/fq/templates/show-abvarfq.html @@ -151,16 +151,16 @@

Jacobians and polarizations

{% if cl.has_jacobian == -1 %} This isogeny class is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}, but does not contain a {{KNOWL('ag.jacobian',title='Jacobian')}}. {% elif cl.jacobian_count is not none %} - This isogeny class contains the {{KNOWL('ag.jacobian',title='Jacobians' if cl.jacobian_count != 1 else 'Jacobian')}} of {{ cl.jacobian_count }} curve{% if cl.jacobian_count != 1 %}s{% endif %}, and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}. {% if cl.jacobian_count == 1 %}The curve whose Jacobian lies within this class is {% if cl.hyp_count == 0 %}not {% endif %}hyperelliptic{% else %}Of the curves whose Jacobians lie within this class, {% if cl.hyp_count == 1 %}1 is{% elif cl.hyp_count == cl.jacobian_count %}all are{% else %}{{ cl.hyp_count }} are{% endif %} hyperelliptic{% endif %}. The Jacobians of the following curves lie within this class:{{ cl.curve_display() | safe }} + This isogeny class is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}} and contains the {{KNOWL('ag.jacobian',title='Jacobians' if cl.jacobian_count != 1 else 'Jacobian')}} of {{ cl.jacobian_count }} curve{% if cl.jacobian_count != 1 %}s{% endif %} ({% if cl.jacobian_count == 1 %}which is {% if cl.hyp_count == 0 %}not {% endif %}hyperelliptic{% else %}of which {% if cl.hyp_count == 1 %}1 is{% elif cl.hyp_count == cl.jacobian_count %}all are{% else %}{{ cl.hyp_count }} are{% endif %} hyperelliptic{% endif %}):{{ cl.curve_display() | safe }} {% elif cl.hyp_count == 0 and cl.has_jacobian == 1 %} {# This case currently does not happen #} - This isogeny class contains a {{KNOWL('ag.jacobian',title='Jacobian')}}, and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}, but does not contain the Jacobian of a hyperelliptic curve. + This isogeny class contains a {{KNOWL('ag.jacobian',title='Jacobian')}} and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}, but does not contain the Jacobian of a hyperelliptic curve. {% elif cl.hyp_count == 0 and cl.has_jacobian == 0 %} This isogeny class is {{KNOWL('av.princ_polarizable', title = 'principally polarizable')}} and contains no Jacobian of a hyperelliptic curve, but it is unknown whether it contains a {{KNOWL('ag.jacobian',title='Jacobian')}} of a non-hyperelliptic curve. {% elif cl.hyp_count >= 1 %} - This isogeny class contains the {{KNOWL('ag.jacobian',title='Jacobians' if cl.hyp_count != 1 else 'Jacobian')}} of {{ cl.hyp_count }} hyperelliptic curve{% if cl.hyp_count != 1 %}s{% endif %}, and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}, but it is unknown how many Jacobians of non-hyperelliptic curves it contains:{{ cl.curve_display() | safe }} + This isogeny class is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}} and contains the {{KNOWL('ag.jacobian',title='Jacobians' if cl.hyp_count != 1 else 'Jacobian')}} of {{ cl.hyp_count }} hyperelliptic curve{% if cl.hyp_count != 1 %}s{% endif %}, but it is unknown how many Jacobians of non-hyperelliptic curves it contains:{{ cl.curve_display() | safe }} {% elif cl.has_jacobian == 1 %} - This isogeny class contains a {{KNOWL('ag.jacobian',title='Jacobian')}}, and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}. + This isogeny class contains a {{KNOWL('ag.jacobian',title='Jacobian')}} and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}. {% else %} This isogeny class is {{KNOWL('av.princ_polarizable', title = 'principally polarizable')}}, but it is unknown whether it contains a {{KNOWL('ag.jacobian',title='Jacobian')}}. {% endif %} From 53a51c3a5bc14f22b7abe7dbde9c8acda5139893 Mon Sep 17 00:00:00 2001 From: David Roe Date: Sat, 9 Nov 2024 23:18:28 -0500 Subject: [PATCH 08/18] Working on browse page --- lmfdb/abvar/fq/main.py | 28 ++++++++++++++------- lmfdb/abvar/fq/templates/abvarfq-index.html | 13 +++++++--- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/lmfdb/abvar/fq/main.py b/lmfdb/abvar/fq/main.py index 3f57d925cb..bccf25018c 100644 --- a/lmfdb/abvar/fq/main.py +++ b/lmfdb/abvar/fq/main.py @@ -168,7 +168,7 @@ class AbvarSearchArray(SearchArray): ("q", "field", ['q', 'g', 'poly']), ("p", "characteristic", ['p', 'q', 'g', 'poly']), ("p_rank", "p-rank", ['p_rank', 'g', 'q', 'poly']), - ("p_rank_deficit", "p-rank deficit", ['p_rank_deficit', 'g', 'q', 'poly']), + ("p_corank", "p-corank", ['p_rank_deficit', 'g', 'q', 'poly']), ("curve_count", "curve points", ['curve_count', 'g', 'q', 'poly']), ("abvar_count", "abvar points", ['abvar_count', 'g', 'q', 'poly'])] jump_example = "2.16.am_cn" @@ -206,9 +206,9 @@ def __init__(self): knowl="av.fq.p_rank", example="2" ) - p_rank_deficit = TextBox( - "p_rank_deficit", - label="$p$-rank deficit", + p_corank = TextBox( + "p_corank", + label="$p$-corank", knowl="av.fq.p_rank", example="2", advanced=True, @@ -221,6 +221,14 @@ def __init__(self): example_col=False, advanced=True, ) + angle_corank = TextBox( + "angle_corank", + label="Angle corank", + knowl="av.fq.angle_rank", + example="3", + example_col=False, + advanced=True, + ) newton_polygon = TextBox( "newton_polygon", label="Slopes of Newton polygon", @@ -482,7 +490,7 @@ def short_label(d): [simple, geom_simple, primitive, polarizable, jacobian], [newton_polygon, abvar_point_count, curve_point_count, simple_factors], [angle_rank, jac_cnt, hyp_cnt, twist_count, max_twist_degree], - [geom_deg, p_rank_deficit, geom_squarefree], + [angle_corank, geom_deg, p_corank, geom_squarefree], use_geom_refine, [dim1, dim2, dim3, dim4, dim5], [dim1d, dim2d, dim3d, number_field, galois_group], @@ -493,10 +501,11 @@ def short_label(d): [g, geom_simple], [initial_coefficients, polarizable], [p_rank, jacobian], - [p_rank_deficit, geom_squarefree], + [p_corank, geom_squarefree], [jac_cnt, hyp_cnt], - [geom_deg, angle_rank], + [angle_rank, angle_corank], [twist_count, max_twist_degree], + [geom_deg], [newton_polygon], [abvar_point_count], [curve_point_count], @@ -529,8 +538,9 @@ def common_parse(info, query): parse_bool_unknown(info, query, "jacobian", qfield="has_jacobian") parse_bool_unknown(info, query, "polarizable", qfield="has_principal_polarization") parse_ints(info, query, "p_rank") - parse_ints(info, query, "p_rank_deficit") + parse_ints(info, query, "p_corank", qfield="p_rank_deficit") parse_ints(info, query, "angle_rank") + parse_ints(info, query, "angle_corank") parse_ints(info, query, "jac_cnt", qfield="jacobian_count", name="Number of Jacobians") parse_ints(info, query, "hyp_cnt", qfield="hyp_count", name="Number of Hyperelliptic Jacobians") parse_ints(info, query, "twist_count") @@ -626,7 +636,7 @@ def extended_code(c): MathCol("p", "ag.base_field", "Base char.", short_title="base characteristic", default=False), MathCol("formatted_polynomial", "av.fq.l-polynomial", "L-polynomial", short_title="L-polynomial", download_col="polynomial"), MathCol("p_rank", "av.fq.p_rank", "$p$-rank"), - MathCol("p_rank_deficit", "av.fq.p_rank", "$p$-rank deficit", default=False), + MathCol("p_rank_deficit", "av.fq.p_rank", "$p$-corank", default=False), MathCol("curve_count", "av.fq.curve_point_counts", "points on curve", default=False), MathCol("abvar_count", "ag.fq.point_counts", "points on variety", default=False), MathCol("jacobian_count", "av.jacobian_count", "jacobians", default=False), diff --git a/lmfdb/abvar/fq/templates/abvarfq-index.html b/lmfdb/abvar/fq/templates/abvarfq-index.html index 67c1145fac..b198be9f97 100644 --- a/lmfdb/abvar/fq/templates/abvarfq-index.html +++ b/lmfdb/abvar/fq/templates/abvarfq-index.html @@ -14,14 +14,21 @@

Browse

{% for g in info.stats.gs %} {{g}}  {% endfor %} By {{ KNOWL('ag.base_field', 'base field cardinality') }}: - {% for q in info.q_ranges %} {{q}}  {% endfor %} + {% for q in info.q_ranges %} {{q}}  {% endfor %} By {{ KNOWL('av.fq.p_rank', '$p$-rank') }}: {% for r in [0] + info.stats.gs %} {{r}}  {% endfor %} + By {{ KNOWL('av.fq.p_rank', '$p$-corank') }}: + {% for r in [0] + info.stats.gs %} {{r}}  {% endfor %} - Some interesting isogeny classes or a random isogeny class + By {{ KNOWL('av.fq.angle_rank', 'angle rank') }}: + {% for r in [0] + info.stats.gs %} {{r}}  {% endfor %} + By {{ KNOWL('av.fq.angle_rank', 'angle corank') }}: + {% for r in [0] + info.stats.gs %} {{r}}  {% endfor %} - A table by dimension and base field. + Some interesting isogeny classes or a random isogeny class + + A table by dimension and base field. From 35222a1ab60cc430d4b1d97ff5f3d949367c99fc Mon Sep 17 00:00:00 2001 From: David Roe Date: Sun, 10 Nov 2024 03:47:04 -0500 Subject: [PATCH 09/18] Add search columns and sorts --- lmfdb/abvar/fq/isog_class.py | 17 ++++- lmfdb/abvar/fq/main.py | 74 +++++++++++++++++---- lmfdb/abvar/fq/templates/abvarfq-index.html | 2 + lmfdb/abvar/fq/templates/show-abvarfq.html | 2 +- 4 files changed, 78 insertions(+), 17 deletions(-) diff --git a/lmfdb/abvar/fq/isog_class.py b/lmfdb/abvar/fq/isog_class.py index 8aeb8e33be..24cae9d84f 100644 --- a/lmfdb/abvar/fq/isog_class.py +++ b/lmfdb/abvar/fq/isog_class.py @@ -19,7 +19,7 @@ from sage.rings.all import Integer, QQ, RR, ZZ from sage.plot.all import line, points, circle, polygon, Graphics -from sage.misc import latex +from sage.misc.latex import latex from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute @@ -114,7 +114,7 @@ def expanded_polynomial(self): if self.is_simple and QQ['x'](self.polynomial).is_irreducible(): return "" else: - return latex.latex(QQ[['x']](self.polynomial)) + return latex(QQ[['x']](self.polynomial)) @property def p(self): @@ -133,6 +133,10 @@ def polygon_slopes(self): # Remove the multiset indicators return [s[:-1] for s in self.slopes] + @property + def pretty_slopes(self): + return "[" + ",".join(latex(QQ(s)) for s in self.polygon_slopes) + "]" + @property def polynomial(self): return self.poly @@ -281,6 +285,9 @@ def is_ordinary(self): def is_supersingular(self): return all(slope == "1/2" for slope in self.polygon_slopes) + def is_almost_ordinary(self): + return self.newton_elevation == 1 + def display_slopes(self): return "[" + ", ".join(self.polygon_slopes) + "]" @@ -306,6 +313,10 @@ def display_galois_group(self): else: return transitive_group_display_knowl(self.galois_groups[0]) + def galois_groups_pretty(self): + # Used in search result pages + return ", ".join(transitive_group_display_knowl(gal, cache=self.gal_cache) for gal in self.galois_groups) + def decomposition_display_search(self): if self.is_simple: return "simple" @@ -503,7 +514,7 @@ def describe_end_algebra(p, extension_label): ans[1] += '${0}$'.format(inv) ans[1] += "\n" center_poly = db.nf_fields.lookup(center, 'coeffs') - center_poly = latex.latex(ZZ["x"](center_poly)) + center_poly = latex(ZZ["x"](center_poly)) ans[1] += r"where $\pi$ is a root of ${0}$.".format(center_poly) ans[1] += "\n" return ans diff --git a/lmfdb/abvar/fq/main.py b/lmfdb/abvar/fq/main.py index 05be2c0f7a..005c42c245 100644 --- a/lmfdb/abvar/fq/main.py +++ b/lmfdb/abvar/fq/main.py @@ -19,8 +19,9 @@ from .search_parsing import parse_nf_string, parse_galgrp from .isog_class import validate_label, AbvarFq_isoclass from .stats import AbvarFqStats +from lmfdb.number_fields.web_number_field import nf_display_knowl, field_pretty from lmfdb.utils import redirect_no_cache -from lmfdb.utils.search_columns import SearchColumns, SearchCol, MathCol, LinkCol +from lmfdb.utils.search_columns import SearchColumns, SearchCol, MathCol, LinkCol, ProcessedCol, CheckCol, CheckMaybeCol from lmfdb.abvar.fq.download import AbvarFq_download logger = make_logger("abvarfq") @@ -168,9 +169,15 @@ class AbvarSearchArray(SearchArray): ("q", "field", ['q', 'g', 'poly']), ("p", "characteristic", ['p', 'q', 'g', 'poly']), ("p_rank", "p-rank", ['p_rank', 'g', 'q', 'poly']), - ("p_corank", "p-corank", ['p_rank_deficit', 'g', 'q', 'poly']), + ("angle_rank", "angle rank", ['angle_rank', 'g', 'q', 'poly']), + ("elevation", "Newton elevation", ['newton_elevation', 'g', 'q', 'poly']), ("curve_count", "curve points", ['curve_count', 'g', 'q', 'poly']), - ("abvar_count", "abvar points", ['abvar_count', 'g', 'q', 'poly'])] + ("abvar_count", "abvar points", ['abvar_count', 'g', 'q', 'poly']), + ("jacobian_count", "Jacobian count", ['jacobian_count', 'g', 'q', 'poly']), + ("hyp_count", "Hyp. Jacobian count", ['hyp_count', 'g', 'q', 'poly']), + ("twist_count", "Num .twists", ['twist_count', 'g', 'q', 'poly']), + ("max_twist_degree", "Max. twist degree", ['max_twist_degree', 'g', 'q', 'poly']), + ("geom_deg", "End. degree", ['geometric_extension_degree', 'g', 'q', 'poly'])] jump_example = "2.16.am_cn" jump_egspan = "e.g. 2.16.am_cn or 1 - x + 2x^2 or x^2 - x + 2" jump_knowl = "av.fq.search_input" @@ -229,6 +236,14 @@ def __init__(self): example_col=False, advanced=True, ) + newton_elevation = TextBox( + "newton_elevation", + label="Newton elevation", + knowl="av.fq.newton_elevation", + example="1", + example_col=False, + advanced=True, + ) newton_polygon = TextBox( "newton_polygon", label="Slopes of Newton polygon", @@ -489,8 +504,8 @@ def short_label(d): [q, p, g, p_rank, initial_coefficients], [simple, geom_simple, primitive, polarizable, jacobian], [newton_polygon, abvar_point_count, curve_point_count, simple_factors], - [angle_rank, jac_cnt, hyp_cnt, twist_count, max_twist_degree], - [angle_corank, geom_deg, p_corank, geom_squarefree], + [newton_elevation, jac_cnt, hyp_cnt, twist_count, max_twist_degree], + [angle_rank, angle_corank, geom_deg, p_corank, geom_squarefree], use_geom_refine, [dim1, dim2, dim3, dim4, dim5], [dim1d, dim2d, dim3d, number_field, galois_group], @@ -505,7 +520,7 @@ def short_label(d): [jac_cnt, hyp_cnt], [angle_rank, angle_corank], [twist_count, max_twist_degree], - [geom_deg], + [newton_elevation, geom_deg], [newton_polygon], [abvar_point_count], [curve_point_count], @@ -541,6 +556,7 @@ def common_parse(info, query): parse_ints(info, query, "p_corank", qfield="p_rank_deficit") parse_ints(info, query, "angle_rank") parse_ints(info, query, "angle_corank") + parse_ints(info, query, "newton_elevation") parse_ints(info, query, "jac_cnt", qfield="jacobian_count", name="Number of Jacobians") parse_ints(info, query, "hyp_cnt", qfield="hyp_count", name="Number of Hyperelliptic Jacobians") parse_ints(info, query, "twist_count") @@ -629,20 +645,52 @@ def extended_code(c): jump_box = "%s.%s.%s" % (g, q, "_".join(extended_code(cdict.get(i, 0)) for i in range(1, g+1))) return by_label(jump_box) +# simple, geom. simple, primitive, princ polarizable, Jacobian +# F_q^k points on curve/variety + abvar_columns = SearchColumns([ - LinkCol("label", "ab.fq.lmfdb_label", "Label", url_for_label), + LinkCol("label", "av.fq.lmfdb_label", "Label", url_for_label), MathCol("g", "ag.dimension", "Dimension"), MathCol("field", "ag.base_field", "Base field", download_col="q"), MathCol("p", "ag.base_field", "Base char.", short_title="base characteristic", default=False), + CheckCol("is_simple", "av.simple", "Simple", default=False), + CheckCol("is_geometrically_simple", "av.geometrically_simple", "Geom. simple", default=False), + CheckCol("is_primitive", "ag.primitive", "Primitive", default=False), + CheckCol("is_ordinary", "av.fq.ordinary", "Ordinary", default=False), + CheckCol("is_almost_ordinary", "av.fq.newton_elevation", "Almost ordinary", default=False), + CheckCol("is_supersingular", "av.fq.supersingular", "Supersingular", default=False), + CheckMaybeCol("has_principal_polarization", "av.princ_polarizable", "Princ. polarizable", default=False), + CheckMaybeCol("has_jacobian", "ag.jacobian", "Jacobian", default=False), MathCol("formatted_polynomial", "av.fq.l-polynomial", "L-polynomial", short_title="L-polynomial", download_col="polynomial"), + MathCol("pretty_slopes", "lf.newton_polygon", "Newton slopes", default=False), + MathCol("newton_elevation", "av.fq.newton_elevation", "Newton elevation", default=False), MathCol("p_rank", "av.fq.p_rank", "$p$-rank"), MathCol("p_rank_deficit", "av.fq.p_rank", "$p$-corank", default=False), - MathCol("curve_count", "av.fq.curve_point_counts", "points on curve", default=False), - MathCol("abvar_count", "ag.fq.point_counts", "points on variety", default=False), - MathCol("jacobian_count", "av.jacobian_count", "jacobians", default=False), - MathCol("hyp_count", "av.hyperelliptic_count", "hyperelliptic jacobians", default=False), + MathCol("angle_rank", "av.fq.angle_rank", "Angle rank", default=False), + MathCol("angle_corank", "av.fq.angle_rank", "Angle corank", default=False), + MathCol("curve_count", "av.fq.curve_point_counts", r"$\mathbb{F}_q$ points on curve", short_title="Fq points on curve", default=False), + MathCol("curve_counts", "av.fq.curve_point_counts", r"$\mathbb{F}_{q^k}$ points on curve", short_title="Fq^k points on curve", default=False), + MathCol("abvar_count", "ag.fq.point_counts", "$\mathbb{F}_q$ points on variety", short_title="Fq points on variety", default=False), + MathCol("abvar_counts", "ag.fq.point_counts", "$\mathbb{F}_{q^k}$ points on variety", short_title="Fq^k points on variety", default=False), + MathCol("jacobian_count", "av.jacobian_count", "Jacobians", default=False), + MathCol("hyp_count", "av.hyperelliptic_count", "Hyperelliptic Jacobians", default=False), + MathCol("twist_count", "av.twist", "Num. twists", default=False), + MathCol("max_twist_degree", "av.twist", "Max. twist degree", default=False), + MathCol("geometric_extension_degree", "av.endomorphism_field", "End. degree", default=False), + ProcessedCol("number_fields", "av.fq.number_field", "Number fields", lambda nfs: ", ".join(nf_display_knowl(nf, field_pretty(nf)) for nf in nfs), default=False), + SearchCol("galois_groups_pretty", "nf.galois_group", "Galois groups", download_col="galois_groups", default=False), SearchCol("decomposition_display_search", "av.decomposition", "Isogeny factors", download_col="decompositionraw")], - db_cols=["label", "g", "q", "poly", "p_rank", "p_rank_deficit", "is_simple", "simple_distinct", "simple_multiplicities", "is_primitive", "primitive_models", "curve_count", "abvar_count", "jacobian_count", "hyp_count"]) + db_cols=["label", "g", "q", "poly", "p_rank", "p_rank_deficit", "is_simple", "is_geometrically_simple", "simple_distinct", "simple_multiplicities", "is_primitive", "primitive_models", "curve_count", "curve_counts", "abvar_count", "abvar_counts", "jacobian_count", "hyp_count", "number_fields", "galois_groups", "slopes", "newton_elevation", "twist_count", "max_twist_degree", "geometric_extension_degree", "angle_rank", "angle_corank", "is_supersingular", "has_principal_polarization", "has_jacobian"]) + +def abvar_postprocess(res, info, query): + gals = set() + for A in res: + for gal in A["galois_groups"]: + gals.add(gal) + cache = {rec["label"]: rec for rec in db.gps_transitive.search({"label": {"$in": list(gals)}}, ["label", "pretty"])} + for A in res: + A["gal_cache"] = cache + return [AbvarFq_isoclass(x) for x in res] @search_wrap( table=db.av_fq_isog, @@ -653,7 +701,7 @@ def extended_code(c): "jump": jump, "download": AbvarFq_download(), }, - postprocess=lambda res, info, query: [AbvarFq_isoclass(x) for x in res], + postprocess=abvar_postprocess, url_for_label=url_for_label, learnmore=learnmore_list, bread=lambda: get_bread(("Search results", " ")), diff --git a/lmfdb/abvar/fq/templates/abvarfq-index.html b/lmfdb/abvar/fq/templates/abvarfq-index.html index b198be9f97..ef5447984a 100644 --- a/lmfdb/abvar/fq/templates/abvarfq-index.html +++ b/lmfdb/abvar/fq/templates/abvarfq-index.html @@ -25,6 +25,8 @@

Browse

{% for r in [0] + info.stats.gs %} {{r}}  {% endfor %} By {{ KNOWL('av.fq.angle_rank', 'angle corank') }}: {% for r in [0] + info.stats.gs %} {{r}}  {% endfor %} + + By primitive    simple    geom. simple    ordinary    almost ordinary    supersingular    princ. polarizable    Jacobian Some interesting isogeny classes or a random isogeny class diff --git a/lmfdb/abvar/fq/templates/show-abvarfq.html b/lmfdb/abvar/fq/templates/show-abvarfq.html index ffc2058425..af92927ac1 100644 --- a/lmfdb/abvar/fq/templates/show-abvarfq.html +++ b/lmfdb/abvar/fq/templates/show-abvarfq.html @@ -157,7 +157,7 @@

Jacobians and polarizations

This isogeny class contains a {{KNOWL('ag.jacobian',title='Jacobian')}} and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}, but does not contain the Jacobian of a hyperelliptic curve. {% elif cl.hyp_count == 0 and cl.has_jacobian == 0 %} This isogeny class is {{KNOWL('av.princ_polarizable', title = 'principally polarizable')}} and contains no Jacobian of a hyperelliptic curve, but it is unknown whether it contains a {{KNOWL('ag.jacobian',title='Jacobian')}} of a non-hyperelliptic curve. - {% elif cl.hyp_count >= 1 %} + {% elif cl.hyp_count is not none and cl.hyp_count >= 1 %} This isogeny class is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}} and contains the {{KNOWL('ag.jacobian',title='Jacobians' if cl.hyp_count != 1 else 'Jacobian')}} of {{ cl.hyp_count }} hyperelliptic curve{% if cl.hyp_count != 1 %}s{% endif %}, but it is unknown how many Jacobians of non-hyperelliptic curves it contains:{{ cl.curve_display() | safe }} {% elif cl.has_jacobian == 1 %} This isogeny class contains a {{KNOWL('ag.jacobian',title='Jacobian')}} and hence is {{ KNOWL('av.princ_polarizable', title = 'principally polarizable')}}. From 50b7c05a473041242a778ed319043f5185abec99 Mon Sep 17 00:00:00 2001 From: John Voight Date: Tue, 12 Nov 2024 13:25:42 +1100 Subject: [PATCH 10/18] Update management.html --- lmfdb/templates/management.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmfdb/templates/management.html b/lmfdb/templates/management.html index e89a8a0c0d..4f596635df 100644 --- a/lmfdb/templates/management.html +++ b/lmfdb/templates/management.html @@ -6,7 +6,7 @@

Managing Editors

  • John Jones, Arizona State University, USA
  • Jennifer Paulhus, Mount Holyoke College, USA
  • Andrew Sutherland, MIT, USA
  • -
  • John Voight, Dartmouth College, USA
  • +
  • John Voight, University of Sydney, USA
  • Associate Editors

    From 587adee567c88d1a3727a5e2fa4e65e90dc94168 Mon Sep 17 00:00:00 2001 From: John Voight Date: Tue, 12 Nov 2024 13:25:51 +1100 Subject: [PATCH 11/18] Update management.html --- lmfdb/templates/management.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmfdb/templates/management.html b/lmfdb/templates/management.html index 4f596635df..3292020949 100644 --- a/lmfdb/templates/management.html +++ b/lmfdb/templates/management.html @@ -6,7 +6,7 @@

    Managing Editors

  • John Jones, Arizona State University, USA
  • Jennifer Paulhus, Mount Holyoke College, USA
  • Andrew Sutherland, MIT, USA
  • -
  • John Voight, University of Sydney, USA
  • +
  • John Voight, University of Sydney, Australia
  • Associate Editors

    From 6fe5589e9825849f919577b6077de15b75ff5b80 Mon Sep 17 00:00:00 2001 From: John Jones Date: Wed, 13 Nov 2024 16:00:57 -0500 Subject: [PATCH 12/18] Remove use of color to designate complex conjugation --- .../templates/artin-representation-show.html | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lmfdb/artin_representations/templates/artin-representation-show.html b/lmfdb/artin_representations/templates/artin-representation-show.html index 16f3d6fc3e..1d69299fad 100644 --- a/lmfdb/artin_representations/templates/artin-representation-show.html +++ b/lmfdb/artin_representations/templates/artin-representation-show.html @@ -98,16 +98,18 @@

    Character values on conjugacy classes

    {% endfor %}$ {% else %} $r_1, \ldots, r_{ {{object.number_field_galois_group().degree()}} }$ - {% endif %}Character value + {% endif %}Character valueComplex conjugation {% for gen in object.number_field_galois_group().conjugacy_classes()%} - - ${{gen.size()}}$${{gen.order()}}$${{cycle_string(gen.representative())}}$${{ object.character_formatted()[loop.index - 1] }}$ + + ${{gen.size()}}$${{gen.order()}}$${{cycle_string(gen.representative())}}$${{ object.character_formatted()[loop.index - 1] }}$ + + {% if loop.index == object.number_field_galois_group().index_complex_conjugation()%}✓{%endif%} + {% endfor %} -

    The blue line marks the conjugacy class containing complex conjugation.

    {# For testing in progress

    From 1606d3eae432d0ec44f8fa23d16a365d4db691e7 Mon Sep 17 00:00:00 2001 From: David Roe Date: Thu, 14 Nov 2024 02:22:52 -0500 Subject: [PATCH 13/18] Change color of Search again button when inputs change --- lmfdb/templates/refine_search_form.html | 12 +++++++++++- lmfdb/templates/style.css | 7 +++++++ lmfdb/utils/search_boxes.py | 7 ++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lmfdb/templates/refine_search_form.html b/lmfdb/templates/refine_search_form.html index 31923d6faf..3ebfbcfd77 100644 --- a/lmfdb/templates/refine_search_form.html +++ b/lmfdb/templates/refine_search_form.html @@ -30,6 +30,16 @@

    {{KNOWL('intro.search', search_header)}} + +show_advancedQ(); {% endif %} + +function set_fresh() { + $(".search_stale").removeClass("search_stale").addClass("search_fresh"); +} + +$("input").on("input", set_fresh); +$("select").on("change", set_fresh); + + diff --git a/lmfdb/templates/style.css b/lmfdb/templates/style.css index e9ed611cd4..0adf995ec6 100644 --- a/lmfdb/templates/style.css +++ b/lmfdb/templates/style.css @@ -2262,3 +2262,10 @@ div.upload_section { .grecaptcha-badge { visibility: hidden; } + +button.search_stale, button.search_stale:hover { + background: {{color.select_background}}; +} +button.search_fresh { + background: {{color.button_background}}; +} diff --git a/lmfdb/utils/search_boxes.py b/lmfdb/utils/search_boxes.py index 031cb19c8d..31177fa8ad 100644 --- a/lmfdb/utils/search_boxes.py +++ b/lmfdb/utils/search_boxes.py @@ -610,11 +610,16 @@ def _input(self, info): onclick = "" else: onclick = " onclick='resetStart()'" - btext = "" + if self.description in ["Search again", "Generate statistics"]: + cls = " class='search_stale'" + else: + cls = " class='search_fresh'" + btext = "" return btext.format( width=self.width, val=self.value, desc=self.description, + cls=cls, onclick=onclick) class SearchButtonWithSelect(SearchButton): From 4427a19e4bafc39da4e0e91520d2054102aec273 Mon Sep 17 00:00:00 2001 From: David Roe Date: Thu, 14 Nov 2024 15:51:52 -0500 Subject: [PATCH 14/18] Add some knowls --- .../templates/artin-representation-show.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lmfdb/artin_representations/templates/artin-representation-show.html b/lmfdb/artin_representations/templates/artin-representation-show.html index 1d69299fad..09b1a14ca6 100644 --- a/lmfdb/artin_representations/templates/artin-representation-show.html +++ b/lmfdb/artin_representations/templates/artin-representation-show.html @@ -68,7 +68,7 @@

    Defining polynomial

    -

    Generators of the action on the roots +

    Generators of the {{KNOWL('gg.galois_group', 'action')}} on the roots {% if object.number_field_galois_group().degree() < 4 %} ${% for i in range(1, object.number_field_galois_group().degree()+1) %} r_{ {{i}} }{% if not loop.last %}, {% endif %} @@ -89,16 +89,16 @@

    Generators of the action on the roots -

    Character values on conjugacy classes

    +

    {{KNOWL('group.representation.character', 'Character values')}} on {{KNOWL('gg.conjugacy_classes', 'conjugacy classes')}}

    - + {% endif %} {% for gen in object.number_field_galois_group().conjugacy_classes()%} From 9e27934069aa1e22678759fbd6368bed9a7e8d5a Mon Sep 17 00:00:00 2001 From: David Roe Date: Thu, 14 Nov 2024 23:06:06 -0500 Subject: [PATCH 15/18] Fix bug in decoding PC group elements --- lmfdb/groups/abstract/test_abstract_groups.py | 5 +++++ lmfdb/groups/abstract/web_groups.py | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lmfdb/groups/abstract/test_abstract_groups.py b/lmfdb/groups/abstract/test_abstract_groups.py index 27703e8bb1..aff1f0cc7c 100644 --- a/lmfdb/groups/abstract/test_abstract_groups.py +++ b/lmfdb/groups/abstract/test_abstract_groups.py @@ -31,6 +31,11 @@ def test_abstract_group_download(self): self.assertTrue("If the group is solvable" in response.get_data(as_text=True)) self.assertTrue("encd:= 293961739841108398509157889" in response.get_data(as_text=True)) + def test_conj_decode(self): + from lmfdb.groups.abstract.web_groups import WebAbstractGroup + G = WebAbstractGroup("18.2") + self.assertTrue(all(G.decode_as_pcgs(i, True) == f"a^{{{i}}}" for i in range(2,18))) + def character_counts(self): # There was a bug in showing all dimensions of irreducible characters when we don't store the complex character table page = self.tc.get("/Groups/Abstract/1800.328").get_data(as_text=True).replace(" ","").replace("\n","") diff --git a/lmfdb/groups/abstract/web_groups.py b/lmfdb/groups/abstract/web_groups.py index 53eaf19497..5d9aa0b430 100644 --- a/lmfdb/groups/abstract/web_groups.py +++ b/lmfdb/groups/abstract/web_groups.py @@ -1830,7 +1830,9 @@ def pcgs_relative_orders(self): def pcgs_expos_to_str(self, vec): w = [] e = 0 - for i, (c, m) in reversed(list(enumerate(zip(vec, reversed(self.pcgs_relative_orders))))): + # We need to shift the relative orders by 1, since we're multiplying on the previous pass of the for loop + relords = [1] + self.pcgs_relative_orders[:-1] + for i, (c, m) in reversed(list(enumerate(zip(vec, relords)))): e += c if i + 1 in self.gens_used: w.append(e) From d0a9769f9092410d7af62cfd8db89097bf74a878 Mon Sep 17 00:00:00 2001 From: David Roe Date: Fri, 15 Nov 2024 01:01:37 -0500 Subject: [PATCH 16/18] Add missing table to Maass downloader --- lmfdb/maass_forms/web_maassform.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lmfdb/maass_forms/web_maassform.py b/lmfdb/maass_forms/web_maassform.py index 5d1aeb5914..0b6bd8ea85 100644 --- a/lmfdb/maass_forms/web_maassform.py +++ b/lmfdb/maass_forms/web_maassform.py @@ -336,6 +336,7 @@ def coefficient_table(self, rows=20, cols=3, row_opts=[20,60,334]): class MaassFormDownloader(Downloader): + table = db.maass_rigor title = 'Maass forms' def download(self, label, lang='text'): From b9eebfad837e8bc2ce1b0ca7f8261e591cf9eb35 Mon Sep 17 00:00:00 2001 From: David Roe Date: Fri, 15 Nov 2024 02:15:58 -0500 Subject: [PATCH 17/18] Add top-level rcs page --- lmfdb/app.py | 6 ++++++ lmfdb/homepage/index_boxes.yaml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lmfdb/app.py b/lmfdb/app.py index 073616e0b3..94a417f095 100644 --- a/lmfdb/app.py +++ b/lmfdb/app.py @@ -359,6 +359,12 @@ def index(): def about(): return render_template("about.html", title="About the LMFDB") +@app.route("/rcs") +def top_rcs(): + t = "Source, reliability, and completeness" + bread = [(t, " ")] + return render_template("single.html", kid="rcs", title=t, bread=bread) + @app.route("/health") @app.route("/alive") diff --git a/lmfdb/homepage/index_boxes.yaml b/lmfdb/homepage/index_boxes.yaml index e9f6933cc6..778a62a512 100644 --- a/lmfdb/homepage/index_boxes.yaml +++ b/lmfdb/homepage/index_boxes.yaml @@ -18,7 +18,7 @@ links: --- title: Learn more image: smalluniverse -content:

    Information is available regarding the source, reliability, and completeness of the database.

    +content:

    Information is available regarding the source, reliability, and completeness of the database.

    Knowls provide explanations when you need them.

    Overview    LMFDB universe    Knowledge    Data

    control: 0 links: From bc5184f90f26e7c3049bc1decae9457b5969fe30 Mon Sep 17 00:00:00 2001 From: AndrewVSutherland Date: Fri, 15 Nov 2024 14:24:25 +0000 Subject: [PATCH 18/18] autopep8 action fixes --- lmfdb/abvar/fq/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lmfdb/abvar/fq/main.py b/lmfdb/abvar/fq/main.py index 005c42c245..82a43950d0 100644 --- a/lmfdb/abvar/fq/main.py +++ b/lmfdb/abvar/fq/main.py @@ -670,8 +670,8 @@ def extended_code(c): MathCol("angle_corank", "av.fq.angle_rank", "Angle corank", default=False), MathCol("curve_count", "av.fq.curve_point_counts", r"$\mathbb{F}_q$ points on curve", short_title="Fq points on curve", default=False), MathCol("curve_counts", "av.fq.curve_point_counts", r"$\mathbb{F}_{q^k}$ points on curve", short_title="Fq^k points on curve", default=False), - MathCol("abvar_count", "ag.fq.point_counts", "$\mathbb{F}_q$ points on variety", short_title="Fq points on variety", default=False), - MathCol("abvar_counts", "ag.fq.point_counts", "$\mathbb{F}_{q^k}$ points on variety", short_title="Fq^k points on variety", default=False), + MathCol("abvar_count", "ag.fq.point_counts", r"$\mathbb{F}_q$ points on variety", short_title="Fq points on variety", default=False), + MathCol("abvar_counts", "ag.fq.point_counts", r"$\mathbb{F}_{q^k}$ points on variety", short_title="Fq^k points on variety", default=False), MathCol("jacobian_count", "av.jacobian_count", "Jacobians", default=False), MathCol("hyp_count", "av.hyperelliptic_count", "Hyperelliptic Jacobians", default=False), MathCol("twist_count", "av.twist", "Num. twists", default=False),
    SizeOrderAction on +
    {{KNOWL('group.size_conjugacy_class', 'Size')}}{{KNOWL('group.order_conjugacy_class', 'Order')}}{{KNOWL('gg.galois_group', 'Action')}} on {% if object.number_field_galois_group().degree() < 4 %} ${% for i in range(1, object.number_field_galois_group().degree()+1) %} r_{ {{i}} }{% if not loop.last %}, {% endif %} {% endfor %}$ {% else %} $r_1, \ldots, r_{ {{object.number_field_galois_group().degree()}} }$ - {% endif %}Character valueComplex conjugation
    {{KNOWL('group.representation.character', 'Character value')}}{{KNOWL('artin.trace_of_complex_conj', 'Complex conjugation')}}