File size: 2,312 Bytes
eef9104
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
60
61
# *******************************************************************************
# Copyright (c) 2026 Ahmad Ali Parr and others.
#
# This program and the accompanying materials are made available under the
# terms of the CPSC Eclipse Strict Copyleft License, Version 1.0, which is
# available in LICENSES/CPSC-ESCL-1.0.txt, or, at your option, under the
# GNU Affero General Public License, Version 3 only, which is available in
# LICENSES/AGPL-3.0.txt. Both options are strict copyleft. There is no
# classpath exception and no permissive relicensing.
#
# SPDX-License-Identifier: LicenseRef-CPSC-ESCL-1.0 OR AGPL-3.0-only
# *******************************************************************************

"""Transverse-field Ising stand-in for the 1+1D lattice specification.

The draft quartic (g/4!) sum_i X_i^4 is identically (g/24) N I, because X^2 = I.
It is reported and then omitted from the matrix.
"""

from __future__ import annotations

import numpy as np


def x4_identity_shift(n: int, g: float) -> float:
    """Constant added by sum_i X_i^4 / 24. Does not change eigenvectors."""
    return (g / 24.0) * n


def transverse_ising(n: int, j: float = 1.0, lam: float = 0.5) -> np.ndarray:
    """Periodic H = -J sum Z_i Z_{i+1} + lam sum X_i, shape (2^n, 2^n)."""
    if n < 2 or n > 10:
        raise ValueError("n must be in 2..10 for the dense prototype")
    dim = 1 << n
    h = np.zeros((dim, dim), dtype=np.complex128)
    for i in range(n):
        # X_i on qubit i (little-endian bit i).
        for basis in range(dim):
            flipped = basis ^ (1 << i)
            h[flipped, basis] += lam
        # Z_i Z_{i+1}
        nxt = (i + 1) % n
        for basis in range(dim):
            zi = 1.0 if ((basis >> i) & 1) == 0 else -1.0
            zj = 1.0 if ((basis >> nxt) & 1) == 0 else -1.0
            h[basis, basis] += -j * zi * zj
    return h


def parity_of(basis: int, n: int) -> int:
    """Z-parity as the popcount mod 2. P_Z |b> = (-1)^{popcount} |b>."""
    return (basis.bit_count() if hasattr(basis, "bit_count") else bin(basis).count("1")) & 1


def magnetization_of(basis: int, n: int) -> int:
    """Sum of Z eigenvalues. |0> contributes +1, |1> contributes -1."""
    mag = 0
    for i in range(n):
        mag += 1 if ((basis >> i) & 1) == 0 else -1
    return mag