"""Check of item 4 of 07-ilang-weight/06-inertia-of-pair against exact eigenvalues with a held source. Two Model D bodies and N_f = 1 field object on a ring of L places (set A of check_inertia.py), plus a held source g_s Phi_{x_s}. The exact contract (all places, no sector) is diagonalized near the bottom of the bound band, where the bound pair sits in the weight well opposite the source. Its levels are compared with (i) the first-order projected contract on the exact bound band: E_b(P_k) + , with the exact P-dependent sector eigenvectors v_P (no frozen relative profile); (ii) the frozen-profile description E_b(P) + W_nu(X) with W_nu of (6.11) (profile f_nu at P = 0); (iii) the same with the pair band rescaled to the inertia mu_1 + mu_2. Second part: the acceleration from rest (6.13) of the centre Y conjugate to P, computed exactly from the double commutator in the position-space band contract (order tau^2, first-order held weights), against -E''_line sum phi^2 W'_nu, and the acceleration of the midpoint against -(beta/2) sum phi^2 (W_1'/mu_1 + W_2'/mu_2). Only set A is used: item 4 is asked only where g_1 + g_2 is not small against max_i |g_i| (note (ii) of question.md). """ import json from pathlib import Path import numpy as np import scipy.sparse as sp import scipy.sparse.linalg as sla from check_inertia import (OUT, J, SG, bodies, bidx, ground, field_hop, pair_block, band_data, body_data, curvature) def full_contract(L, tf, b1, b2, cpl, gs, xs, par): """Exact contract on all places, restricted to the block (-1)^(x2-x1) s1 s2 of the band G.""" cc = 1.0 if cpl == "c" else 0.0 ix = lambda x1, s1, x2, s2, y: (((x1 * 2 + s1) * L + x2) * 2 + s2) * L + y pi0 = (-1) ** par * b1["sig"] * b2["sig"] states = [(x1, s1, x2, s2, y) for x1 in range(L) for s1 in range(2) for x2 in range(L) for s2 in range(2) for y in range(L) if (-1) ** ((x2 - x1) % L) * SG[s1] * SG[s2] == pi0] pos = {ix(*s): i for i, s in enumerate(states)} rows, cols, vals = [], [], [] def add(i, st, v): j = pos[ix(*st)] rows.append(j); cols.append(i); vals.append(v) for i, (x1, s1, x2, s2, y) in enumerate(states): add(i, (x1, s1, x2, s2, y), SG[s1] * b1["M"] * (1 + b1["gam"] * (y == x1)) + SG[s2] * b2["M"] * (1 + b2["gam"] * (y == x2)) + gs * (y == xs)) for dy in (1, -1): add(i, (x1, s1, x2, s2, (y + dy) % L), -tf) t1, t2 = 1 - s1, 1 - s2 # a motion term flips the branch (J has zero diagonal) for dx in (1, -1): # body hop x -> x+dx (bond between x and x+dx); J for right hops, J^T for left hops Jm = J[t1, s1] if dx == 1 else J[s1, t1] f = 1 + cc * b1["gam"] * 0.5 * ((y == x1) + (y == (x1 + dx) % L)) add(i, ((x1 + dx) % L, t1, x2, s2, y), 0.5 * b1["tau"] * Jm * f) Jm = J[t2, s2] if dx == 1 else J[s2, t2] f = 1 + cc * b2["gam"] * 0.5 * ((y == x2) + (y == (x2 + dx) % L)) add(i, (x1, s1, (x2 + dx) % L, t2, y), 0.5 * b2["tau"] * Jm * f) H = sp.csr_matrix((vals, (rows, cols)), shape=(len(states), len(states))) assert abs(H - H.T).max() < 1e-14 return H, states, pos, ix def sector_state(blk, mode, P): """Exact bound level and eigenvector of the sector contract H(P) (body 1 as representative).""" H0, B1, V2, U0, w0, gi, rs = blk G = U0[:, gi] w, v = np.linalg.eigh(H0 + V2 + np.exp(-1j * P) * B1 + np.exp(1j * P) * B1.T) c = np.where((np.abs(G.T @ v) ** 2).sum(0) > 0.5)[0] k = c[np.argmin(w[c])] if mode == "low" else c[np.argmax(w[c])] return w[k], v[:, k] def held_weights(L, tf, b1, b2, gs, xs=0): """First-order held weights: pair W(j,l) = g_s |Omega_jl(x_s)|^2 and single bodies W_i(x).""" hf = field_hop(L, tf) Om, Wpair = {}, np.zeros((L, L)) for j in range(L): for l in range(L): h = hf.copy(); h[j, j] += b1["sig"] * b1["gam"] * b1["M"]; h[l, l] += b2["sig"] * b2["gam"] * b2["M"] _, o = ground(h) Om[(j, l)] = o Wpair[j, l] = gs * o[xs] ** 2 W1, W2 = np.zeros(L), np.zeros(L) for x in range(L): h = hf.copy(); h[x, x] += b1["sig"] * b1["gam"] * b1["M"]; W1[x] = gs * ground(h)[1][xs] ** 2 h = hf.copy(); h[x, x] += b2["sig"] * b2["gam"] * b2["M"]; W2[x] = gs * ground(h)[1][xs] ** 2 return Om, Wpair, W1, W2 def run(L, tf, cpl, gs, nlev=4): b1, b2, par, mode = bodies("A", L, cpl) xs = 0 s1g, s2g = bidx(b1["sig"]), bidx(b2["sig"]) Om, Wpair, W1, W2 = held_weights(L, tf, b1, b2, gs, xs) # relative level f_nu (band contract, P = 0) and the pair weight W_nu(X), (6.11) bd = band_data(L, tf, b1, b2, cpl, par, mode) rsg = [((r + L // 2) % L) - L // 2 for r in bd["rs"]] # signed separations Wnu = np.array([sum(bd["f"][k] ** 2 * Wpair[(X - r // 2) % L, (X + r // 2) % L] for k, r in enumerate(rsg)) for X in range(L)]) # exact bound band: levels E_b(P_k) and P-dependent sector eigenvectors v_P blk = pair_block(L, tf, b1, b2, cpl, par) Ps = 2 * np.pi * np.arange(L) / L sts = [sector_state(blk, mode, P) for P in Ps] Eb = np.array([s[0] for s in sts]); Vb = np.array([s[1] for s in sts]).T # (i) projected contract: = (1/L) sum_x e^{i(P-P')x} _frame yof = np.tile(np.arange(L), Vb.shape[0] // L) Wp = np.zeros((L, L), complex) for x in range(L): sel = yof == (xs - x) % L rho = Vb[sel].conj().T @ Vb[sel] Wp += np.exp(1j * (Ps[None, :] - Ps[:, None]) * x) * rho Wp *= gs / L assert np.abs(Wp - Wp.conj().T).max() < 1e-12 Eproj = np.linalg.eigvalsh(np.diag(Eb) + Wp)[:nlev] # (ii), (iii) frozen-profile descriptions F = np.exp(1j * np.outer(np.arange(L), Ps)) / np.sqrt(L) # _, Epp, _, _ = curvature(bd["H0"] + bd["V2"], bd["B1"], bd["U0"][:, bd["gi"]], mode) d1, d2 = body_data(L, tf, b1, cpl), body_data(L, tf, b2, cpl) mu = d1["mu"] + d2["mu"] s_free = 1 / (mu * Epp) # rescales the band curvature to 1/(mu_1 + mu_2) model = lambda Eband: np.linalg.eigvalsh(F @ np.diag(Eband) @ F.conj().T + np.diag(Wnu))[:nlev] Em = model(Eb) Ealt = model(Eb[0] + s_free * (Eb - Eb[0])) # exact contract with the source, levels near the bottom of the bound band, filtered by G-weight H, states, pos, ix = full_contract(L, tf, b1, b2, cpl, gs, xs, par) target = Em[0] - 0.3 * (Em[1] - Em[0]) w, v = sla.eigsh(H, k=3 * nlev, sigma=target, which="LM") Gw = np.zeros(len(w)) for (j, l), o in Om.items(): if (l - j) % 2 != par: continue idx = [pos[ix(j, s1g, l, s2g, y)] for y in range(L)] Gw += np.abs(o @ v[idx, :]) ** 2 ex = np.sort(w[Gw > 0.5])[:nlev] X = L // 4 dWnu = (Wnu[X + 1] - Wnu[X - 1]) / 2 dW1, dW2 = (W1[X + 1] - W1[X - 1]) / 2, (W2[X + 1] - W2[X - 1]) / 2 return dict(L=L, coupling=cpl, g_s=gs, exact_levels=ex.tolist(), proj_levels=Eproj.tolist(), model_levels=Em.tolist(), alt_levels=Ealt.tolist(), exact_spacings=np.diff(ex).tolist(), proj_spacings=np.diff(Eproj).tolist(), model_spacings=np.diff(Em).tolist(), alt_spacings=np.diff(Ealt).tolist(), beta=float(bd["b"].sum()), s_free=float(s_free), max_abs_dev_proj=float(np.max(np.abs(ex - Eproj))), max_abs_dev_model=float(np.max(np.abs(ex - Em))), G_weights=np.sort(Gw)[::-1][:nlev].tolist(), dWnu_over_dW1_plus_dW2=float(dWnu / (dW1 + dW2)), Epp_times_mu_sum=float(Epp * mu), Pi_pair_over_weighted_single=float(Epp * dWnu * mu / (dW1 + dW2))) def accel_test(name, L, tf, cpl, gs, widths=(1.0, 1.5, 2.0), wscales=(1.0, 0.05)): """Acceleration from rest (6.13) in the position-space band contract with first-order held weights. wscale < 1 scales the couplings g_i inside the held weights only (band contract and f_nu unchanged), to isolate the first order in g_i of the weights, on which the last step of (6.13) rests.""" b1, b2, par, mode = bodies(name, L, cpl) bd = band_data(L, tf, b1, b2, cpl, par, mode) rs = list(bd["rs"]); nr = len(rs); idx = {r: k for k, r in enumerate(rs)} K1, K2, Ea, f = bd["K1"], bd["K2"], bd["Ea"], bd["f"] I = lambda j, r: (j % L) * nr + idx[r % L] N = L * nr H = np.zeros((N, N)) for j in range(L): for k, r in enumerate(rs): a = I(j, r) H[a, a] += Ea[k] + K1[k, k] + K2[k, k] H[I(j + 2, r - 2), a] += K1[idx[(r - 2) % L], k] # body 1 by +2 H[I(j - 2, r + 2), a] += K1[idx[(r + 2) % L], k] # body 1 by -2 H[I(j, r + 2), a] += K2[idx[(r + 2) % L], k] # body 2 by +2 H[I(j, r - 2), a] += K2[idx[(r - 2) % L], k] # body 2 by -2 assert np.abs(H - H.T).max() < 1e-15 H -= np.mean(np.diag(H)) * np.eye(N) # a constant does not enter the commutators; reduces round-off data = {} for ws in wscales: b1w, b2w = dict(b1, gam=b1["gam"] * ws), dict(b2, gam=b2["gam"] * ws) data[ws] = held_weights(L, tf, b1w, b2w, gs)[1:] # theta'(r): theta'(r+2) - theta'(r) = (k1 - k2)/(k1 + k2) at rho = r + 1, theta' odd (even class) k1, k2 = bd["k1"], bd["k2"] step = {(rs[j] + 1) % L: (k1[j] - k2[j]) / (k1[j] + k2[j]) for j in range(nr)} sgn = lambda r: ((r + L // 2) % L) - L // 2 th = {0: 0.0} for r in range(2, L // 2 + 1, 2): th[r] = th[r - 2] + step[(r - 1) % L] th[-r] = th[-r + 2] - step[(-r + 1) % L] x0 = L // 4 Xu = np.zeros(N); Y = np.zeros(N); Fr = np.zeros(N); Rs = np.zeros(N) for j in range(L): for k, r in enumerate(rs): rsn = sgn(r) Xc = j + rsn / 2 Xc = x0 + ((Xc - x0 + L / 2) % L) - L / 2 a = I(j, r) Xu[a], Y[a], Fr[a], Rs[a] = Xc, Xc + th[rsn], f[k], rsn rsg = [sgn(r) for r in rs] Xg = np.arange(x0 - L // 2 + 2, x0 + L // 2 - 1) beta, Eline = bd["b"].sum(), np.sum(bd["b"] / bd["m"]) d1, d2 = body_data(L, tf, b1, cpl), body_data(L, tf, b2, cpl) out = [] for ws, (Wpair, W1, W2) in data.items(): Wb = np.array([Wpair[j, (j + r) % L] for j in range(L) for r in rs]) Wnu = lambda X: sum(f[k] ** 2 * Wpair[int(X - rr // 2) % L, int(X + rr // 2) % L] for k, rr in enumerate(rsg)) dWnu = np.array([(Wnu(X + 1) - Wnu(X - 1)) / 2 for X in Xg]) dW1 = np.array([(W1[(X + 1) % L] - W1[(X - 1) % L]) / 2 for X in Xg]) dW2 = np.array([(W2[(X + 1) % L] - W2[(X - 1) % L]) / 2 for X in Xg]) for wd in widths: psi = np.exp(-(Y - x0) ** 2 / (4 * wd ** 2)) * Fr psi /= np.linalg.norm(psi) acc = lambda Ht, Z: -psi @ (Ht @ Ht @ (Z * psi) - 2 * Ht @ (Z * (Ht @ psi)) + Z * (Ht @ Ht @ psi)) Ht = H + np.diag(Wb) # a is linear in W exactly ([W,[W,Z]] = 0 for diagonal W, Z): a = a(g_s=0) + first order in g_s aY, a0, amid, am0 = acc(Ht, Y - x0), acc(H, Y - x0), acc(Ht, Xu - x0), acc(H, Xu - x0) # the g_s-independent part with the relative profile cut away from the relative cut r = L/2 psi_c = np.exp(-(Y - x0) ** 2 / (4 * wd ** 2)) * Fr * (np.abs(Rs) < L // 3) psi_c /= np.linalg.norm(psi_c) a0cut = -psi_c @ (H @ H @ ((Y - x0) * psi_c) - 2 * H @ ((Y - x0) * (H @ psi_c)) + (Y - x0) * (H @ H @ psi_c)) p2 = psi ** 2 predY = -Eline * np.sum(p2 * np.interp(Y, Xg, dWnu)) predmid = -0.5 * beta * np.sum(p2 * (np.interp(Xu, Xg, dW1) / d1["mu"] + np.interp(Xu, Xg, dW2) / d2["mu"])) pred1 = -np.sum(p2 * np.interp(Y, Xg, dW1)) / d1["mu"] # body 1 alone at the centre, leading form of (7.7) out.append(dict(set=name, L=L, coupling=cpl, weight_scale=ws, width=wd, beta=float(beta), a_Y=float(aY), a_Y_gs0=float(a0), a_Y_gs0_profile_cut=float(a0cut), pred_Y=float(predY), ratio_Y=float((aY - a0) / predY), a_mid=float(amid), a_mid_gs0=float(am0), pred_mid=float(predmid), ratio_mid=float((amid - am0) / predmid), aY_over_amid=float((aY - a0) / (amid - am0)), pred_single1=float(pred1), aY_over_single1=float((aY - a0) / pred1), pred_ratio=float(predY / predmid))) return out def weight_ratio_scaling(L, tf, cpl, gs, scales=(1.0, 0.3, 0.1, 0.03)): """dW_nu/(dW_1+dW_2) at X = L/4 with the relative profile f_nu of set A kept fixed and the couplings gamma_i scaled: tends to 1 as the couplings go to zero (first order in g_i).""" b1, b2, par, mode = bodies("A", L, cpl) bd = band_data(L, tf, b1, b2, cpl, par, mode) rsg = [((r + L // 2) % L) - L // 2 for r in bd["rs"]] hf, X, out = field_hop(L, tf), L // 4, [] for sc in scales: g1 = sc * b1["sig"] * b1["gam"] * b1["M"]; g2 = sc * b2["sig"] * b2["gam"] * b2["M"] def w(pots): h = hf.copy() for x, g in pots: h[x % L, x % L] += g return gs * ground(h)[1][0] ** 2 Wnu = lambda Y: sum(bd["f"][k] ** 2 * w([(Y - r // 2, g1), (Y + r // 2, g2)]) for k, r in enumerate(rsg)) dWnu = Wnu(X + 1) - Wnu(X - 1) dWs = w([(X + 1, g1)]) - w([(X - 1, g1)]) + w([(X + 1, g2)]) - w([(X - 1, g2)]) out.append(dict(scale=sc, ratio=float(dWnu / dWs))) return out if __name__ == "__main__": OUT.mkdir(exist_ok=True) res = [run(L, 1.0, cpl, -0.01) for L in (16, 20) for cpl in ("a", "c")] res.append(run(16, 1.0, "a", -0.005)) lines = [] for r in res: lines.append(f"L={r['L']} coupling={r['coupling']} g_s={r['g_s']} beta={r['beta']:.5f} " f"E''(mu1+mu2)={r['Epp_times_mu_sum']:.5f} s_free={r['s_free']:.5f}") lines.append(" exact spacings " + " ".join(f"{x:.6e}" for x in r["exact_spacings"])) lines.append(" projected spacings " + " ".join(f"{x:.6e}" for x in r["proj_spacings"])) lines.append(" frozen spacings " + " ".join(f"{x:.6e}" for x in r["model_spacings"])) lines.append(" alt spacings " + " ".join(f"{x:.6e}" for x in r["alt_spacings"])) lines.append(f" max |exact - projected| level = {r['max_abs_dev_proj']:.3e} max |exact - frozen| level = " f"{r['max_abs_dev_model']:.3e} G-weights {np.round(r['G_weights'], 4).tolist()}") lines.append(f" dW_nu/(dW_1+dW_2) = {r['dWnu_over_dW1_plus_dW2']:.5f} " f"Pi_pair / inertia-weighted Pi_i = {r['Pi_pair_over_weighted_single']:.5f}") sc = weight_ratio_scaling(16, 1.0, "a", -0.01) lines.append("dW_nu/(dW_1+dW_2) at X=L/4, L=16, f_nu of set A fixed, couplings scaled: " + " ".join(f"scale={x['scale']}: {x['ratio']:.5f}" for x in sc)) acc = [] for cpl in ("a", "c"): acc += accel_test("A", 24, 1.0, cpl, -0.01) lines.append("acceleration from rest (6.13), position-space band contract, L=24, g_s=-0.01, envelope centre L/4; " "ratios use the first-order part a - a(g_s=0):") lines += [f" set={x['set']} coupling={x['coupling']} weight_scale={x['weight_scale']} w={x['width']} beta={x['beta']:.4f}: a_Y={x['a_Y']:.6e} " f"(g_s=0: {x['a_Y_gs0']:.1e}, profile cut at |r|