Skip to content

GL_q(2|1)

supergroup_gl21

supergroup_gl21.py

Computer-assisted symbolic verification of the graded (Z_2-graded) Yang–Baxter equation for the quantum supergroup GL_q(2|1) introduced by Çelik & Çelik (Reports on Mathematical Physics, 88 (2021), 259).

Mathematical background

GL_q(2|1) is defined on the super vector space V = C^{2|1}. Its basis vectors e_1, e_2, e_3 have parities (Z_2-degrees):

p(e_1) = 0,  p(e_2) = 0,  p(e_3) = 1   ->   parity = [0, 0, 1]

The first two vectors are even; the third is odd.

The R-matrix given on page 261 of the paper acts on V ⊗ V (dimension 9) and has size 9×9. The basis order (with internal zero-based indexing) is:

e_1⊗e_1, e_1⊗e_2, e_1⊗e_3,
e_2⊗e_1, e_2⊗e_2, e_2⊗e_3,
e_3⊗e_1, e_3⊗e_2, e_3⊗e_3

The R-matrix satisfies the graded Yang–Baxter equation:

R12 R13 R23 = R23 R13 R12          (on V ⊗ V ⊗ V, 27×27)

where, following the paper's definition,

R12 = R ⊗ I_3
R23 = I_3 ⊗ R
R13 = (P ⊗ I_3) R23 (P ⊗ I_3)

and P is the super-permutation matrix:

P(e_i ⊗ e_j) = (-1)^{p(i) p(j)} e_j ⊗ e_i.

Since parity = [0, 0, 1], P differs from the ordinary swap matrix only in the odd-odd component, where P^{33}_{33} = -1.

This module turns a lengthy, error-prone manual graded Yang–Baxter calculation into a symbolic verification procedure that can be modelled, tested and reproduced in Python/SymPy.

super_parity_gl21

super_parity_gl21() -> list[int]

Return the Z_2 parities of the GL_q(2|1) basis vectors.

e_1 and e_2 are even (0); e_3 is odd (1). Thus [0, 0, 1].

Source code in quantum_group/supergroup_gl21.py
61
62
63
64
65
66
def super_parity_gl21() -> list[int]:
    """Return the Z_2 parities of the GL_q(2|1) basis vectors.

    e_1 and e_2 are even (0); e_3 is odd (1). Thus [0, 0, 1].
    """
    return [0, 0, 1]

basis_pairs_gl21

basis_pairs_gl21() -> list[tuple[int, int]]

Return (i, j) pairs of the V ⊗ V basis with zero-based indices.

Row-major order: (0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2). Index k = 3*i + j corresponds to e_{i+1} ⊗ e_{j+1} in the paper.

Source code in quantum_group/supergroup_gl21.py
69
70
71
72
73
74
75
76
def basis_pairs_gl21() -> list[tuple[int, int]]:
    """Return (i, j) pairs of the V ⊗ V basis with **zero-based indices**.

    Row-major order:
    (0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2).
    Index k = 3*i + j corresponds to e_{i+1} ⊗ e_{j+1} in the paper.
    """
    return [(i, j) for i in range(3) for j in range(3)]

R_matrix_GLq21

R_matrix_GLq21(q_sym: Expr = default_q) -> sp.Matrix

Return the GL_q(2|1) R-matrix (9×9) from page 261 of the paper.

The basis order matches basis_pairs_gl21().

Source code in quantum_group/supergroup_gl21.py
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
def R_matrix_GLq21(q_sym: sp.Expr = default_q) -> sp.Matrix:
    """Return the GL_q(2|1) R-matrix (9×9) from page 261 of the paper.

    The basis order matches ``basis_pairs_gl21()``.
    """
    q = sp.sympify(q_sym)
    return sp.Matrix([
        [1, 0,      0,  0,        0, 0,      0,        0,      0],
        [0, q**2,   0,  1 - q**2, 0, 0,      0,        0,      0],
        [0, 0,      q,  0,        0, 0,      1 - q**2, 0,      0],
        [0, 0,      0,  1,        0, 0,      0,        0,      0],
        [0, 0,      0,  0,        1, 0,      0,        0,      0],
        [0, 0,      0,  0,        0, q,      0,        1 - q**2, 0],
        [0, 0,      0,  0,        0, 0,      q,        0,      0],
        [0, 0,      0,  0,        0, 0,      0,        q,      0],
        [0, 0,      0,  0,        0, 0,      0,        0,      q**2],
    ])

