Skip to content

toupie

quiverlab.families.toupie

Toupie algebras (Plan 59 / R35).

Artenstein-Lanzilotta-Solotar (Algebras and Representation Theory 23 (2020) 421-456, arXiv:1803.10310): a TOUPIE algebra is a kQ/I whose quiver has a UNIQUE source, a UNIQUE sink, and every other vertex has in-degree 1 and out-degree 1 -- a vertex-disjoint bundle of a directed BRANCHES (parallel paths) from source to sink -- with I an admissible ideal (monomial branch-truncations and/or non-monomial cross-branch linear combinations of the full source->sink branch-paths). Same ordinary quiver as the canonical algebras.

This module ships
  • ToupieAlgebra(branches, relations=None, field=None, expected_dim=None) -- the presented kQ/I constructor, with a per-instance dimension certificate for the relation-free case (dim = 2 + sum(l_i - 1) + sum l_i(l_i+1)/2).
  • is_toupie(A) -- the graph-shape recognizer (CONNECTED + ACYCLIC + the degree pattern; both connectivity and acyclicity are load-bearing -- the path U oriented-cycle quiver passes the degree checks yet is neither).
  • toupie_branch_count(A) -- the number of branches a = out_deg(source) == in_deg(sink) (asserts the equality).
  • toupie_direct_arrow_count(A) -- the number of arrows DIRECTLY source->sink (length-1 branches). THIS is the sl_a a (ALS Thm 6.5), NOT the branch count.
  • toupie_sl_a_lower_bound(A) -- a^2 - 1 with a = the direct-arrow count, hard-gated to characteristic 0 (ALS Thm 6.5 hypothesis is k = C).
  • toupie_block(A) -- the toupie algebra-only compute kind.

The a-Kronecker Q_a (two vertices, a parallel arrows, no relations = the length-1 toupie ToupieAlgebra([1]*a)) has HH^* = [1, a^2-1, 0, ...] (hereditary, char-independent, Euler chi = 2 - a^2). The sl_a inclusion HH^1 ⊇ sl_a holds over char 0 with a = the number of direct source->sink arrows; equality on the a-Kronecker, a strict lower bound on a subdivided toupie.

ToupieAlgebra

ToupieAlgebra(branches, relations=None, field=None, expected_dim=None)

Build the presented toupie kQ/I from a list of branch lengths.

branches = [l_1, ..., l_a] (each l_i >= 1). relations (optional) = grammar-token strings over the branch-path tokens p0..p_{a-1} (monomial truncations and/or NON-MONOMIAL cross-branch linear combinations) or over the raw arrow names a{i}_{j}. expected_dim (optional) is checked loudly.

Relation-free: certifies dim == 2 + sum(l_i - 1) + sum l_i(l_i+1)/2. With relations: Quiver.algebra computes the dimension (raising if not finite), and expected_dim is checked when given. a-Kronecker = ToupieAlgebra([1]*a) (dim = 2 + a); a single branch of length n = kA_{n+1}.

Source code in src/quiverlab/families/toupie.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
def ToupieAlgebra(branches, relations=None, field=None, expected_dim=None):
    """Build the presented toupie ``kQ/I`` from a list of branch lengths.

    ``branches`` = ``[l_1, ..., l_a]`` (each ``l_i >= 1``). ``relations`` (optional) =
    grammar-token strings over the branch-path tokens ``p0..p_{a-1}`` (monomial
    truncations and/or NON-MONOMIAL cross-branch linear combinations) or over the raw
    arrow names ``a{i}_{j}``. ``expected_dim`` (optional) is checked loudly.

    Relation-free: certifies ``dim == 2 + sum(l_i - 1) + sum l_i(l_i+1)/2``. With
    relations: ``Quiver.algebra`` computes the dimension (raising if not finite), and
    ``expected_dim`` is checked when given. ``a``-Kronecker = ``ToupieAlgebra([1]*a)``
    (``dim = 2 + a``); a single branch of length ``n`` = ``kA_{n+1}``.
    """
    if field is None:
        field = QQ
    branches = list(branches)
    if len(branches) < 1:
        raise QuiverlabError(
            "ToupieAlgebra: need at least one branch",
            hint="ToupieAlgebra([1, 1]) is the 2-Kronecker; ToupieAlgebra([2]) is kA3")
    for i, l in enumerate(branches):
        if not isinstance(l, int) or isinstance(l, bool) or l < 1:
            raise QuiverlabError(
                f"ToupieAlgebra: branch {i} has length {l!r}; branch lengths must be "
                "integers >= 1",
                hint="a length-1 branch is a single source->sink arrow")

    Q, branch_paths = _build_quiver(branches)
    rel_strings = _expand_relations(relations, branch_paths)
    try:
        A = Q.algebra(relations=rel_strings, field=field)
    except NotFiniteDimensionalError as exc:
        raise QuiverlabError(
            f"ToupieAlgebra: the presentation kQ/I is not finite-dimensional ({exc})",
            hint="a genuine toupie is finite-dimensional; check the relations") from exc

    if not rel_strings:
        want = _free_dim(branches)
        if A.dim != want:
            raise QuiverlabError(
                f"ToupieAlgebra dimension certificate failed: relation-free dim should "
                f"be {want}, got {A.dim}",
                hint="please report this presentation -- the closed form "
                     "2 + sum(l_i - 1) + sum l_i(l_i+1)/2 was violated")
    if expected_dim is not None and A.dim != expected_dim:
        raise QuiverlabError(
            f"ToupieAlgebra dimension certificate failed: expected_dim={expected_dim}, "
            f"got dim {A.dim}",
            hint="the relations do not cut the algebra to the expected dimension")

    A._family_citations = _REFS
    return A

