Skip to content

convergence

quiverlab.specseq.convergence

ConvergenceReport + the standing self-certificate.

The load-bearing arbiter of the whole engine: on EVERY :class:SpectralSequence construction, certify_convergence asserts

sum_{p+q=n} dim E_inf^{p,q}  ==  dim H_n(Tot)   for every total degree n

-- a rank identity that must hold by construction (a bounded filtration of a bounded complex converges strongly, Weibel 5.5.1 / metaplan section-2 SS brief). A mismatch can only be a page / filtration bookkeeping bug, and it raises :class:~quiverlab.errors.QuiverlabError loudly -- never a silent wrong page. No floats (src/ AST gate).

ConvergenceReport dataclass

ConvergenceReport(e_infinity_page: int, degenerates_at: int | None, abutment: dict)

The convergence data attached to every :class:SpectralSequence.

collapse

collapse()

The sequence collapses early (degenerates at E_1 or E_2).

Source code in src/quiverlab/specseq/convergence.py
26
27
28
def collapse(self):
    """The sequence collapses early (degenerates at ``E_1`` or ``E_2``)."""
    return self.degenerates_at in (1, 2)

prose

prose()

A human sentence for the worked-steps report.

Source code in src/quiverlab/specseq/convergence.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
def prose(self):
    """A human sentence for the worked-steps report."""
    degs = sorted(self.abutment)
    abut = ", ".join(f"H_{n}={self.abutment[n]}" for n in degs)
    if self.degenerates_at is None:
        deg = (f"it stabilizes only at the E_{self.e_infinity_page} page "
               "(the bounded-filtration bound)")
    elif self.degenerates_at == 1:
        deg = "it degenerates at E_1 (E_1 = E_inf; the filtration is trivial)"
    else:
        deg = (f"it degenerates at E_{self.degenerates_at} "
               "(all higher differentials vanish)")
    return (f"The spectral sequence converges to the homology of the total "
            f"complex ({abut}); {deg}.")

certify_convergence

certify_convergence(ss)

Run the standing self-certificate and return the :class:ConvergenceReport.

e_infinity_page = max(width, height) + 1 (E_r stabilizes by the p-extent, so this bound is generous). Asserts the E_inf totals equal the total-complex homology per degree; raises loudly on any mismatch. degenerates_at is the least r >= 1 whose per-cell page dims already equal E_inf's (page dims are monotone non-increasing per cell, so this is exactly the degeneration page).

Source code in src/quiverlab/specseq/convergence.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def certify_convergence(ss):
    """Run the standing self-certificate and return the :class:`ConvergenceReport`.

    ``e_infinity_page = max(width, height) + 1`` (E_r stabilizes by the p-extent, so
    this bound is generous). Asserts the ``E_inf`` totals equal the total-complex
    homology per degree; raises loudly on any mismatch. ``degenerates_at`` is the
    least ``r >= 1`` whose per-cell page dims already equal ``E_inf``'s (page dims
    are monotone non-increasing per cell, so this is exactly the degeneration
    page)."""
    e_inf = max(ss.width, ss.height) + 1
    abutment = dict(ss.total_homology_dims)
    einf_page = ss.page(e_inf)
    totals = {}
    for (p, q) in einf_page.spots:
        totals[p + q] = totals.get(p + q, 0) + einf_page.dim(p, q)
    ns = set(abutment) | set(totals)
    for n in ns:
        if totals.get(n, 0) != abutment.get(n, 0):
            raise QuiverlabError(
                "spectral sequence does not converge to its abutment: at total "
                f"degree {n} the E_inf totals sum to {totals.get(n, 0)} but "
                f"H_{n}(Tot) = {abutment.get(n, 0)} -- a page/filtration "
                "bookkeeping bug",
                hint="the standing self-certificate (E_inf totals == total "
                     "homology) is a rank identity that must hold by construction")
    einf_dims = _page_dims(ss, einf_page)
    degenerates_at = None
    for r in range(1, e_inf + 1):
        if _page_dims(ss, ss.page(r)) == einf_dims:
            degenerates_at = r
            break
    return ConvergenceReport(e_inf, degenerates_at, abutment)