super_permutation_matrix

super_permutation_matrix(parity: list[int]) -> sp.Matrix

Return the graded swap (super-permutation) matrix on V ⊗ V.

P(e_i ⊗ e_j) = (-1)^{p(i) p(j)} e_j ⊗ e_i.

For d = len(parity), the result has shape (d², d²). With parity = [0, 0, 1], it differs from the ordinary swap only at P[8, 8] = -1 (the e_3⊗e_3 component).

Source code in quantum_group/supergroup_gl21.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
def super_permutation_matrix(parity: list[int]) -> sp.Matrix:
    """Return the graded swap (super-permutation) matrix on V ⊗ V.

        P(e_i ⊗ e_j) = (-1)^{p(i) p(j)} e_j ⊗ e_i.

    For d = len(parity), the result has shape (d², d²). With
    parity = [0, 0, 1], it differs from the ordinary swap only at
    P[8, 8] = -1 (the e_3⊗e_3 component).
    """
    if not parity or any(p not in (0, 1) for p in parity):
        raise ValueError("parity must be a nonempty list of zeros and ones.")
    d = len(parity)
    P = sp.zeros(d * d, d * d)
    for i in range(d):
        for j in range(d):
            sign = -1 if (parity[i] and parity[j]) else 1
            # Input: e_i ⊗ e_j  (column index i*d + j)
            # Output: e_j ⊗ e_i  (row index j*d + i)
            P[j * d + i, i * d + j] = sign
    return P

R12_GLq21

R12_GLq21(q_sym: Expr = default_q) -> sp.Matrix

R12 = R ⊗ I_3 (27×27).

Source code in quantum_group/supergroup_gl21.py
140
141
142
def R12_GLq21(q_sym: sp.Expr = default_q) -> sp.Matrix:
    """R12 = R ⊗ I_3  (27×27)."""
    return kron_list([R_matrix_GLq21(q_sym), sp.eye(3)])

R23_GLq21

R23_GLq21(q_sym: Expr = default_q) -> sp.Matrix

R23 = I_3 ⊗ R (27×27).

Source code in quantum_group/supergroup_gl21.py
145
146
147
def R23_GLq21(q_sym: sp.Expr = default_q) -> sp.Matrix:
    """R23 = I_3 ⊗ R  (27×27)."""
    return kron_list([sp.eye(3), R_matrix_GLq21(q_sym)])

R13_GLq21

R13_GLq21(q_sym: Expr = default_q) -> sp.Matrix

R13 = (P ⊗ I_3) R23 (P ⊗ I_3) (27×27).

This follows the paper's definition; P is the super-permutation matrix.

Source code in quantum_group/supergroup_gl21.py
150
151
152
153
154
155
156
157
158
def R13_GLq21(q_sym: sp.Expr = default_q) -> sp.Matrix:
    """R13 = (P ⊗ I_3) R23 (P ⊗ I_3)  (27×27).

    This follows the paper's definition; P is the super-permutation matrix.
    """
    P = super_permutation_matrix(super_parity_gl21())
    PI = kron_list([P, sp.eye(3)])
    R23 = R23_GLq21(q_sym)
    return PI * R23 * PI

graded_yang_baxter_residual_GLq21

graded_yang_baxter_residual_GLq21(q_sym: Expr = default_q) -> sp.Matrix

Return Y(q) = R12 R13 R23 - R23 R13 R12 (27×27 residual matrix).

Source code in quantum_group/supergroup_gl21.py
169
170
171
172
173
174
def graded_yang_baxter_residual_GLq21(q_sym: sp.Expr = default_q) -> sp.Matrix:
    """Return Y(q) = R12 R13 R23 - R23 R13 R12 (27×27 residual matrix)."""
    R12 = R12_GLq21(q_sym)
    R13 = R13_GLq21(q_sym)
    R23 = R23_GLq21(q_sym)
    return R12 * R13 * R23 - R23 * R13 * R12

