|
| 1 | +import numpy as np |
| 2 | +from scipy.linalg import expm |
| 3 | + |
| 4 | +# --- Simulation Parameters (user configurable) --- |
| 5 | +num_qubits = 3 # Number of qubits in the system |
| 6 | +num_terms = 3 * num_qubits # Number of random Pauli terms in H (e.g., 3*n) |
| 7 | +total_time = 1.0 # Total evolution time |
| 8 | +trotter_steps = 10 # Number of Trotter steps (N) |
| 9 | +track_error = False # If True, track fidelity at each step (prints overlaps) |
| 10 | + |
| 11 | +np.random.seed(42) # Set a random seed for reproducibility (optional) |
| 12 | + |
| 13 | +# Define Pauli matrices (2x2 complex NumPy arrays) |
| 14 | +I = np.eye(2, dtype=complex) |
| 15 | +X = np.array([[0, 1], [1, 0]], dtype=complex) |
| 16 | +Y = np.array([[0, -1j], [1j, 0]], dtype=complex) |
| 17 | +Z = np.array([[1, 0], [0, -1]], dtype=complex) |
| 18 | +paulis = [I, X, Y, Z] |
| 19 | +pauli_labels = ['I', 'X', 'Y', 'Z'] # for reference/printing |
| 20 | + |
| 21 | +# Randomly generate a Hamiltonian as a sum of Pauli strings |
| 22 | +terms = [] # list to hold (coeff, matrix, label) for each term |
| 23 | +dim = 2 ** num_qubits |
| 24 | +H = np.zeros((dim, dim), dtype=complex) # Hamiltonian matrix |
| 25 | +for term_index in range(num_terms): |
| 26 | + # Randomly pick a Pauli for each qubit to form a tensor-product term |
| 27 | + matrices = [] |
| 28 | + labels = [] |
| 29 | + for q in range(num_qubits): |
| 30 | + choice = np.random.randint(0, 4) # 0=I, 1=X, 2=Y, 3=Z |
| 31 | + matrices.append(paulis[choice]) |
| 32 | + labels.append(pauli_labels[choice]) |
| 33 | + # Ensure the term is not Identity on all qubits (if so, replace one factor with X) |
| 34 | + if all(lbl == 'I' for lbl in labels): |
| 35 | + rand_q = np.random.randint(0, num_qubits) |
| 36 | + matrices[rand_q] = X |
| 37 | + labels[rand_q] = 'X' |
| 38 | + # Construct the term matrix via Kronecker (tensor) product |
| 39 | + term_matrix = matrices[0] |
| 40 | + for mat in matrices[1:]: |
| 41 | + term_matrix = np.kron(term_matrix, mat) |
| 42 | + # Random real coefficient for this term |
| 43 | + coeff = np.random.uniform(-1.0, 1.0) |
| 44 | + # Add to Hamiltonian |
| 45 | + H += coeff * term_matrix |
| 46 | + terms.append((coeff, term_matrix, "".join(labels))) |
| 47 | + |
| 48 | +# Print out the generated Hamiltonian terms (for information) |
| 49 | +print(f"Hamiltonian has {len(terms)} terms on {num_qubits} qubits:") |
| 50 | +for coeff, _, label in terms: |
| 51 | + print(f" {coeff:+.3f} * ({label})") |
| 52 | + |
| 53 | +# Initial state |00...0>: a vector of size 2^n with 1 in the 0th element |
| 54 | +initial_state = np.zeros(dim, dtype=complex) |
| 55 | +initial_state[0] = 1.0 |
| 56 | + |
| 57 | +# Prepare for Trotter evolution |
| 58 | +dt = total_time / trotter_steps # time step size |
| 59 | +state = initial_state.copy() # will hold the evolving state vector |
| 60 | + |
| 61 | +# (Optional) prepare for exact intermediate evolution tracking |
| 62 | +if track_error: |
| 63 | + U_step = expm(-1j * H * dt) # exact propagator for one step |
| 64 | + exact_state = initial_state.copy() # will hold exact state as we step through |
| 65 | + |
| 66 | +# --- Main Trotter Evolution Loop --- |
| 67 | +for step in range(trotter_steps): |
| 68 | + # Apply each term's evolution for this time slice |
| 69 | + for (coeff, term_matrix, label) in terms: |
| 70 | + theta = coeff * dt # the angle for this term's evolution |
| 71 | + # Using exp(-i * coeff * P * dt) = I * cos(theta) - i * P * sin(theta) |
| 72 | + cos_theta = np.cos(theta) |
| 73 | + sin_theta = np.sin(theta) |
| 74 | + # Update state: new_state = cos(theta)*state - i * sin(theta) * (P * state) |
| 75 | + state = cos_theta * state - 1j * sin_theta * (term_matrix.dot(state)) |
| 76 | + # After applying all terms, one full Trotter step is complete. |
| 77 | + if track_error: |
| 78 | + # Update exact state by one full step and compute fidelity |
| 79 | + exact_state = U_step.dot(exact_state) |
| 80 | + overlap = np.vdot(exact_state, state) # inner product <psi_exact | psi_trotter> |
| 81 | + fidelity = np.abs(overlap)**2 |
| 82 | + print(f"Step {step+1:2d}/{trotter_steps}: fidelity = {fidelity:.6f}") |
| 83 | + |
| 84 | +# Compute exact final state for comparison using full matrix exponential |
| 85 | +psi_exact_final = expm(-1j * H * total_time).dot(initial_state) |
| 86 | + |
| 87 | +# Compute overlap and fidelity between Trotter result and exact result |
| 88 | +final_overlap = np.vdot(psi_exact_final, state) |
| 89 | +final_fidelity = np.abs(final_overlap)**2 |
| 90 | + |
| 91 | +print("\nFinal state (Trotter approximation):") |
| 92 | +print(state) |
| 93 | +print("\nFinal state (Exact evolution):") |
| 94 | +print(psi_exact_final) |
| 95 | +print(f"\nOverlap = {final_overlap:.6f}") |
| 96 | +print(f"Fidelity = {final_fidelity:.6f}") |
0 commit comments