Path: blob/main/hybrid-workflows/auxiliary_files/labs_utils.py
1133 views
import numpy as np12def calculate_energy(sequence):3"""4Calculates the Energy (E) of a LABS sequence.5E is the sum of squares of aperiodic autocorrelations for all non-zero lags.67Args:8sequence (np.array): A binary sequence of 1s and -1s.910Returns:11float: The energy value (lower is better).12"""13N = len(sequence)1415# Faster vectorized approach using np.correlate16# mode='full' gives correlations from lag -(N-1) to (N-1)17corr = np.correlate(sequence, sequence, mode='full')1819# The center index (lag 0) is at N-1.20# We want lags 1 to N-1, which are indices N to 2N-2.21sidelobes = corr[N:]2223return np.sum(sidelobes**2)2425def Flip(s, i):26"""Flips the bit at index i of sequence s."""27s_new = s.copy()28s_new[i] *= -129return s_new3031def tabu_local_search(s0):32"""33Algorithm 2: TabuSearch34Performs a Tabu Search for local improvement on a starting sequence.3536Input: starting sequence s0 (np.array)37Output: locally improved sequence s_best, s_best_energy3839We assume the existence of a utility function 'calculate_energy(s)'40to compute E(s)41"""42N = len(s0)4344# 1. Initialize variables45s_best = s0.copy() # ~s in the paper46p = s0.copy() # Current sequence p47s_best_energy = calculate_energy(s_best)4849# 2. Initialize TabuList50# TabuList[i] stores the iteration 't' until which index 'i' is forbidden.51TabuList = np.zeros(N, dtype=int)5253# 3. Determine number of iterations M54# M ∈ [N/2, 3N/2].55N_over_2 = N // 256M_range = N - 1 # Range is from N/2 to 3N/2, which is N wide.57# To get a random int in [N/2, 3N/2], we can do N/2 + random_int[0, N]58# np.random.randint(low, high) is [low, high-1]. We need +1 for inclusive range.59M = np.random.randint(0, N + 1) + N_over_26061# 4. Loop for t = 1 to M62for t in range(1, M + 1):6364# 5. Choose index i* with minimum energy among { i | TabuList[i] < t }6566# Candidate set: indices that are NOT tabu at time t67non_tabu_indices = np.where(TabuList < t)[0]6869if len(non_tabu_indices) == 0:70# If all moves are tabu, break the search71break7273best_candidate_energy = np.inf74i_star = -17576# Evaluate all non-tabu neighbors77for i in non_tabu_indices:78# Generate the candidate sequence p_candidate by flipping bit i in the current p79p_candidate = Flip(p, i)80E_candidate = calculate_energy(p_candidate)8182# Aspiration Criterion: If the move leads to a new global best,83# it should be chosen even if it is tabu.84# However, the algorithm does not explicitly list an aspiration criterion.85# Sticking strictly to line 5: Choose index i* with minimum energy among { i | TabuList[i] < t }8687if E_candidate < best_candidate_energy:88best_candidate_energy = E_candidate89i_star = i9091# The best non-tabu neighbor (p_star) is now identified by i_star and best_candidate_energy9293# 6. Update current sequence p94p = Flip(p, i_star)95E_p = best_candidate_energy # E(p) is already calculated9697# 7. Update TabuList for the chosen index i*98theta_min = int(M / 10)99theta_max = int(M / 50)100101# If theta_min < theta_max, we might have an issue with integer division.102# Assuming theta_min should be the smaller bound (M/50) and theta_max the larger (M/10).103# Adjusting interpretation for standard range: [M/50, M/10]104if theta_min > theta_max:105theta_min, theta_max = theta_max, theta_min106107# Ensure minimum tabu tenure is at least 1108if theta_min == 0:109theta_min = 1110if theta_max <= theta_min:111theta_max = theta_min + 1112113# Tabu tenure: random int(theta_min, theta_max)114# np.random.randint(low, high) is [low, high-1]. We use [theta_min, theta_max + 1].115tenure = np.random.randint(theta_min, theta_max + 1)116117TabuList[i_star] = t + tenure118119# 8. Check for Global Best Update120if E_p < s_best_energy:121# 9. ~s <- p122s_best = p.copy()123s_best_energy = E_p124125# 10. return ~s126return s_best, s_best_energy127128def compute_topology_overlaps(G2, G4):129"""130Computes the topological invariants I_22, I_24, I_44 based on set overlaps.131I_alpha_beta counts how many sets share IDENTICAL elements.132"""133# Helper to count identical sets134def count_matches(list_a, list_b):135matches = 0136# Convert to sorted tuples to ensure order doesn't affect equality137set_b = set(tuple(sorted(x)) for x in list_b)138for item in list_a:139if tuple(sorted(item)) in set_b:140matches += 1141return matches142143# For standard LABS/Ising chains, these overlaps are often 0 or specific integers144# We implement the general counting logic here.145I_22 = count_matches(G2, G2) # Self overlap is just len(G2)146I_44 = count_matches(G4, G4) # Self overlap is just len(G4)147I_24 = 0 # 2-body set vs 4-body set overlap usually 0 as sizes differ148149return {'22': I_22, '44': I_44, '24': I_24}150151from math import sin, cos, pi152153def compute_theta(t, dt, total_time, N):154"""155Computes theta(t) using the analytical solutions for Gamma1 and Gamma2.156"""157158# --- 1. Better Schedule (Trigonometric) ---159# lambda(t) = sin^2(pi * t / 2T)160# lambda_dot(t) = (pi / 2T) * sin(pi * t / T)161162if total_time == 0:163return 0.0164165# Argument for the trig functions166arg = (pi * t) / (2.0 * total_time)167168lam = sin(arg)**2169# Derivative: (pi/2T) * sin(2 * arg) -> sin(pi * t / T)170lam_dot = (pi / (2.0 * total_time)) * sin((pi * t) / total_time)171172# --- 2. Get Interactions and counts ---173G2, G4 = get_interactions(N)174175# --- 3. Calculate Gamma Terms (LABS assumptions: h^x=1, h^b=0) ---176# For G2 (size 2): S_x = 2177# For G4 (size 4): S_x = 4178179# Gamma 1 (Eq 16)180# Gamma1 = 16 * Sum_G2(S_x) + 64 * Sum_G4(S_x)181term_g1_2 = 16 * len(G2) * 2182term_g1_4 = 64 * len(G4) * 4183Gamma1 = term_g1_2 + term_g1_4184185# Gamma 2 (Eq 17)186# G2 term: Sum (lambda^2 * S_x)187# S_x = 2188sum_G2 = len(G2) * (lam**2 * 2)189190# G4 term: 4 * Sum (4*lambda^2 * S_x + (1-lambda)^2 * 8)191# S_x = 4192# Inner = 16*lam^2 + 8*(1-lam)^2193sum_G4 = 4 * len(G4) * (16 * (lam**2) + 8 * ((1 - lam)**2))194195# Topology part196I_vals = compute_topology_overlaps(G2, G4)197term_topology = 4 * (lam**2) * (4 * I_vals['24'] + I_vals['22']) + 64 * (lam**2) * I_vals['44']198199# Combine Gamma 2200Gamma2 = -256 * (term_topology + sum_G2 + sum_G4)201202# --- 4. Alpha & Theta ---203if abs(Gamma2) < 1e-12:204alpha = 0.0205else:206alpha = - Gamma1 / Gamma2207208return dt * alpha * lam_dot209210def get_interactions(N):211"""212Generates the interaction sets G2 and G4 based on the loop limits in Eq. 15.213Returns standard 0-based indices as lists of lists of ints.214215Args:216N (int): Sequence length.217218Returns:219G2: List of lists containing two body term indices220G4: List of lists containing four body term indices221"""222223G2 = []224# Eq 15: prod_{i=0}^{N-3} prod_{k=0}^{floor((N-i)/2)-1}225for i in range(1, N-2 +1):226limit_k = (N - i) // 2227for k in range(1, limit_k+1):228# Term: indices [i, i+k]229G2.append([i-1, i + k-1]) #subtract 1 for zero index230231G4 = []232# Eq 15: prod_{i=1}^{N-3} prod_{t=1}^{floor((N-i-1)/2)} prod_{k=t+1}^{N-i-t}233for i in range(1, N - 3 + 1):234limit_t = (N - i - 1) // 2235for t in range(1, limit_t +1):236limit_k_loop = N - i - t237for k in range(t + 1, limit_k_loop +1):238# Term: indices [i, i+t, i+k, i+k+t]239G4.append([i-1, i + t-1, i + k-1, i + k + t-1]) #subtract 1 for zero indexing of qubits240241#TODO END242243return G2, G4244245