Skip to content

q-arithmetic and helpers

utils

utils.py

q-arithmetic helpers: q-integers, q-factorials and q-binomial coefficients.

Definitions

q-integer: [n]_q = (q^n - q^{-n}) / (q - q^{-1}) = q^{n-1} + q^{n-3} + ... + q^{-(n-1)}

q-factorial: [n]_q! = [n]_q [n-1]_q ... [1]_q, [0]_q! = 1

q-binomial: [n choose k]_q = [n]_q! / ([k]_q! [n-k]_q!)

These functions return SymPy expressions, so q can remain symbolic or be assigned a numerical value with subs() when needed.

q_integer

q_integer(n: int, q_sym: Expr = q) -> sp.Expr

Return the q-integer [n]_q.

Parameters:

Name Type Description Default
n int

May be nonnegative or negative; [-n]_q = -[n]_q.

required
q_sym sympy expression

The q parameter. Defaults to the module symbol q.

q

Returns:

Type Description
sympy expression.
Example

q_integer(3) q2 + 1 + q(-2)

Source code in quantum_group/utils.py
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def q_integer(n: int, q_sym: sp.Expr = q) -> sp.Expr:
    """Return the q-integer [n]_q.

    Parameters
    ----------
    n : int
        May be nonnegative or negative; [-n]_q = -[n]_q.
    q_sym : sympy expression
        The q parameter. Defaults to the module symbol `q`.

    Returns
    -------
    sympy expression.

    Example
    -------
    >>> q_integer(3)
    q**2 + 1 + q**(-2)
    """
    n = as_int(n, "n")
    if n == 0:
        return sp.Integer(0)
    q_sym = sp.sympify(q_sym)
    if q_sym == 0:
        raise ValueError("q must be nonzero.")
    sign = 1 if n > 0 else -1
    return sign * sum((q_sym**j for j in range(abs(n) - 1, -abs(n), -2)), sp.S.Zero)

q_factorial

q_factorial(n: int, q_sym: Expr = q) -> sp.Expr

Return the q-factorial [n]_q! = [n]_q [n-1]_q ... [1]_q.

Source code in quantum_group/utils.py
62
63
64
65
66
67
68
69
70
def q_factorial(n: int, q_sym: sp.Expr = q) -> sp.Expr:
    """Return the q-factorial [n]_q! = [n]_q [n-1]_q ... [1]_q."""
    n = as_int(n, "n")
    if n < 0:
        raise ValueError("The q-factorial is defined only for n >= 0.")
    result = sp.Integer(1)
    for k in range(1, n + 1):
        result *= q_integer(k, q_sym)
    return sp.simplify(result)

q_binomial

q_binomial(n: int, k: int, q_sym: Expr = q) -> sp.Expr

Return the q-binomial coefficient [n choose k]_q (zero unless 0 <= k <= n).

Source code in quantum_group/utils.py
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def q_binomial(n: int, k: int, q_sym: sp.Expr = q) -> sp.Expr:
    """Return the q-binomial coefficient [n choose k]_q (zero unless 0 <= k <= n)."""
    n = as_int(n, "n")
    k = as_int(k, "k")
    if k < 0 or k > n:
        return sp.Integer(0)
    q_sym = sp.sympify(q_sym)
    if q_sym == 0:
        raise ValueError("q must be nonzero.")
    # Laurent-polynomial recurrence avoids removable 0/0 at roots of unity.
    row = [sp.S.One]
    for m in range(1, n + 1):
        row = [sp.S.One] + [
            sp.expand(q_sym**(-j) * (row[j] if j < len(row) else 0)
                      + q_sym**(m-j) * row[j-1])
            for j in range(1, min(m, k) + 1)
        ]
    return sp.simplify(row[k])

classical_limit

classical_limit(expr: Expr, q_sym: Expr = q) -> sp.Expr

Compute the classical limit q -> 1 (using L'Hôpital's rule).

This is useful for symbolic checks of limits such as [n]_q -> n.

Source code in quantum_group/utils.py
93
94
95
96
97
98
def classical_limit(expr: sp.Expr, q_sym: sp.Expr = q) -> sp.Expr:
    """Compute the classical limit q -> 1 (using L'Hôpital's rule).

    This is useful for symbolic checks of limits such as [n]_q -> n.
    """
    return sp.limit(expr, q_sym, 1)

linalg

linalg.py

Small exact-matrix helpers shared by the verification modules.

  • kron / kron_list — Kronecker (tensor) products of SymPy matrices, using the standard basis order in which the left factor's index changes slowly and the right factor's index changes quickly.
  • is_zero_matrix — the zero-residual test used throughout the package.

These helpers were previously duplicated as private functions in several modules (hopf._kron, r_matrix._is_zero, supergroup_gl21._is_zero_matrix_symbolic). Those names remain as aliases for backward compatibility.

kron

kron(A: Matrix, B: Matrix) -> sp.Matrix

Return the Kronecker product A ⊗ B of two SymPy matrices.

kron(sp.eye(2), sp.Matrix([[0, 1], [1, 0]])).shape (4, 4)

Source code in quantum_group/linalg.py
25
26
27
28
29
30
31
32
33
34
35
36
37
def kron(A: sp.Matrix, B: sp.Matrix) -> sp.Matrix:
    """Return the Kronecker product A ⊗ B of two SymPy matrices.

    >>> kron(sp.eye(2), sp.Matrix([[0, 1], [1, 0]])).shape
    (4, 4)
    """
    m, n = A.shape
    p, r = B.shape
    out = sp.zeros(m * p, n * r)
    for i in range(m):
        for j in range(n):
            out[i * p:(i + 1) * p, j * r:(j + 1) * r] = A[i, j] * B
    return out

kron_list

kron_list(mats: Sequence[Matrix]) -> sp.Matrix

Return the left-to-right Kronecker product of the given matrices.

Source code in quantum_group/linalg.py
40
41
42
43
44
45
46
47
def kron_list(mats: Sequence[sp.Matrix]) -> sp.Matrix:
    """Return the left-to-right Kronecker product of the given matrices."""
    if not mats:
        raise ValueError("kron_list requires at least one matrix.")
    result = mats[0]
    for M in mats[1:]:
        result = kron(result, M)
    return result

is_zero_matrix

is_zero_matrix(M: Matrix) -> bool

Return whether every entry of M simplifies to zero.

True means each entry was reduced to the exact value 0 by sympy.simplify. False means at least one entry was not; for arbitrary SymPy expressions this is not by itself a proof that the entry is nonzero, because simplify is not a decision procedure. For the rational functions of q produced by this package, a nonzero result can be confirmed with sympy.cancel.

Source code in quantum_group/linalg.py
50
51
52
53
54
55
56
57
58
59
60
def is_zero_matrix(M: sp.Matrix) -> bool:
    """Return whether every entry of ``M`` simplifies to zero.

    ``True`` means each entry was reduced to the exact value 0 by
    ``sympy.simplify``. ``False`` means at least one entry was not; for
    arbitrary SymPy expressions this is not by itself a proof that the
    entry is nonzero, because ``simplify`` is not a decision procedure.
    For the rational functions of ``q`` produced by this package, a
    nonzero result can be confirmed with ``sympy.cancel``.
    """
    return all(sp.simplify(entry) == 0 for entry in M)