Text · original UTF-8 line locators · 3.4 KiB
def make_instance(family, size, seed):
import numpy as np
rng = np.random.default_rng(seed)
ham = family["hamiltonian"]; kind = ham["type"]
terms = []
if kind == "xxz_chain":
L = size; J, delta = ham["J"], ham["delta"]
for i in range(L - 1):
terms += [[J, {str(i): "X", str(i + 1): "X"}], [J, {str(i): "Y", str(i + 1): "Y"}], [J * delta, {str(i): "Z", str(i + 1): "Z"}]]
W = ham["field"]["W"]
for i in range(L): terms.append([float(rng.uniform(-W, W)), {str(i): "Z"}])
n_sites, geometry = L, {"type": "chain", "L": L}
elif kind == "tfim_chain_longitudinal":
L = size; J, hx = ham["J"], ham["hx"]; W = ham["hz"]["W"]
for i in range(L - 1): terms.append([-J, {str(i): "Z", str(i + 1): "Z"}])
for i in range(L): terms += [[-hx, {str(i): "X"}], [float(rng.uniform(-W, W)), {str(i): "Z"}]]
n_sites, geometry = L, {"type": "chain", "L": L}
elif kind == "heisenberg_2d":
Lx, Ly = size; J = ham["J"]; W = ham["field"]["W"]
idx = lambda x, y: x * Ly + y
for x in range(Lx):
for y in range(Ly):
for (x2, y2) in ((x + 1, y), (x, y + 1)):
if x2 < Lx and y2 < Ly:
for p in "XYZ": terms.append([J, {str(idx(x, y)): p, str(idx(x2, y2)): p}])
for s in range(Lx * Ly): terms.append([float(rng.uniform(-W, W)), {str(s): "Z"}])
n_sites, geometry = Lx * Ly, {"type": "square_open", "Lx": Lx, "Ly": Ly}
else:
raise ValueError(f"unknown hamiltonian type {kind}")
init = family["initial_state"]
if init["type"] == "random_product_z":
bits = (rng.uniform(0, 1, n_sites) >= init["p_up"]).astype(int).tolist()
elif init["type"] == "neel":
bits = [(x+y) % 2 for x in range(size[0]) for y in range(size[1])] if kind == "heisenberg_2d" else [i % 2 for i in range(n_sites)]
elif init["type"] == "domain_wall":
bits = [0] * (n_sites // 2) + [1] * (n_sites - n_sites // 2)
else:
raise ValueError("unknown initial state")
times = [float(x) for x in np.linspace(0.0, family["times"]["T"], family["times"]["num"])]
obs = [{"site": i, "pauli": family["observable"]["pauli"]} for i in range(n_sites)]
return {"family": family["family"], "n_sites": n_sites, "geometry": geometry, "terms": terms,
"initial_state": {"type": "product_z", "bits": bits}, "observables": obs, "times": times,
"conserved": family.get("conserved", [])}
def gate(trace, instance, tol_bounds, tol_cons, label):
import numpy as np
arr = np.asarray(trace, dtype=float)
nt, nobs = len(instance["times"]), len(instance["observables"])
if arr.shape != (nt, nobs): return f"{label}: output shape {arr.shape} differs from ({nt}, {nobs})"
if not np.all(np.isfinite(arr)): return f"{label}: output contains non-finite values"
if np.any(arr < -1 - tol_bounds) or np.any(arr > 1 + tol_bounds): return f"{label}: a Pauli expectation value lies outside [-1, 1]"
bits = np.array(instance["initial_state"]["bits"]); z0 = 1.0 - 2.0 * bits
if np.max(np.abs(arr[0] - z0)) > tol_cons: return f"{label}: the t = 0 row does not match the initial product state"
for c in instance.get("conserved", []):
if c == "total_z":
tot = arr.sum(axis=1)
if np.max(np.abs(tot - tot[0])) > tol_cons * nobs: return f"{label}: total magnetisation is not conserved (max drift {float(np.max(np.abs(tot - tot[0]))):.3g})"
return None