Skip to content

ag

quiverlab.strings.ag

The Avella-Alaminos-Geiss (AG) derived invariant of a gentle algebra (Plan 46 / C5).

For a gentle algebra A = kQ/I (I generated by length-2 monomials) AAG 2008 attach a MULTISET of pairs (n, m) in N x N, a DERIVED invariant: derived-equivalent gentle algebras share it (AAG 2008, Theorem A). It is honestly NOT complete -- completeness needs the graded Opper-Plamondon-Schroll geometric data, out of scope here; never claim it separates all derived-equivalence classes.

Threads (partition Q_1): - a permitted thread is a maximal NONZERO directed path (c_i c_{i+1} not in I); gentle => the permitted successor of each arrow is unique, so permitted threads partition Q_1. - a forbidden thread (antipath) is a maximal path of RELATIONS (c_i c_{i+1} in I), maximal; gentle => partition Q_1 too. A forbidden thread may be CYCLIC (self-injective case, e.g. ...abab...).

Algorithm (Asashiba's blossoming form of AAG, as in Nakaoka arXiv:1811.00775 Def 2.7-2.11 -- verified to reproduce that paper's Example 2.15 tabulated value {(3,2),(2,2),(0,3)} exactly): - Blossom (Q, I) to (Q^*, I^*) so every original vertex has in- and out-degree 2 (add source/sink blossom arrows), completing the permitted matching M_p and the complementary forbidden matching M_f (perfect matchings in-arrows -> out-arrows at each vertex). - Each source blossom seeds a maximal permitted path P_p = sigma_p wp_p tau_p. Following M_f backward from tau_p reaches a source blossom sigma_{Phi(p)}: this is the AAG permutation Phi. An orbit O of Phi has type (n, m) = (|O|, sum of forbidden-thread lengths in O). - A cyclic forbidden thread not touching a blossom contributes (0, length).

sum n = 2|Q_0| - |Q_1| (the number of permitted threads d_A) and sum m = |Q_1| (every arrow lies in exactly one forbidden thread). The (n, m) order is (permitted, forbidden), the AAG / Nakaoka convention. Float-free / exact.

AGInvariant dataclass

AGInvariant(pairs: tuple)

The AG invariant: a MULTISET of (n, m) pairs (sorted). n counts the permitted threads in an AAG-orbit, m the forbidden-thread arrows; a cyclic forbidden thread gives (0, m). A DERIVED invariant, honestly NOT complete.

ag_invariant

ag_invariant(A) -> AGInvariant

The Avella-Alaminos-Geiss derived invariant of the gentle algebra A. Loud on non-gentle input. DERIVED-INVARIANT, NOT COMPLETE (documented).

Source code in src/quiverlab/strings/ag.py
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
def ag_invariant(A) -> AGInvariant:
    """The Avella-Alaminos-Geiss derived invariant of the gentle algebra ``A``.
    Loud on non-gentle input. DERIVED-INVARIANT, NOT COMPLETE (documented)."""
    _require_gentle(A)
    Mp, Mf, orig = _blossom_matchings(A)
    source_blossoms = [a for a in Mp if _is_blossom_in(a)]

    # permitted path from each source blossom: follow M_p to a sink blossom
    ends = {}          # source blossom -> ending blossom-out tau
    for s in source_blossoms:
        cur = s
        while True:
            nxt = Mp[cur]
            if _is_blossom_out(nxt):
                ends[s] = nxt
                break
            cur = nxt

    # antipath ending at tau_p: follow M_f backward to a source blossom = Phi(p);
    # count the original arrows crossed (the forbidden-thread length delta_p)
    Mf_inv = {out: inp for inp, out in Mf.items()}
    Phi, anti_len = {}, {}
    for s in source_blossoms:
        cur = ends[s]
        length = 0
        while True:
            pred = Mf_inv[cur]
            if _is_blossom_in(pred):
                Phi[s] = pred
                break
            length += 1
            cur = pred
        anti_len[s] = length

    # orbits of Phi -> pairs (|orbit|, sum forbidden lengths)
    pairs = []
    seen = set()
    for s in source_blossoms:
        if s in seen:
            continue
        orbit = []
        cur = s
        while cur not in seen:
            seen.add(cur)
            orbit.append(cur)
            cur = Phi[cur]
        n = len(orbit)
        m = sum(anti_len[t] for t in orbit)
        pairs.append((n, m))

    # cyclic forbidden threads (anticycles) -> (0, length)
    seen2 = set()
    for a in orig:
        if a in seen2:
            continue
        cyc, cur, cyclic = [], a, True
        while True:
            cyc.append(cur)
            seen2.add(cur)
            nxt = Mf.get(cur)
            if nxt is None or _is_blossom(nxt):
                cyclic = False
                break
            if nxt == a:
                break
            if nxt in seen2:
                cyclic = False
                break
            cur = nxt
        if cyclic:
            pairs.append((0, len(cyc)))

    return AGInvariant(tuple(sorted(pairs)))

forbidden_threads

forbidden_threads(A)

Maximal paths of relations (forbidden threads / antipaths), as arrow-name tuples. Partition Q_1 (gentle); a cyclic forbidden thread is one tuple.

Source code in src/quiverlab/strings/ag.py
111
112
113
114
115
116
def forbidden_threads(A):
    """Maximal paths of relations (forbidden threads / antipaths), as arrow-name
    tuples. Partition ``Q_1`` (gentle); a cyclic forbidden thread is one tuple."""
    _require_gentle(A)
    _p, _pp, f_succ, f_pred = _succ_maps(A)
    return _threads(list(A.quiver.arrows), f_succ, f_pred)

permitted_threads

permitted_threads(A)

Maximal nonzero directed paths (permitted threads), as arrow-name tuples. Partition Q_1 (gentle).

Source code in src/quiverlab/strings/ag.py
103
104
105
106
107
108
def permitted_threads(A):
    """Maximal nonzero directed paths (permitted threads), as arrow-name tuples.
    Partition ``Q_1`` (gentle)."""
    _require_gentle(A)
    p_succ, p_pred, _f, _fp = _succ_maps(A)
    return _threads(list(A.quiver.arrows), p_succ, p_pred)