Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

master -> devv #5758

Merged
merged 20 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 50 additions & 17 deletions lmfdb/groups/abstract/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def find_props(
props = []
noted = set()
for prop in overall_order:
if not getattr(gp, prop) or prop in noted or prop not in show:
if not getattr(gp, prop, None) or prop in noted or prop not in show:
continue
noted.add(prop)
impl = [B for B in implications.get(prop, []) if B not in noted]
Expand Down Expand Up @@ -229,41 +229,54 @@ def find_props(

def get_group_prop_display(gp):
# We want elementary and hyperelementary to display which primes, but only once
elementaryp = ",".join(str(p) for (p, e) in ZZ(gp.elementary).factor())
hyperelementaryp = ",".join(
str(p)
for (p, e) in ZZ(gp.hyperelementary).factor()
if not p.divides(gp.elementary)
)
elementaryp = ''
hyperelementaryp = ''
if hasattr(gp, 'elementary'):
elementaryp = ",".join(str(p) for (p, e) in ZZ(gp.elementary).factor())
hyperelementaryp = ",".join(
str(p)
for (p, e) in ZZ(gp.hyperelementary).factor()
if not p.divides(gp.elementary)
)
if (
gp.order == 1
): # here it will be implied from cyclic, so both are in the implication list
elementaryp = " (for every $p$)"
hyperelementaryp = ""
elif gp.pgroup: # We don't display p since there's only one in play
elif hasattr(gp, 'pgroup') and gp.pgroup: # We don't display p since there's only one in play
elementaryp = hyperelementaryp = ""
elif gp.cyclic: # both are in the implication list
elementaryp = f" ($p = {elementaryp}$)"
if gp.elementary == gp.hyperelementary:
hyperelementaryp = ""
else:
hyperelementaryp = f" (also for $p = {hyperelementaryp}$)"
elif gp.is_elementary: # Now elementary is a top level implication
elif hasattr(gp, 'is_elementary') and gp.is_elementary: # Now elementary is a top level implication
elementaryp = f" for $p = {elementaryp}$"
if gp.elementary == gp.hyperelementary:
if hasattr(gp, 'hyperelementary') and gp.elementary == gp.hyperelementary:
hyperelementaryp = ""
else:
hyperelementaryp = f" (also for $p = {hyperelementaryp}$)"
elif gp.hyperelementary: # Now hyperelementary is a top level implication
elif hasattr(gp, 'hyperelementary') and gp.hyperelementary: # Now hyperelementary is a top level implication
hyperelementaryp = f" for $p = {hyperelementaryp}$"
nilp_class = getattr(gp, 'nilpotency_class', None)
if nilp_class is not None:
nilp_phrase = f"{display_knowl('group.nilpotent', 'nilpotent')} of class {nilp_class}"
else:
nilp_phrase = f"{display_knowl('group.nilpotent', 'nilpotent')} of uncomputed class"
solv_length = getattr(gp, 'derived_length', None)
if solv_length is not None:
solv_phrase = f"{display_knowl('group.solvable', 'solvable')} of {display_knowl('group.derived_series', 'length')} {solv_length}"
else:
solv_phrase = f"{display_knowl('group.solvable', 'solvable')} of uncomputed length"
overall_display = {
"cyclic": display_knowl("group.cyclic", "cyclic"),
"abelian": display_knowl("group.abelian", "abelian"),
"nonabelian": display_knowl("group.abelian", "nonabelian"),
"nilpotent": f"{display_knowl('group.nilpotent', 'nilpotent')} of class {gp.nilpotency_class}",
"nilpotent": nilp_phrase,
"supersolvable": display_knowl("group.supersolvable", "supersolvable"),
"monomial": display_knowl("group.monomial", "monomial"),
"solvable": f"{display_knowl('group.solvable', 'solvable')} of {display_knowl('group.derived_series', 'length')} {gp.derived_length}",
"solvable": solv_phrase,
"nonsolvable": display_knowl("group.solvable", "nonsolvable"),
"Zgroup": f"a {display_knowl('group.z_group', 'Z-group')}",
"Agroup": f"an {display_knowl('group.a_group', 'A-group')}",
Expand All @@ -289,9 +302,19 @@ def get_group_prop_display(gp):

def get_group_impl_display(gp):
# Mostly we display things the same in implication lists, but there are a few extra parentheses
nilp_class = getattr(gp, 'nilpotency_class', None)
if nilp_class is not None:
nilp_phrase = f"{display_knowl('group.nilpotent', 'nilpotent')} of class {nilp_class}"
else:
nilp_phrase = f"{display_knowl('group.nilpotent', 'nilpotent')} of uncomputed class"
solv_length = getattr(gp, 'derived_length', None)
if solv_length is not None:
solv_phrase = f"{display_knowl('group.solvable', 'solvable')} of {display_knowl('group.derived_series', 'length')} {solv_length}"
else:
solv_phrase = f"{display_knowl('group.solvable', 'solvable')} of uncomputed length"
return {
"nilpotent": f"{display_knowl('group.nilpotent', 'nilpotent')} (of class {gp.nilpotency_class})",
"solvable": f"{display_knowl('group.solvable', 'solvable')} (of {display_knowl('group.derived_series', 'length')} {gp.derived_length})",
"nilpotent": f"{display_knowl('group.nilpotent', 'nilpotent')} ({nilp_phrase})",
"solvable": f"{display_knowl('group.solvable', 'solvable')} ({solv_phrase})",
}


Expand Down Expand Up @@ -468,7 +491,11 @@ def create_boolean_subgroup_string(sgp, type="normal"):
main = f"The subgroup is {display_props(props)}."
else:
main = f"This subgroup is {display_props(props)}."
unknown = [overall_display[prop] for prop in overall_order if getattr(sgp, prop) is None]
unknown = [prop for prop in overall_order if getattr(sgp, prop, None) is None]
if {'ab_simple', 'nab_simple'} <= set(unknown):
unknown.remove('ab_simple')

unknown = [overall_display[prop] for prop in unknown]
if unknown:
main += f" Whether it is {display_props(unknown, 'or')} has not been computed."
return main
Expand All @@ -478,6 +505,8 @@ def create_boolean_string(gp, type="normal"):
# We totally order the properties in two ways: by the order that they should be listed overall,
# and by the order they should be listed in implications
# For the first order, it's important that A come before B whenever A => B
if not gp:
return "Properties have not been computed"
overall_order = [
"cyclic",
"abelian",
Expand Down Expand Up @@ -563,7 +592,11 @@ def create_boolean_string(gp, type="normal"):
main = f"{display_props(props)}."
else:
main = f"This group is {display_props(props)}."
unknown = [overall_display[prop] for prop in overall_order if getattr(gp, prop) is None]
unknown = [prop for prop in overall_order if getattr(gp, prop, None) is None]
if {'ab_simple', 'nab_simple'} <= set(unknown):
unknown.remove('ab_simple')

unknown = [overall_display[prop] for prop in unknown]
if unknown and type != "knowl":
main += f" Whether it is {display_props(unknown, 'or')} has not been computed."
return main
Expand Down
40 changes: 35 additions & 5 deletions lmfdb/groups/abstract/templates/abstract-show-subgroup.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ <h2> Subgroup ($H$) information</h2>
{% endif %}
<tr><td>{{KNOWL('group.order',title='Order:')}}</td><td> {{info.pos_int_and_factor(seq.subgroup_order)}} </td></tr>
<tr><td>{{KNOWL('group.subgroup.index',title='Index:')}}</td><td> {{info.pos_int_and_factor(seq.quotient_order)}} </td></tr>
<tr><td>{{KNOWL('group.exponent',title='Exponent:')}}</td><td> {{info.pos_int_and_factor(seq.sub.exponent)}} </td></tr>
<tr><td>{{KNOWL('group.exponent',title='Exponent:')}}</td><td>
{% if seq.sub.exponent %}
{{info.pos_int_and_factor(seq.sub.exponent)}}
{% else %}
not computed
{% endif %}
</td></tr>
{% if seq.amb.show_subgroup_flag() %}<tr><td>{{KNOWL('group.generators', 'Generators:')}}</td><td> ${{seq.amb.show_subgroup_generators(seq)}}$</td></tr>{% endif %}
</table>
</p>
Expand Down Expand Up @@ -60,9 +66,27 @@ <h2> Quotient group ($Q$) structure </h2>
{% if seq.quotient is not none %} <tr><td>{{KNOWL('group.name', title='Description:')}}</td>
<td><a href="{{url_for('.by_label', label=seq.quotient)}}">${{seq.quotient_tex}}$</a></td></tr> {% endif %}
<tr><td>{{KNOWL('group.order',title='Order:')}}</td><td> {{info.pos_int_and_factor(seq.quotient_order)}} </td></tr>
<tr><td>{{KNOWL('group.exponent',title='Exponent:')}}</td><td> {{info.pos_int_and_factor(seq.quo.exponent)}} </td></tr>
<tr><td>Automorphism Group:</td><td>{{seq.quo.show_aut_group()|safe}}</td></tr>
<tr><td>Outer Automorphisms:</td><td>{{seq.quo.show_outer_group()|safe}}</td></tr>
<tr><td>{{KNOWL('group.exponent',title='Exponent:')}}</td><td>
{% if seq.sub|attr('exponent') %}
{{info.pos_int_and_factor(seq.quo.exponent)}}
{% else %}
not computed
{% endif %}
</td></tr>
<tr><td>Automorphism Group:</td><td>
{% if seq.quo|attr('show_aut_group')%}
{{seq.quo.show_aut_group()|safe}}
{% else %}
not computed
{% endif %}
</td></tr>
<tr><td>Outer Automorphisms:</td><td>
{% if seq.quo|attr('show_outer_group')%}
{{seq.quo.show_outer_group()|safe}}
{% else %}
not computed
{% endif %}
</td></tr>
</table>
</p>

Expand All @@ -88,7 +112,13 @@ <h2> Automorphism information </h2>
{% endif %}
<table>
<tr><td>$\operatorname{Aut}(G)$</td><td>{{seq.amb.show_aut_group()|safe}}</td></tr>
<tr><td>$\operatorname{Aut}(H)$</td><td>{{seq.sub.show_aut_group()|safe}}</td></tr>
<tr><td>$\operatorname{Aut}(H)$</td><td>
{% if seq.sub|attr('show_aut_group') %}
{{seq.sub.show_aut_group()|safe}}
{% else %}
not computed
{% endif %}
</td></tr>
{% if seq.aut_weyl_group is not none %}
<tr><td>$\operatorname{res}({{S}})$</td><td><a href="{{url_for('.by_label', label=seq.aut_weyl_group)}}">${{seq.aut_weyl.tex_name}}$</a>, of order {{info.pos_int_and_factor(seq.aut_weyl.order)}}</td></tr>
{% elif seq.aut_weyl_index is not none %}
Expand Down
53 changes: 41 additions & 12 deletions lmfdb/groups/abstract/web_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,9 @@ def order(self):
return ZZ(self.G.Order())
@lazy_attribute
def exponent(self):
return ZZ(self.G.Exponent())
if self.G:
return ZZ(self.G.Exponent())
return None

@lazy_attribute
def cyclic(self):
Expand Down Expand Up @@ -1832,8 +1834,8 @@ def auto_gens_list(self):
gens = self.aut_gens
return [ [ self.decode(gen, as_str=True) for gen in gens[i]] for i in range(len(gens))]

def representation_line(self, rep_type):
# TODO: Add links to searches for other representations when available
def representation_line(self, rep_type,inc_matrix):
# TODO: Add links to searches for other representations when available Inc_matrix is 0 if first time a matrix group and 1 otherwise
if rep_type != "PC":
rdata = self.representations[rep_type]
if rep_type == "Lie":
Expand Down Expand Up @@ -1864,21 +1866,35 @@ def representation_line(self, rep_type):
R, N, k, d, _ = self._matrix_coefficient_data(rep_type, as_str=True)
gens = ", ".join(self.decode_as_matrix(g, rep_type, as_str=True) for g in rdata["gens"])
gens = fr"$\left\langle {gens} \right\rangle \subseteq \GL_{{{d}}}({R})$"
return f'<tr><td>{display_knowl("group.matrix_group", "Matrix group")}:</td><td colspan="5">{gens}</td></tr>'
if inc_matrix == 0:
return f'<tr><td>{display_knowl("group.matrix_group", "Matrix group")}:</td><td colspan="5">{gens}</td></tr>'
else:
return f'<tr><td></td><td colspan="5">{gens}</td></tr>'

@lazy_attribute
def stored_representations(self):
if self.live():
if self.solvable:
return self.representation_line("PC")
return self.representation_line("PC",0)
return "data not computed"
#raise NotImplementedError

def sort_key(typ):
if typ == self.element_repr_type:
return -1
return ["Lie", "PC", "Perm", "GLZ", "GLFp", "GLFq", "GLZq", "GLZN"].index(typ)
return "\n".join(self.representation_line(rep_type) for rep_type in sorted(self.representations, key=sort_key))
return ["Lie", "PC", "Perm","GLZ", "GLFp", "GLFq", "GLZq", "GLZN"].index(typ)
output_strg = ""
flag = 0 # used when multiple matrix group represenations
for rep_type in sorted(self.representations, key=sort_key):
if rep_type in ["Lie","PC","Perm"]:
inc_matrix = 0
elif flag == 0:
inc_matrix = 0
flag =1
else:
inc_matrix = 1
output_strg = output_strg + "\n" + self.representation_line(rep_type,inc_matrix)
return output_strg

def is_null(self):
return self._data is None
Expand All @@ -1892,7 +1908,7 @@ def show_aut_group(self):
try:
if self.aut_group is None:
if self.aut_order is None:
return r"$\textrm{not computed}$"
return r"not computed"
else:
return f"Group of order {pos_int_and_factor(self.aut_order)}"
else:
Expand Down Expand Up @@ -2355,11 +2371,14 @@ def show_special_labels(self):
return ", ".join(specials)

def _lookup(self, label, data, Wtype):
if not label:
return None
for rec in data:
if rec["label"] == label:
return Wtype(label, rec)
elif rec.get("short_label") == label:
return Wtype(rec["label"], rec)
if rec:
if rec["label"] == label:
return Wtype(label, rec)
elif 'short_label' in rec and rec.get("short_label") == label:
return Wtype(rec["label"], rec)
# It's possible that the label refers to a small group that is not in the database
# but that we can create dynamically
return Wtype(label)
Expand All @@ -2386,6 +2405,16 @@ def _full(self):
def sub(self):
S = self._lookup(self.subgroup, self._full, WebAbstractGroup)
# We set various properties from S for create_boolean_subgroup_string
if not S:
order = self.subgroup_order
#newgroup.order = order
#newgroup.pgroup = len(ZZ(order).abs().factor())==1
newgroup = WebAbstractGroup('nolabel',
data={'order': order, 'G': None, 'abelian': self.abelian,
# What if aut_label is set?
'aut_group': self.aut_label, 'aut_order': None,
'pgroup':len(ZZ(order).abs().factor())==1})
return newgroup
for prop in [
"pgroup",
"is_elementary",
Expand Down
2 changes: 2 additions & 0 deletions lmfdb/number_fields/templates/nf-show-field.html
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ <h2>{{ KNOWL('nf.sibling', title='Sibling') }} fields</h2>
{% endfor %}
{% endif %}
{% endfor %}
<tr><td>
{{KNOWL('nf.minimal_sibling', title='Minimal sibling')}}: <td>{{ nf.minimal_sibling() | safe }}</tr>
</table>
</div>
{% endif %}
Expand Down
7 changes: 7 additions & 0 deletions lmfdb/number_fields/web_number_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,13 @@ def field_pretty(self):
def knowl(self):
return nf_display_knowl(self.get_label(), self.field_pretty())

def minimal_sibling(self):
if 'is_minimal_sibling' in self._data:
if self._data['is_minimal_sibling']:
return 'This field is its own minimal sibling'
return formatfield(self._data['minimal_sibling'], show_poly=True)
return na_text()

# Is the polynomial polredabs'ed
def is_reduced(self):
if not self.haskey('reduced'):
Expand Down
Loading