Source code for ingenii_quantum.hybrid_networks.statistics
from pennylane import numpy as np
from pennylane.math import partial_trace, reduce_statevector, fidelity_statevector
from scipy.special import kl_div
[docs]
class EntanglingCapacity:
def __init__(self, circuit, dev, params_shape):
"""
Initializes the EntanglingCapacity class with the given circuit, device, and parameter shape.
Args:
circuit: the QNode representing the quantum circuit
dev: quantum device used for simulation
params_shape: shape of the parameters for the circuit
"""
self.circuit = circuit
self.dev = dev
self.params_shape = params_shape
self.N = len(dev.wires) # Number of qubits
[docs]
def generate_random_params(self):
"""
Generates random parameters for the quantum circuit based on the specified parameter shape.
"""
return np.random.uniform(0, 2 * np.pi, self.params_shape)
[docs]
def meyer_wallach_entanglement(self, n_samples):
"""
Computes the Meyer-Wallach entanglement measure for the quantum circuit.
Args:
n_samples (int): Number of samples to calculate the entanglement measure
Returns:
float: Meyer-Wallach entanglement measure averaged over multiple samples.
"""
res = np.zeros(n_samples, dtype=complex)
for i in range(n_samples):
params = self.generate_random_params() # Random parameters for the PQC
state = self.circuit(params) # Get quantum state from the PQC
# Reduce the full state vector to density matrix for the entire system
rho = reduce_statevector(state, indices=self.dev.wires)
entropy = self._calculate_entropy(rho)
# Meyer-Wallach measure for the current state
res[i] = 1 - (entropy / self.N)
# Average over the samples and return
return float(2 * np.mean(res).real)
def _calculate_entropy(self, rho):
"""
Helper function to calculate the average entropy over all qubits.
Args:
rho: the reduced state of the quantum system (density matrix).
Returns:
float: Entropy
"""
entropy = 0
qb_indices = list(range(self.N))
# Loop over each qubit, calculate the partial trace and its entropy
for j in range(self.N):
# Partial trace over all qubits except the j-th qubit
dens = partial_trace(rho, qb_indices[:j] + qb_indices[j+1:])
trace = np.trace(dens**2) # Calculate the purity (trace of the square of density matrix)
entropy += trace
return entropy
[docs]
class Expressibility:
def __init__(self, pqc, params_shape, dev):
"""
Initialize the Expressibility class.
Args:
pqc (qml.QNode): Parameterized quantum circuit.
params_shape (tuple): Shape of the parameters used in the PQC.
dev (qml.Device): Pennylane device for executing the PQC.
"""
self.pqc = pqc
self.params_shape = params_shape
self.dev = dev
[docs]
def generate_random_params(self, params_shape):
"""
Generate random parameters for the parameterized quantum circuit.
Args:
params_shape (tuple): Shape of the parameters.
Returns:
np.ndarray: Randomly generated parameters in the range [0, 2π].
"""
return np.random.uniform(0, 2 * np.pi, params_shape)
[docs]
def pqc_fidelity(self, n_samples):
"""
Calculate the fidelity between quantum states produced by the PQC.
Args:
n_samples (int): Number of samples for fidelity computation.
Returns:
list: List of fidelity values for the sampled states.
"""
fidelities = []
for _ in range(n_samples):
# Get random parameters for PQC1 and PQC2
params1 = self.generate_random_params(self.params_shape)
params2 = self.generate_random_params(self.params_shape)
state1 = self.pqc(params1)
state2 = self.pqc(params2)
# Calculate fidelity between the two states
fidelities.append(fidelity_statevector(state1, state2))
return fidelities
[docs]
def haar_fidelity(self, n_samples):
"""
Generate fidelities for Haar-random states.
Args:
n_samples (int): Number of samples for fidelity computation.
Returns:
np.ndarray: Array of Haar-random fidelities.
"""
n_qubits = len(self.dev.wires)
N = 2**n_qubits
F = np.random.uniform(0, 1, n_samples)
return (N-1) * (1 - F)**(N-2)
[docs]
def compute_expressibility(self, n_samples):
"""
Compute the expressibility of the PQC by comparing its fidelity distribution
to that of Haar-random states using KL divergence.
Args:
n_samples (int): Number of samples for the computation.
Returns:
float: Expressibility value, computed as the mean KL divergence.
"""
pqc_f = self.pqc_fidelity(n_samples)
haar_f = self.haar_fidelity(n_samples)
return float(np.mean(kl_div(pqc_f, haar_f)))