is_toupie

is_toupie(A) -> bool

The toupie graph-shape recognizer: the quiver is CONNECTED and ACYCLIC, has a UNIQUE source (in-deg 0), a UNIQUE sink (out-deg 0), and EVERY other vertex has in-deg 1 and out-deg 1. CONNECTED + ACYCLIC are load-bearing (the path U oriented-cycle quiver passes the degree checks but is neither). Refuses loudly on a presentation-less algebra.

Source code in src/quiverlab/families/toupie.py
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
def is_toupie(A) -> bool:
    """The toupie graph-shape recognizer: the quiver is CONNECTED and ACYCLIC, has a
    UNIQUE source (in-deg 0), a UNIQUE sink (out-deg 0), and EVERY other vertex has
    in-deg 1 and out-deg 1. CONNECTED + ACYCLIC are load-bearing (the path U
    oriented-cycle quiver passes the degree checks but is neither). Refuses loudly on a
    presentation-less algebra."""
    _require_quiver(A, "is_toupie")
    Q = A.quiver
    ep = _endpoints(Q)
    if ep is None:
        return False
    if not Q.is_connected():
        return False
    if not Q.is_acyclic():
        return False
    return True

toupie_block

toupie_block(A, hh_top=4)

The toupie algebra-only compute kind (Plan 59 Task 5).

A presentation-less algebra -> {"error": ..., "references": [...]} (never a 500). A non-toupie -> {"is_toupie": False, ...nulls..., "note": "not a toupie"}. A toupie -> the branch/direct-arrow counts, HH^0..HH^{hh_top} (engine="cs", auto_cs=True), and the sl_a line (dim sl_a = a^2 - 1, a = direct-arrow count; char0 flag + a note when the inclusion is not claimed).

Source code in src/quiverlab/families/toupie.py
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
def toupie_block(A, hh_top=4):
    """The ``toupie`` algebra-only compute kind (Plan 59 Task 5).

    A presentation-less algebra -> ``{"error": ..., "references": [...]}`` (never a 500).
    A non-toupie -> ``{"is_toupie": False, ...nulls..., "note": "not a toupie"}``. A
    toupie -> the branch/direct-arrow counts, ``HH^0..HH^{hh_top}`` (``engine="cs",
    auto_cs=True``), and the ``sl_a`` line (``dim sl_a = a^2 - 1``, ``a`` = direct-arrow
    count; ``char0`` flag + a note when the inclusion is not claimed).
    """
    refs = list(_REFS)
    try:
        it = is_toupie(A)
    except QuiverlabError as exc:
        return {"error": str(exc), "references": refs}
    if not it:
        return {"is_toupie": False, "branch_count": None, "direct_arrow_count": None,
                "hh": None, "hh_top": hh_top, "sl_a": None,
                "note": "not a toupie", "references": refs}

    bc = toupie_branch_count(A)
    dac = toupie_direct_arrow_count(A)
    hh = list(A.hochschild_cohomology(hh_top, engine="cs", auto_cs=True,
                                      verbose=False).dims)
    char0 = (A.domain.characteristic == 0)
    sla_dim = max(dac * dac - 1, 0)
    sl_a = {"a": dac, "dim": sla_dim, "char0": char0}
    note = None
    if not char0:
        note = ("sl_a ⊆ HH^1 (ALS Thm 6.5) is claimed only in characteristic 0; over "
                f"char {A.domain.characteristic} the abstract dim sl_a = {sla_dim} is "
                "reported without the inclusion")
    elif dac < 2:
        note = (f"a_direct = {dac} < 2: sl_a is trivial (dim {sla_dim}); the inclusion "
                "carries no content")
    return {"is_toupie": True, "branch_count": bc, "direct_arrow_count": dac,
            "hh": hh, "hh_top": hh_top, "sl_a": sl_a, "note": note, "references": refs}

