1. Initialization and Setup
We define a quantum circuit (qc) with 127 qubits and 127 classical bits.
qc = QuantumCircuit(127, 127)
Calibration data for the backend (ibm_brisbane) is loaded from a CSV file. This data includes errors for various quantum gates such as RZ, SX, and ECR, as well as readout errors.
calibration_data = pd. read_csv(calibration_data_path)
The mean values of these errors are computed to understand the performance characteristics of the backend.
avg_rz_error = calibration_data[′Z-axis rotation (rz) error ′].mean()
avg_sx_error = calibration_data[′√x (sx) error ′].mean()
avg_ecr_error = calibration_data[′ECR error ′].mean()
avg_readout_error = calibration_data[′Readout assignment error ′].mean()
2. Twistor State Initialization
Each qubit in the circuit is initialized into a superposition state using the Hadamard gate.
qc.h(i)
Subsequent rotations are applied to encode the twistor state. These rotations use the following gates:
RZ Gate: Rotates the qubit around the Z-axis by an angle of π/4.
qc.rz(4/π, i)
RX Gate: Rotates the qubit around the X-axis by an angle of π/3.
qc.rx(π/3, i)
RY Gate: Rotates the qubit around the Y-axis by an angle of π/6.
qc.ry(π/6, i)
3. Application of Complex Conformal Transformations
We apply conformal rotations using a combination of controlled-RZ (CRZ), controlled-RX (CRX), and controlled-RY (CRY) gates. These transformations simulate the rotational symmetries in twistor space.
CRZ Gate: Applies a controlled rotation around the Z-axis by π/4.
qc.crz(4/π, i, i + 2)
CRX Gate: Applies a controlled rotation around the X-axis by π/3.
qc.crx(3/π, i + 1, i + 3)
CRY Gate: Applies a controlled rotation around the Y-axis by π/6.
qc.cry(6/π, i + 2, i + 4)
A custom unitary gate, representing a specific conformal transformation in twistor space, is created and applied. This gate is based on a 4x4 matrix constructed from the Kronecker product of a 2x2 rotation matrix and the identity matrix.
custom_unitary = np.kron(small_unitary, np.eye(2))
The unitary gate is then applied across the qubits, controlled by adjacent qubits.
qc.append(unitary_gate.control(1), [i, i + 1, i + 2])
Conformal dilations are implemented using controlled-RX (CRX) and controlled-X (CX) gates. These transformations represent scaling symmetries.
CRX Gate: Applies a controlled rotation around the X-axis by π/2.
qc.crx(2/π, i, i + 5)
CX Gate: Applies a controlled-X gate (CNOT).
qc. cx(i + 1, i + 6)
Additional conformal transformations are applied using a deeper combination of controlled-RZ, controlled-RX, controlled-RY, and controlled-X gates.
qc.crz(8/π, i, i + 6)
qc.crx(7/π, i + 1, i + 7)
qc.cry(5/π, i + 2, i + 8)
qc. cx(i + 3, i + 9)
qc.crz(6/π, i + 4, i + 10)
4. Measurement in the Twistor Basis
Before measurement, each qubit is rotated back using the RX gate with an angle of −π/3.
qc.rx(−3/π, i)
All qubits are then measured, and the results are stored in classical registers.
qc.measure(i, i)
5. Transpilation and Execution
The quantum circuit is transpiled to match the specific properties and calibration data of ibm_brisbane backend. The optimization level is set to 3.
transpiled_qc = transpile(qc, backend = backend, optimization_level = 3)
The circuit is executed on ibm_brisbane backend using SamplerV2. The number of shots is set to 8192.
job = sampler. run([transpiled_qc], shots = 8192)
The name of the classical register is retrieved, and the counts for the measurement outcomes are extracted.
classical_register = qc.cregs[0].name
pub_result = job_result[0].data[classical_register].get_counts()
6. Analysis and Visualization
The raw counts from the quantum computation are saved to a JSON.
A histogram of the measurement outcomes is plotted to visually inspect the distribution and potential symmetries.
Code:
# imports
import numpy as np
from qiskit import QuantumCircuit, transpile
from qiskit_ibm_runtime import QiskitRuntimeService, Session, SamplerV2
from qiskit.circuit.library import UnitaryGate, RZGate, RXGate, RYGate, CXGate, CRXGate, CRYGate, CRZGate
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_histogram
import json
import logging
import matplotlib.pyplot as plt
import pandas as pd
# Logging
logging.basicConfig(level=logging. INFO)
logger = logging.getLogger(__name__)
# Load IBM Quantum account
service = QiskitRuntimeService(
channel='ibm_quantum',
instance='ibm-q/open/main',
token='YOUR_IBMQ_KEY_O-`'
)
backend = service.backend('ibm_brisbane')
# Load backend calibration data from CSV file
calibration_data_path = '/Users/Documents/ibm_brisbane_calibrations_2024-08-29T20_24_47Z.csv'
calibration_data = pd. read_csv(calibration_data_path)
# Convert the relevant columns to numeric, coercing errors to NaN
calibration_data['Readout assignment error '] = pd. to_numeric(calibration_data['Readout assignment error '], errors='coerce')
calibration_data['Z-axis rotation (rz) error '] = pd. to_numeric(calibration_data['Z-axis rotation (rz) error '], errors='coerce')
calibration_data['√x (sx) error '] = pd. to_numeric(calibration_data['√x (sx) error '], errors='coerce')
calibration_data['ECR error '] = pd. to_numeric(calibration_data['ECR error '], errors='coerce')
# Calculate the means
avg_readout_error = calibration_data['Readout assignment error '].mean()
avg_rz_error = calibration_data['Z-axis rotation (rz) error '].mean()
avg_sx_error = calibration_data['√x (sx) error '].mean()
avg_ecr_error = calibration_data['ECR error '].mean()
# Define the quantum circuit
num_qubits = 127 # Maximum qubits available on ibm_osaka
qc = QuantumCircuit(num_qubits, num_qubits)
# Step 1: Initialize qubits in twistor state representations
for i in range(num_qubits):
qc.h(i)
qc.rz(np.pi / 4, i)
qc.rx(np.pi / 3, i)
qc.ry(np.pi / 6, i)
# Step 2: Apply Complex Conformal Transformations
for i in range(0, num_qubits - 4, 3):
qc.crz(np.pi / 4, i, i + 2)
qc.crx(np.pi / 3, i + 1, i + 3)
qc.cry(np.pi / 6, i + 2, i + 4)
theta = np.pi / 4
small_unitary = np.array([
[np.cos(theta), -np.sin(theta)],
[np.sin(theta), np.cos(theta)]
])
custom_unitary = np.kron(small_unitary, np.eye(2))
unitary_gate = UnitaryGate(custom_unitary)
for i in range(0, num_qubits - 3, 4):
qc.append(unitary_gate.control(1), [i, i + 1, i + 2])
for i in range(0, num_qubits - 6, 5):
qc.crx(np.pi / 2, i, i + 5)
qc. cx(i + 1, i + 6)
for i in range(0, num_qubits - 10, 6):
qc.crz(np.pi / 8, i, i + 6)
qc.crx(np.pi / 7, i + 1, i + 7)
qc.cry(np.pi / 5, i + 2, i + 8)
qc. cx(i + 3, i + 9)
qc.crz(np.pi / 6, i + 4, i + 10)
# Step 3: Measure in the twistor basis
for i in range(num_qubits):
qc.rx(-np.pi / 3, i)
qc.measure(i, i)
# Transpile the circuit
transpiled_qc = transpile(qc, backend=backend, optimization_level=3)
# Execution with SamplerV2
with Session(service=service, backend=backend) as session:
sampler = SamplerV2(session=session)
# Run the circuit
job = sampler. run([transpiled_qc], shots=8192)
job_result = job.result()
# Retrieve the classical register name
classical_register = qc.cregs[0].name
# Extract counts for the first (and only) pub result
pub_result = job_result[0].data[classical_register].get_counts()
# Save the results to JSON
results_data = {
"raw_counts": pub_result
}
file_path = '/Users/Documents/qft_twistor_experiment_results.json'
with open(file_path, 'w') as f:
json.dump(results_data, f, indent=4)
# Plotting the results
plot_histogram(pub_result)
plt. show()
# End
///////////////////////////////////////////////////////////////////
Code for Bitstring Frequency Distribution Visual, Hamming Weight Distribution Visual, and Pairwise Bit Correlations Visual from Run Data
# imports
import json
import matplotlib.pyplot as plt
from collections import Counter
import numpy as np
# Load the results from JSON
file_path = '/Users/Documents/qft_twistor_experiment_results.json'
with open(file_path, 'r') as file:
results = json.load(file)
# Extract raw counts
counts = results['raw_counts']
# Plot 1: Bitstring Frequency Distribution
bitstring_frequencies = Counter(counts)
plt.figure(figsize=(10, 6))
plt. bar(range(len(bitstring_frequencies)), list(bitstring_frequencies.values()), color='blue')
plt.xlabel('Bitstring Index')
plt.ylabel('Frequency')
plt.title('Bitstring Frequency Distribution')
plt. show()
# Plot 2: Hamming Weight Distribution
hamming_weights = [bitstring.count('1') for bitstring in counts.keys()]
plt.figure(figsize=(10, 6))
plt.hist(hamming_weights, bins=range(128), color='green', edgecolor='black')
plt.xlabel('Hamming Weight')
plt.ylabel('Frequency')
plt.title('Hamming Weight Distribution')
plt. show()
# Plot 3: Pairwise Bit Correlations
num_qubits = 127
correlation_matrix = np.zeros((num_qubits, num_qubits))
for bitstring in counts.keys():
for i in range(num_qubits):
for j in range(i+1, num_qubits):
if bitstring[i] == bitstring[j]:
correlation_matrix[i, j] += 1
total_shots = sum(counts.values())
correlation_matrix /= total_shots
plt.figure(figsize=(12, 10))
plt.imshow(correlation_matrix, cmap='hot', interpolation='nearest')
plt.colorbar()
plt.xlabel('Qubit Index')
plt.ylabel('Qubit Index')
plt.title('Pairwise Bit Correlations')
plt. show()
///////////////////////////////////////////////////////////////////
Code for the Bistring Cluster Analysis with PCA Visual, Distribution of Hamming Distances Between Bitstrings Visual, and Fourier Transform of Bitstrings Visual from Run Data
# imports
import json
import numpy as np
import matplotlib.pyplot as plt
from itertools import combinations
# Load the results from JSON
file_path = '/Users/Documents/qft_twistor_experiment_results.json'
with open(file_path, 'r') as file:
results = json.load(file)
# Extract raw counts
counts = results['raw_counts']
# Convert bitstrings to a list of integers
bitstrings = list(counts.keys())
bitstring_frequencies = list(counts.values())
# Convert bitstrings to numpy array
bit_array = np.array([[int(bit) for bit in bitstring] for bitstring in bitstrings])
# Visualization 1: Bitstring Cluster Analysis
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
# Apply PCA to reduce dimensionality
pca = PCA(n_components=2)
bitstring_pca = pca. fit_transform(bit_array)
# Apply KMeans clustering
kmeans = KMeans(n_clusters=5)
clusters = kmeans. fit_predict(bitstring_pca)
# Plot the PCA-transformed bitstrings with cluster coloring
plt.figure(figsize=(10, 6))
plt.scatter(bitstring_pca[:, 0], bitstring_pca[:, 1], c=clusters, cmap='viridis')
plt.title('Bitstring Cluster Analysis with PCA')
plt.xlabel('PCA Component 1')
plt.ylabel('PCA Component 2')
plt.colorbar(label='Cluster')
plt. show()
# Visualization 2: Distribution of Hamming Distances
def hamming_distance(s1, s2):
"""Calculate the Hamming distance between two bitstrings."""
return sum(c1 != c2 for c1, c2 in zip(s1, s2))
# Calculate all pairwise Hamming distances
hamming_distances = [hamming_distance(b1, b2) for b1, b2 in combinations(bitstrings, 2)]
# Plot the distribution of Hamming distances
plt.figure(figsize=(10, 6))
plt.hist(hamming_distances, bins=range(128), color='purple', edgecolor='black')
plt.xlabel('Hamming Distance')
plt.ylabel('Frequency')
plt.title('Distribution of Hamming Distances Between Bitstrings')
plt. show()
# Visualization 3: Fourier Transform of Bitstrings
def bitstring_to_int(bitstring):
"""Convert a bitstring to an integer."""
return int(bitstring, 2)
# Convert bitstrings to integers
bitstring_ints = [bitstring_to_int(bitstring) for bitstring in bitstrings]
# Apply Fourier transform
fft_result = np.fft.fft(bitstring_ints)
fft_magnitude = np.abs(fft_result)
# Plot the magnitude of the Fourier transform
plt.figure(figsize=(10, 6))
plt.plot(fft_magnitude, color='orange')
plt.xlabel('Frequency Component')
plt.ylabel('Magnitude')
plt.title('Fourier Transform of Bitstrings')
plt. show()
# End.
/////////////////////////////////////////////////////////////
Code for 3D Pairwise Correlation of Qubits Visual 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/qft_twistor_experiment_results.json'
with open(file_path, 'r') as file:
results = json.load(file)
# Extract raw counts
counts = results['raw_counts']
# Convert bitstrings to a list of integers
bitstrings = list(counts.keys())
# Convert bitstrings to numpy array
bit_array = np.array([[int(bit) for bit in bitstring] for bitstring in bitstrings])
# Calculate pairwise correlations in 3D space
num_qubits = bit_array.shape[1]
correlation_matrix_3d = np.zeros((num_qubits, num_qubits, num_qubits))
# Calculate correlations for all triplet combinations of qubits
for i in range(num_qubits):
for j in range(i+1, num_qubits):
for k in range(j+1, num_qubits):
correlation_matrix_3d[i, j, k] = np.mean(bit_array[:, i] * bit_array[:, j] * bit_array[:, k])
# Plot the 3D correlation matrix
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111, projection='3d')
# Use non-zero correlations for plotting
non_zero_indices = np.nonzero(correlation_matrix_3d)
ax.scatter(non_zero_indices[0], non_zero_indices[1], non_zero_indices[2], c=correlation_matrix_3d[non_zero_indices], cmap='hot', depthshade=True)
ax.set_xlabel('Qubit Index 1')
ax.set_ylabel('Qubit Index 2')
ax.set_zlabel('Qubit Index 3')
ax.set_title('3D Pairwise Correlation of Qubits')
plt. show()
# End.