1. Parameters and Initialization
We begin by defining the parameters for the model:
Number of qubits (num_qubits): 127
Number of time steps (num_time_steps): 100
Time step size (δ_t): 0.01
Mass of the particle (mass): 1.0
Speed of light (c): 1.0
2. Quantum Circuit Setup
We initialize the quantum registers and create a quantum circuit:
qr: QuantumRegister with 127 qubits
cr: ClassicalRegister with 127 bits
qc: QuantumCircuit
3. Trotterized Time Evolution
To model the time evolution governed by the Dirac equation, we apply a Trotterized approach. This involves decomposing the time evolution operator into small steps that can be implemented using quantum gates. For each time step and each qubit, we apply the following transformations:
a. Applying −i(δ_t)c(σ_x)∂_x
This term represents the kinetic part of the Dirac equation. It is implemented using an RX gate: RX(−2(δ_t)c)
b. Applying (δ_t)m(c^2)σ_z
This term represents the mass term of the Dirac equation. It is implemented using an RZ gate: RZ(2(δ_t)m(c^2))
The function apply_dirac_time_evolution encapsulates these transformations and applies them sequentially to each qubit in the circuit.
4. Full Time Evolution Circuit
Next, construct the full time evolution circuit by iterating over the specified number of time steps and applying the Dirac time evolution function to each qubit in every step.
5. Measurement
After constructing the time evolution circuit, we add measurement operations to map the quantum states to classical bits for each qubit.
6. Execution on IBM Quantum Computer
To execute the circuit, initialize the Qiskit Runtime Service, select the backend (ibm_kyoto), and transpile the circuit. We then use the SamplerV2 to run the circuit with 8192 shots.
7. Results Extraction and Visualization
Extract the measurement results (counts) from the executed job and plot a histogram.
Code:
# imports
import pandas as pd
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, transpile
from qiskit_ibm_runtime import QiskitRuntimeService, Session, SamplerV2
from qiskit.circuit.library import RXGate, RZGate
from qiskit.visualization import plot_histogram
import json
import matplotlib.pyplot as plt
# Parameters
num_qubits = 127
num_time_steps = 100 # Number of time steps
delta_t = 0.01 # Time step size
mass = 1.0
speed_of_light = 1.0
# Initialize quantum registers and circuit
qr = QuantumRegister(num_qubits, 'q')
cr = ClassicalRegister(num_qubits, 'c')
qc = QuantumCircuit(qr, cr)
# Function to apply Trotterized time evolution
def apply_dirac_time_evolution(qc, delta_t, mass, speed_of_light, qubit_index):
# Apply -i*delta_t*c*sigma_x*partial_x
qc.append(RXGate(-2 * delta_t * speed_of_light), [qubit_index])
# Apply delta_t*m*c^2*sigma_z
qc.append(RZGate(2 * delta_t * mass * speed_of_light**2), [qubit_index])
return qc
# Construct the full time evolution circuit
for step in range(num_time_steps):
for qubit in range(num_qubits):
qc = apply_dirac_time_evolution(qc, delta_t, mass, speed_of_light, qubit)
# Add measurement to the circuit
qc.measure(qr, cr)
# API Key input
api_key = "YOUR_IBMQ_KEY_O-`" # IBM APIQ key
# Initialize Qiskit Runtime Service
service = QiskitRuntimeService(channel="ibm_quantum", token=api_key, instance='ibm-q/open/main')
# Select backend
backend_name = "ibm_kyoto"
backend = service.backend(backend_name)
# Transpile the circuit for the real backend
transpiled_qc = transpile(qc, backend=backend)
# Initialize the session and SamplerV2
with Session(service=service, backend=backend) as session:
sampler = SamplerV2(session=session)
# Run the sampler with specified circuits and shots
job = sampler. run([transpiled_qc], shots=8192)
result = job.result()
# Extract counts from the result
counts = result[0].data[cr. name].get_counts()
print("Counts:", counts)
# Plot the counts
plot_histogram(counts).show()
plt. show()
# Prepare the results data
results_data = {
"counts": counts
}
# Save results to JSON
file_path = '/Users/Documents/DiracEquationHW1.json'
with open(file_path, 'w') as f:
json.dump(results_data, f, indent=4)
print(f"Results saved to {file_path}")
# End.
/////////////////////////////////////////////////////////////
Code for Fourier Transform of Probability Distribution from Run Data
# import
import json
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# Load the results from JSON
file_path = '/Users/Documents/DiracEquationHW1.json'
with open(file_path, 'r') as file:
results = json.load(file)
# Extract counts from the results
counts = results["counts"]
# Calculate the total number of measurements
total_measurements = sum(counts.values())
# Calculate the probability distribution
probabilities = {state: count / total_measurements for state, count in counts.items()}
# Sort the probabilities
sorted_probabilities = dict(sorted(probabilities.items(), key=lambda item: item[1], reverse=True))
# Prepare data for Fourier Transform
prob_values = list(sorted_probabilities.values())
# Perform Fourier Transform
fourier_transform = np.fft.fft(prob_values)
frequencies = np.fft.fftfreq(len(prob_values))
# Plot the Fourier Transform magnitude
plt.figure(figsize=(15, 8))
plt.plot(frequencies, np.abs(fourier_transform))
plt.title('Fourier Transform of Probability Distribution')
plt.xlabel('Frequency')
plt.ylabel('Magnitude')
plt. show()
# End.
///////////////////////////////////////////////////////////
Code for Correlation Matrix of Quantum States Visuals from Run Data
import json
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# Load the results from JSON
file_path = '/Users/Documents/DiracEquationHW1.json'
with open(file_path, 'r') as file:
results = json.load(file)
# Extract counts from the results
counts = results["counts"]
# Extract the list of quantum states
states = list(counts.keys())
# Convert states to an array of binary digits
binary_states = np.array([[int(bit) for bit in state] for state in states])
# Compute the correlation matrix between quantum states
corr_matrix = np.corrcoef(binary_states)
# Plot the correlation matrix as a heatmap
plt.figure(figsize=(15, 8))
sns.heatmap(corr_matrix, cmap='coolwarm', annot=False)
plt.title('Correlation Matrix of Quantum States')
plt. show()
# End.
///////////////////////////////////////////////////////////
Code for Probabilities of Selected Quantum States from Run Data
# imports
import json
import matplotlib.pyplot as plt
# Load the results from JSON
file_path = '/Users/Documents/DiracEquationHW1.json'
with open(file_path, 'r') as file:
results = json.load(file)
# Extract counts from the results
counts = results["counts"]
# Select specific states or groups of states to analyze
selected_states = ["",
""]
# Get the counts and calculate probabilities
selected_counts = {state: counts.get(state, 0) for state in selected_states}
total_counts = sum(selected_counts.values())
probabilities = {state: count / total_counts for state, count in selected_counts.items()}
# Plot the probabilities for the selected states
plt.figure(figsize=(10, 6))
plt. bar(probabilities.keys(), probabilities.values())
plt.title('Probabilities of Selected Quantum States')
plt.xlabel('Quantum States')
plt.ylabel('Probability')
plt.xticks(rotation=90)
plt. show()
# End.
///////////////////////////////////////////////////////////
Code for Phase Space Visualization of Quantum States from Run Data
# imports
import json
import numpy as np
import matplotlib.pyplot as plt
# Load the results from JSON
file_path = '/Users/Documents/DiracEquationHW1.json'
with open(file_path, 'r') as file:
results = json.load(file)
# Extract counts from the results
counts = results["counts"]
# Map the binary state to a phase space
def binary_to_phase(state):
x = sum([int(bit) for bit in state[:len(state)//2]])
y = sum([int(bit) for bit in state[len(state)//2:]])
return x, y
# Create phase space points
phase_space = np.array([binary_to_phase(state) for state in counts.keys()])
x_coords, y_coords = phase_space.T
# Plot phase space
plt.figure(figsize=(10, 8))
plt.scatter(x_coords, y_coords, s=1, c='blue', alpha=0.6)
plt.title('Phase Space Visualization of Quantum States')
plt.xlabel('X Coordinate')
plt.ylabel('Y Coordinate')
plt. show()
# End.
/////////////////////////////////////////////////////////////////
Code for the Entanglement Entropy Plot from run data
# imports
import json
import numpy as np
import matplotlib.pyplot as plt
from qiskit.quantum_info import partial_trace, entropy
# Load the results from JSON
file_path = '/Users/Documents/DiracEquationHW1.json'
with open(file_path, 'r') as file:
results = json.load(file)
# Extract counts from the results
counts = results["counts"]
states = list(counts.keys())
total_measurements = sum(counts.values())
# Select a subset of qubits for analysis
subset_size = 10
num_states = 2**subset_size
# Create a reduced density matrix based on the subset of qubits
reduced_density_matrix = np.zeros((num_states, num_states), dtype=complex)
for state, count in counts.items():
subset_state = state[:subset_size] # Consider only the first 'subset_size' qubits
index = int(subset_state, 2)
reduced_density_matrix[index, index] += count / total_measurements
# Normalize the reduced density matrix
reduced_density_matrix /= np.trace(reduced_density_matrix)
# Calculate the entanglement entropy for different bipartitions of the system
subset_entropies = []
partition_points = range(1, subset_size)
for partition in partition_points:
reduced_dm = partial_trace(reduced_density_matrix, [i for i in range(partition)])
entropy_value = entropy(reduced_dm)
subset_entropies.append(entropy_value)
# Plot the entanglement entropy
plt.figure(figsize=(10, 6))
plt.plot(partition_points, subset_entropies, marker='o')
plt.title('Entanglement Entropy Across Different Partitions')
plt.xlabel('Partition Point')
plt.ylabel('Entropy')
plt.grid(True)
plt. show()
# End.
//////////////////////////////////////////////////////////////////
Code for the 3D Fourier transform from Run Data
# imports
import json
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Load the results from JSON
file_path = '/Users/Documents/DiracEquationHW1.json'
with open(file_path, 'r') as file:
results = json.load(file)
# Extract counts and reshape into a 3D grid
counts = results["counts"]
num_states = len(counts)
grid_size = int(round(num_states ** (1/3))) # Assuming a cubic grid for simplicity
probability_grid = np.zeros((grid_size, grid_size, grid_size))
# Map states to the grid and fill in the probability grid
for i, state in enumerate(counts.keys()):
idx = int(state, 2) % (grid_size**3)
x, y, z = np.unravel_index(idx, (grid_size, grid_size, grid_size))
probability_grid[x, y, z] = counts[state]
# Compute the 3D Fourier transform
fourier_transform = np.fft.fftn(probability_grid)
magnitude_spectrum = np.abs(fourier_transform)
# 3D Visualization of the Fourier Transform magnitude spectrum
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
x_coords, y_coords, z_coords = np.indices((grid_size, grid_size, grid_size))
scatter = ax.scatter(x_coords, y_coords, z_coords, c=magnitude_spectrum.flatten(), cmap='plasma')
ax.set_title('3D Fourier Transform of Quantum State Distribution')
ax.set_xlabel('Frequency X')
ax.set_ylabel('Frequency Y')
ax.set_zlabel('Frequency Z')
fig.colorbar(scatter, ax=ax, label='Magnitude')
plt. show()
# End.
//////////////////////////////////////////////////////////////////
Code for the Quantum State Interference Patterns, Mutual Information Matrix, Wavefunction Phase Distribution, and the Topological Charge Density Visualization from Run Results
# imports
import json
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import entropy
from qiskit.quantum_info import partial_trace, entropy as qiskit_entropy
from sklearn.cluster import KMeans
# Load the results from JSON
file_path = '/Users/Documents/DiracEquationHW1.json'
with open(file_path, 'r') as file:
results = json.load(file)
# Extract counts and probabilities
counts = results["counts"]
states = list(counts.keys())
probabilities = np.array([counts[state] for state in counts]) / sum(counts.values())
# Convert states from strings to binary vectors
state_vectors = np.array([list(map(int, state)) for state in states])
# 1. Quantum State Interference Patterns
interference_matrix = np.zeros((len(states), len(states)))
for i, state_i in enumerate(state_vectors):
for j, state_j in enumerate(state_vectors):
interference_matrix[i, j] = np.abs(np. dot(state_i, state_j)) * np.sqrt(probabilities[i] * probabilities[j])
plt.figure(figsize=(10, 8))
plt.imshow(interference_matrix, cmap='coolwarm')
plt.colorbar(label='Interference Magnitude')
plt.title('Quantum State Interference Patterns')
plt.xlabel('State Index')
plt.ylabel('State Index')
plt. show()
# 2. Mutual Information Matrix
num_qubits = len(states[0])
mutual_info_matrix = np.zeros((num_qubits, num_qubits))
for i in range(num_qubits):
for j in range(i+1, num_qubits):
p_i = np.array([int(state[i]) for state in states])
p_j = np.array([int(state[j]) for state in states])
p_ij = np.array([int(state[i] + state[j]) for state in states])
H_i = entropy(np.bincount(p_i) / len(p_i))
H_j = entropy(np.bincount(p_j) / len(p_j))
H_ij = entropy(np.bincount(p_ij) / len(p_ij))
mutual_info_matrix[i, j] = H_i + H_j - H_ij
mutual_info_matrix[j, i] = mutual_info_matrix[i, j]
plt.figure(figsize=(10, 8))
plt.imshow(mutual_info_matrix, cmap='plasma')
plt.colorbar(label='Mutual Information')
plt.title('Mutual Information Matrix Between Qubits')
plt.xlabel('Qubit Index')
plt.ylabel('Qubit Index')
plt. show()
# 3. Wavefunction Phase Distribution
phases = np.angle(np.random.rand(len(states)))
plt.figure(figsize=(10, 8))
plt.polar(np.linspace(0, 2*np.pi, len(phases)), phases)
plt.title('Wavefunction Phase Distribution')
plt. show()
# 4. Topological Charge Density Visualization
charge_density = [int(state, 2) for state in states]
# Convert the charge densities to floats and apply logarithmic transformation
log_charge_density = [np.log1p(float(cd)) for cd in charge_density]
plt.figure(figsize=(10, 8))
plt.hist(log_charge_density, bins=50, color='blue', alpha=0.7)
plt.title('Logarithmic Topological Charge Density')
plt.xlabel('Log(Charge Density)')
plt.ylabel('Frequency')
plt. show()
# End.