Skip to content

Add Gauss-sum portraits to Dirichlet character pages - #7172

Open
roed-math wants to merge 4 commits into
LMFDB:mainfrom
roed-math:ai/t34-char-portraits
Open

Add Gauss-sum portraits to Dirichlet character pages#7172
roed-math wants to merge 4 commits into
LMFDB:mainfrom
roed-math:ai/t34-char-portraits

Conversation

@roed-math

Copy link
Copy Markdown
Contributor

Closes #3996.

Adds a "portrait" visualization to each individual Dirichlet character homepage, in the properties box alongside the other basic data, following the design proposed in the issue (Alex Best's demo).

For a character chi of modulus N, and every residue a, the partial Gauss sums S_a(k) = sum_{n<=k} chi(n) e(a n / N) are drawn as rainbow radial segments (early terms darkened), the complete Gauss sums tau_a(chi) as dots, and a grey circle of radius sqrt(N). The coprime dots lie on the circle iff chi is primitive, and rotational/reflective symmetry reveals the order and reality of the character.

The plot is a new self-contained module lmfdb/characters/portraits.py that renders all segments as a single matplotlib LineCollection and embeds the result via encode_plot (the elliptic-curve pattern). The only hook into existing code is a one-line call in render_Dirichletwebpage.

The picture has N * phi(N) segments, and that count (not the modulus) is what its cost scales with, so that is what is capped: portraits are drawn when N * phi(N) <= 25000, which keeps N = 300 at about 0.1s while dropping prime-like cases such as N = 293, whose 85556 segments take some eight times longer. The check happens before any character is constructed or any array allocated, and is exposed as portrait_complexity / portrait_is_enabled so the policy can be tested without rendering. Completed portraits are kept in a bounded lru_cache(maxsize=64), so a repeat visit costs microseconds; a modulus with no portrait is a quiet no-op that never reaches the cache, and any failure while building one is logged and swallowed, since the picture must never break the page.

A new /Character/Dirichlet/Pictures page and a "Picture description" entry in the Learn more box explain what the picture shows, as for number fields and Galois groups, and the image carries a stable class="dirichlet-character-portrait" plus alt text. That page renders the knowl portrait.character.dirichlet, which still needs to be created in the knowl database (draft text is in the old PR thread).

Tested: the complete Gauss sums agree with pari's znchargauss to machine precision for primitive, imprimitive, real, non-real and composite-modulus characters; |tau_a| = sqrt(N) on units and tau_a = 0 off them for a primitive character; the N = 1 special case; the workload cutoff on the non-monotone 293/300 pair; the cache; and the portrait's own CSS class on the character page.


Ported from roed-math#38, where the full write-up and comment history live.

🤖 Generated with Claude Code

roed314 and others added 3 commits July 19, 2026 13:41
New self-contained module lmfdb/characters/portraits.py draws, for each
residue a mod N, the partial Gauss sums S_a(k) as rainbow radial segments
(early terms darkened), the complete Gauss sums as dots, and a circle of
radius sqrt(N), following Alex Best's demo from the issue; rendered as a
single matplotlib LineCollection and embedded via encode_plot in the
properties box (the elliptic curve pattern). Computed on the fly for
modulus up to 300, skipped above. Hook is a single 4-line call in
render_Dirichletwebpage; any portrait failure is logged and swallowed.

Verified: complete sums match pari znchargauss to 1e-13 for ~18
odd/even/primitive/imprimitive/trivial characters, |tau_a|=sqrt(N) for
primitive chi at coprime a, 27.8 matches the issue's demo image; pages
checked via flask test client for N=1..300 and skip for N>300;
DirichletCharactersTest (17 tests incl. new test_portrait) green;
pyflakes clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Portraits were guarded by a modulus cutoff, but their cost scales with the
number of segments drawn, N*phi(N), which is far from monotone in N: the
prime 293 draws 85556 of them and takes some eight times as long as the
larger 300, whose phi is only 80, and a reviewer measured seconds of latency
on the 293.17 page. The cutoff is now on the segment count (25000, which
keeps 300 and drops 293), consulted before any character is constructed or
any array allocated, and exposed as portrait_complexity/portrait_is_enabled
so the policy can be tested without rendering. Completed portraits are kept
in a bounded lru_cache(maxsize=64), so repeat visits and crawlers cost
microseconds; moduli with no portrait are rejected before the cache, and a
failure is not cached.

Adds the accessibility and explanatory pieces asked for in review: a stable
dirichlet-character-portrait class and alt text on the image, a
/Character/Dirichlet/Pictures page rendering the knowl
portrait.character.dirichlet, and a "Picture description" entry in the Learn
more box, following the number field and Galois group pattern. The knowl
itself still has to be created in the knowl database.

Also corrects the module docstring, which said every k = 1, ..., N-1 was
drawn: only the k coprime to N are, since chi(n) = 0 off the units makes the
other stages repeat the segment before them.

Tests: complete sums against pari znchargauss for primitive, imprimitive,
non-real and composite-modulus characters; |tau_a| = sqrt(N) on units and 0
off them for a primitive character; the N = 1 point data; the 293/300
workload pair; the cache; and the portrait's own class on the character
page, rather than the presence of any base64 image. Rendered portraits are
byte-identical to before. lmfdb/characters/test_characters.py green (32),
pyflakes/pylint/ruff clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
LMFDB uses American spelling, so "maths" becomes "math".  The "grey" of
the module docstring is left as it is, matching the color="grey" literal
it describes and the rest of the plotting code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Visualisations of Dirichlet characters

2 participants