toupie_branch_count

toupie_branch_count(A) -> int

The number of branches a = out_deg(source) == in_deg(sink) (asserts the equality). NOT the sl_a a -- see :func:toupie_direct_arrow_count.

Source code in src/quiverlab/families/toupie.py
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
def toupie_branch_count(A) -> int:
    """The number of branches ``a = out_deg(source) == in_deg(sink)`` (asserts the
    equality). NOT the ``sl_a`` ``a`` -- see :func:`toupie_direct_arrow_count`."""
    if not is_toupie(A):
        raise QuiverlabError(
            "toupie_branch_count: A is not a toupie",
            hint="check is_toupie(A) first")
    s, t, ind, outd = _endpoints(A.quiver)
    a_out, a_in = outd[s], ind[t]
    if a_out != a_in:
        raise QuiverlabError(
            f"toupie_branch_count: out_deg(source)={a_out} != in_deg(sink)={a_in}",
            hint="a toupie is a vertex-disjoint bundle of branches, so the two must "
                 "agree -- please report this quiver")
    return a_out

toupie_direct_arrow_count

toupie_direct_arrow_count(A) -> int

The sl_a a (ALS Thm 6.5): the number of arrows DIRECTLY source->sink (length-1 branches). a_direct <= a_branch, equal iff every branch has length 1 (the a-Kronecker).

Source code in src/quiverlab/families/toupie.py
233
234
235
236
237
238
239
240
241
242
243
def toupie_direct_arrow_count(A) -> int:
    """The ``sl_a`` ``a`` (ALS Thm 6.5): the number of arrows DIRECTLY source->sink
    (length-1 branches). ``a_direct <= a_branch``, equal iff every branch has length 1
    (the ``a``-Kronecker)."""
    if not is_toupie(A):
        raise QuiverlabError(
            "toupie_direct_arrow_count: A is not a toupie",
            hint="check is_toupie(A) first")
    s, t, _ind, _outd = _endpoints(A.quiver)
    return sum(1 for (src, tgt) in A.quiver.arrows.values()
               if src == s and tgt == t)

toupie_sl_a_lower_bound

toupie_sl_a_lower_bound(A) -> int

dim sl_a = max(a^2 - 1, 0) with a = the number of direct source->sink arrows (:func:toupie_direct_arrow_count). ALS Thm 6.5 gives HH^1(A) ⊇ sl_a over a field of characteristic 0 (k = C), so dim HH^1 >= a^2 - 1. The clamp is for the degenerate a = 0 (no direct arrows): sl_0 is empty, dim 0 -- never the nonsensical -1. HARD-GATED to char 0 (the theorem's hypothesis); refuses loudly off it.

Source code in src/quiverlab/families/toupie.py
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
def toupie_sl_a_lower_bound(A) -> int:
    """``dim sl_a = max(a^2 - 1, 0)`` with ``a`` = the number of direct source->sink
    arrows (:func:`toupie_direct_arrow_count`). ALS Thm 6.5 gives ``HH^1(A) ⊇ sl_a`` over
    a field of characteristic 0 (``k = C``), so ``dim HH^1 >= a^2 - 1``. The clamp is for
    the degenerate ``a = 0`` (no direct arrows): ``sl_0`` is empty, dim 0 -- never the
    nonsensical ``-1``. HARD-GATED to char 0 (the theorem's hypothesis); refuses loudly
    off it."""
    if A.domain.characteristic != 0:
        raise QuiverlabError(
            "toupie_sl_a_lower_bound: the sl_a inclusion (ALS Thm 6.5) requires "
            f"characteristic 0, got char {A.domain.characteristic}",
            hint="the theorem's hypothesis is k = C; over GF(p) the inclusion is not "
                 "claimed -- re-run over QQ")
    a = toupie_direct_arrow_count(A)
    return max(a * a - 1, 0)          # a=0 (no direct arrows): sl_0 is empty -> 0, not -1