graded_yang_baxter_holds_GLq21

graded_yang_baxter_holds_GLq21(q_sym: Expr = default_q) -> bool

Check the graded Yang–Baxter equation entry by entry.

Source code in quantum_group/supergroup_gl21.py
177
178
179
def graded_yang_baxter_holds_GLq21(q_sym: sp.Expr = default_q) -> bool:
    """Check the graded Yang–Baxter equation entry by entry."""
    return is_zero_matrix(graded_yang_baxter_residual_GLq21(q_sym))

summarize_GLq21_ybe

summarize_GLq21_ybe(q_sym: Expr = default_q) -> dict

Return a summary of the GL_q(2|1) graded YBE verification.

Source code in quantum_group/supergroup_gl21.py
182
183
184
185
186
187
188
189
190
191
192
def summarize_GLq21_ybe(q_sym: sp.Expr = default_q) -> dict:
    """Return a summary of the GL_q(2|1) graded YBE verification."""
    R = R_matrix_GLq21(q_sym)
    nonzero = sum(1 for x in R if sp.simplify(x) != 0)
    return {
        "dimension": 3,
        "R_shape": (9, 9),
        "triple_tensor_shape": (27, 27),
        "nonzero_entries_R": nonzero,
        "residual_is_zero": graded_yang_baxter_holds_GLq21(q_sym),
    }

embed_R_in_tensor_power

embed_R_in_tensor_power(R: Matrix, positions: tuple[int, int], tensor_power: int, parity: list[int]) -> sp.Matrix

Embed the R-matrix into factors (i, j) of V^{⊗n}.

R must be an even d²×d² operator on V ⊗ V (d = len(parity)). Odd operators require additional prefix signs and are not supported. positions = (i, j) uses zero-based indices with i < j. The output is the d^n × d^n operator R_{ij}.

For adjacent factors (j = i+1), use a simple Kronecker embedding: I^{⊗i} ⊗ R ⊗ I^{⊗(n-i-2)}. For nonadjacent factors (j > i+1), move the second index from i+1 to j using successive graded adjacent swaps (super-permutations): R_{i,j} = S_{j-1} ... S_{i+1} · R_{i,i+1} · S_{i+1} ... S_{j-1}. This conjugation carries the graded signs correctly.

Source code in quantum_group/supergroup_gl21.py
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
def embed_R_in_tensor_power(
    R: sp.Matrix,
    positions: tuple[int, int],
    tensor_power: int,
    parity: list[int],
) -> sp.Matrix:
    """Embed the R-matrix into factors (i, j) of V^{⊗n}.

    R must be an even d²×d² operator on V ⊗ V (d = len(parity)).
    Odd operators require additional prefix signs and are not supported.
    ``positions = (i, j)`` uses zero-based indices with i < j. The output
    is the d^n × d^n operator R_{ij}.

    For adjacent factors (j = i+1), use a simple Kronecker embedding:
        I^{⊗i} ⊗ R ⊗ I^{⊗(n-i-2)}.
    For nonadjacent factors (j > i+1), move the second index from i+1 to j
    using successive graded adjacent swaps (super-permutations):
        R_{i,j} = S_{j-1} ... S_{i+1} · R_{i,i+1} · S_{i+1} ... S_{j-1}.
    This conjugation carries the graded signs correctly.
    """
    tensor_power = as_int(tensor_power, "tensor_power", minimum=2)
    try:
        i, j = (as_int(x, "position") for x in positions)
    except ValueError:
        raise ValueError("positions must be a pair (i, j) of integers.") from None
    if not (0 <= i < j < tensor_power):
        raise ValueError("positions (i, j) must satisfy 0 <= i < j < tensor_power.")
    d = len(parity)
    n = tensor_power
    if not parity or any(p not in (0, 1) for p in parity):
        raise ValueError("parity must be a nonempty list of zeros and ones.")
    check_square(R, "R")
    if R.shape != (d*d, d*d):
        raise ValueError("R must have shape (d², d²), where d = len(parity).")
    degrees = [(parity[a] + parity[b]) % 2 for a in range(d) for b in range(d)]
    if any(degrees[a] != degrees[b] and sp.simplify(R[a, b]) != 0
           for a in range(d*d) for b in range(d*d)):
        raise ValueError("R must be an even operator (preserve total parity).")

    # Adjacent embedding R_{i, i+1}
    op = kron_list([_eye_pow(d, i), R, _eye_pow(d, n - i - 2)])

    # Move the second index from i+1 to j by conjugation.
    for k in range(i + 1, j):
        S_k = _adjacent_swap(parity, n, k)
        op = S_k * op * S_k
    return op

all_Rij_GLq21

all_Rij_GLq21(tensor_power: int, q_sym: Expr = default_q) -> dict[tuple[int, int], sp.Matrix]

Return all R_{ij} operators (i < j) on V^{⊗n}.

For example, all_Rij_GLq21(3) -> {(0,1), (0,2), (1,2)}; all_Rij_GLq21(4) -> six operators, each 81×81.

Source code in quantum_group/supergroup_gl21.py
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
def all_Rij_GLq21(
    tensor_power: int, q_sym: sp.Expr = default_q
) -> dict[tuple[int, int], sp.Matrix]:
    """Return all R_{ij} operators (i < j) on V^{⊗n}.

    For example, ``all_Rij_GLq21(3)`` -> {(0,1), (0,2), (1,2)};
    ``all_Rij_GLq21(4)`` -> six operators, each 81×81.
    """
    tensor_power = as_int(tensor_power, "tensor_power", minimum=2)
    parity = super_parity_gl21()
    R = R_matrix_GLq21(q_sym)
    return {
        (i, j): embed_R_in_tensor_power(R, (i, j), tensor_power, parity)
        for i, j in combinations(range(tensor_power), 2)
    }

braid_far_commutativity_residual_GLq21

braid_far_commutativity_residual_GLq21(q_sym: Expr = default_q) -> sp.Matrix

Far-commutativity residual on V^{⊗4}: R12 R34 - R34 R12.

Operators on disjoint factors should commute, so this 81×81 residual should be zero.

Source code in quantum_group/supergroup_gl21.py
275
276
277
278
279
280
281
282
283
284
285
286
def braid_far_commutativity_residual_GLq21(
    q_sym: sp.Expr = default_q,
) -> sp.Matrix:
    """Far-commutativity residual on V^{⊗4}: R12 R34 - R34 R12.

    Operators on disjoint factors should commute, so this 81×81 residual
    should be zero.
    """
    Rij = all_Rij_GLq21(4, q_sym)
    R12 = Rij[(0, 1)]
    R34 = Rij[(2, 3)]
    return R12 * R34 - R34 * R12

local_ybe_on_four_tensor_GLq21

local_ybe_on_four_tensor_GLq21(q_sym: Expr = default_q) -> dict[tuple[int, int, int], bool]

Check the YBE on every triple of factors in V^{⊗4}.

Triples (zero-based): (0,1,2), (0,1,3), (0,2,3), (1,2,3). For each, R_{ab} R_{ac} R_{bc} = R_{bc} R_{ac} R_{ab} return whether the equation holds.

Source code in quantum_group/supergroup_gl21.py
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
def local_ybe_on_four_tensor_GLq21(
    q_sym: sp.Expr = default_q,
) -> dict[tuple[int, int, int], bool]:
    """Check the YBE on every triple of factors in V^{⊗4}.

    Triples (zero-based): (0,1,2), (0,1,3), (0,2,3), (1,2,3). For each,
        R_{ab} R_{ac} R_{bc} = R_{bc} R_{ac} R_{ab}
    return whether the equation holds.
    """
    Rij = all_Rij_GLq21(4, q_sym)
    result: dict[tuple[int, int, int], bool] = {}
    for a, b, c in combinations(range(4), 3):
        Rab = Rij[(a, b)]
        Rac = Rij[(a, c)]
        Rbc = Rij[(b, c)]
        residual = Rab * Rac * Rbc - Rbc * Rac * Rab
        result[(a, b, c)] = is_zero_matrix(residual)
    return result