import numpy as np
import scipy.special
import random
from qutip import Qobj, tensor, basis, ket2dm, bell_state, ghz_state, coherent, fock
from qsttoolkit.data.num_state_coeffs import states17, statesM, statesP, statesP2, statesM2
from qsttoolkit.quantum import hadamard
from qsttoolkit.utils import _range_error, _deprecation_warning
##### Discrete-variable quantum states #####
### State vectors ###
[docs]
def hadamard_state(N: int) -> Qobj:
"""
Generates an N-qubit Hadamard state |Φ_H⟩ = (|0⟩ + |1⟩)/√2 ⊗ ... ⊗ (|0⟩ + |1⟩)/√2, equivalent to applying a Hadamard gate to every qubit in |0⟩^⊗N.
Parameters
----------
N : int
Number of qubits.
Returns
-------
Qobj
Hadamard state.
"""
if N < 1: raise ValueError("Number of qubits must be at least 1.")
zero_state = tensor([basis(2, 0)] * N)
H_all = tensor([hadamard()] * N)
return H_all * zero_state
### Density matrices ###
[docs]
def hadamard_dm(N: int) -> Qobj:
"""
Generates a density matrix for the N-qubit Hadamard state |Φ_H⟩ = (|0⟩ + |1⟩)/√2 ⊗ ... ⊗ (|0⟩ + |1⟩)/√2, equivalent to applying a Hadamard gate to every qubit in |0⟩^⊗N.
Parameters
----------
N : int
Number of qubits.
Returns
-------
Qobj
Density matrix for the Hadamard state.
"""
if N < 1: raise ValueError("Number of qubits must be at least 1.")
return ket2dm(hadamard_state(N))
[docs]
def bell_dm(state: str='00') -> Qobj:
"""
Generates a density matrix for a given Bell state.
Parameters
----------
state : str
Type of Bell state to generate. Must be one of '00', '01', '10', or '11'.
Returns
-------
Qobj
Density matrix for the Bell state.
"""
return ket2dm(bell_state(state))
[docs]
def ghz_dm(N: int) -> Qobj:
"""
Generates a density matrix for an N-qubit Greenberger-Horne-Zeilinger (GHZ) state.
Parameters
----------
N : int
Number of qubits.
Returns
-------
Qobj
Density matrix for the GHZ state.
"""
return ket2dm(ghz_state(N))
##### Continuous-variable (optical) quantum states #####
### State vectors ###
[docs]
def cat_state(N: int, alpha: float, positive: bool=True, dim=None) -> Qobj:
"""
Generates a cat state (superposition of coherent states).
Parameters
----
N : int
Hilbert space dimensionality.
alpha : float
Coherent state parameter.
positive : bool
If True, generates a positive cat state (superposition of coherent states with the same phase). If False, generates a negative cat state (superposition of coherent states with opposite phases).
Returns
-------
Qobj
Cat state.
"""
if dim:
_deprecation_warning('dim', 'N')
N = dim
if not isinstance(N, int): raise TypeError("N must be an integer")
if not isinstance(positive, bool): raise TypeError("positive must be a boolean")
if positive:
return (coherent(N, alpha) + coherent(N, -alpha)).unit()
else:
return (coherent(N, alpha) - coherent(N, -alpha)).unit()
[docs]
def num_state(state: str, N: int=None, state_index: int=None, dim=None) -> Qobj:
"""
Generates a 'num state' (specific superposition of Fock states numerically optimized for quantum error correction).
Parameters
----
state : str
Type of num state to generate. Must be one of '17', 'M', 'P', 'P2', or 'M2'.
N : int
Hilbert space dimensionality. If None, the dimension of the state vector will be used.
state_index : int
Index of the state to generate. If None, a random state will be generated.
Returns
-------
Qobj
'Num state'.
"""
if dim:
_deprecation_warning('dim', 'N')
N = dim
if not isinstance(N, int): raise TypeError("N must be an integer.")
if state == '17':
coeffs = states17
elif state == 'M':
coeffs = statesM
elif state == 'P':
coeffs = statesP
elif state == 'P2':
coeffs = statesP2
elif state == 'M2':
coeffs = statesM2
else:
raise ValueError("state must be one of '17', 'M', 'P', 'P2', or 'M2'")
if state_index is not None:
if state_index < 0:
raise ValueError("state_index must be non-negative.")
elif state_index >= len(coeffs):
raise ValueError("state_index must be less than the number of states in the given type set.")
else:
state_index = random.randint(0, len(coeffs) - 1)
vector = coeffs[state_index]
if N is not None:
if N < len(vector):
raise ValueError("N must be greater than or equal to the length of the state vector.")
elif N > len(vector):
# Extend vector with zeros
vector = np.append(vector, np.zeros((N - len(vector), 1)), axis=0)
return Qobj(vector).unit()
[docs]
def binomial_state(dim: int, S: int, N: int, mu: int, Nc=None) -> Qobj:
"""
Generates a binomial superposition of Fock states.
Parameters
----
dim : int
Hilbert space dimensionality.
S : int
Coherent state parameter.
N : int
Number of excitations.
mu : int
Logical encoding parameter.
Returns
-------
Qobj
Binomial state in the Nc-dimensional Hilbert space.
"""
if not dim:
if Nc:
dim = Nc
_deprecation_warning('Nc', 'dim')
else:
raise ValueError("dim must be specified.")
if not isinstance(dim, int): raise TypeError("dim must be an integer.")
if S < 1 or S > 10:
raise ValueError("S must be between 1 and 10.")
# if N < 2 or N > (dim // (S + 1))-1: Ignored for now, will be reintroduced in a future version
# raise ValueError("N must be between 2 and dim/(S+1) - 1")
return sum([(-1 ** (mu*m)) * np.sqrt(scipy.special.binom(N+1, m)) * fock(dim, (S+1)*m) for m in range(N+1)]).unit()
[docs]
def gkp_state(N: int, n1_range: list[int, int], n2_range: list[int, int], delta: float, mu: int, dim=None) -> Qobj:
"""
Generates a Gottesman-Kitaev-Preskill (GKP) state.
Parameters
----
N : int
Hilbert space dimensionality.
n1_range : list
Grid parameter 1.
n2_range : int
Grid parameter 2.
delta : float
Real normalisation parameter.
mu : int
Logical encoding parameter.
Returns
-------
Qobj
GKP state.
"""
if dim:
_deprecation_warning('dim', 'N')
N = dim
if not isinstance(N, int): raise TypeError("N must be an integer.")
_range_error(n1_range, integers=True, positive=False)
_range_error(n2_range, integers=True, positive=False)
# if not isinstance(mu, int): raise TypeError("mu must be an integer.") Ignored for now, may be reintroduced in a future version
grid = np.mgrid[n1_range[0]:n1_range[1]+1, n2_range[0]:n2_range[1]+1].reshape(2, -1)
alphas = np.sqrt(np.pi/2) * ((2 * grid[0] + mu) + 1j * grid[1])
weights = np.exp(-delta**2 * np.abs(alphas)**2 - 1j * alphas.real * alphas.imag)
return sum(weights[i] * coherent(N, alphas[i]) for i in range(alphas.size)).unit()
### Density matrices ###
[docs]
def cat_dm(N: int, alpha: float, positive: bool=True, dim=None) -> Qobj:
"""
Generates a density matrix for a cat state (superposition of coherent states).
Parameters
----
N : int
Hilbert space dimensionality.
alpha : float
Coherent state parameter.
positive : bool
If True, generates a positive cat state (superposition of coherent states with the same phase). If False, generates a negative cat state (superposition of coherent states with opposite phases).
Returns
-------
Qobj
Density matrix for a cat state.
"""
if dim:
_deprecation_warning('dim', 'N')
N = dim
return ket2dm(cat_state(N, alpha, positive))
[docs]
def num_dm(state: str, N=None, state_index=None, dim=None) -> Qobj:
"""
Generates a density matrix for a 'num state' (specific superposition of Fock states numerically optimized for quantum error correction).
Parameters
----
state : str
Type of 'num state' to generate. Must be one of '17', 'M', 'P', 'P2', or 'M2'.
N : int
Hilbert space dimensionality. If None, the dimension of the state vector will be used.
state_index : int
Index of the state to generate. If None, a random state will be generated.
Returns
-------
Qobj
Density matrix for a 'num state'.
"""
if dim:
_deprecation_warning('dim', 'N')
N = dim
return ket2dm(num_state(state, N, state_index))
[docs]
def binomial_dm(dim: int, S: int, N: int, mu: int, Nc=None) -> Qobj:
"""
Generates a density matrix for a binomial superposition of Fock states.
Parameters
----
dim : int
Hilbert space dimensionality.
S : int
Coherent state parameter.
N : int
Number of excitations.
mu : int
Logical encoding parameter.
Returns
-------
Qobj
Density matrix for a binomial state in the Nc-dimensional Hilbert space.
"""
if Nc:
_deprecation_warning('Nc', 'dim')
dim = Nc
return ket2dm(binomial_state(dim, S, N, mu))
[docs]
def gkp_dm(N: int, n1_range: list[int, int], n2_range: list[int, int], delta: float, mu: int, dim=None) -> Qobj:
"""
Generates a density matrix for a Gottesman-Kitaev-Preskill (GKP) state.
Parameters
----
N : int
Hilbert space dimensionality.
n1_range : list
Grid parameter 1.
n2_range : int
Grid parameter 2.
delta : float
Real normalisation parameter.
mu : int
Logical encoding parameter.
Returns
-------
Qobj
Density matrix for a GKP state.
"""
if dim:
_deprecation_warning('dim', 'N')
N = dim
return ket2dm(gkp_state(N, n1_range, n2_range, delta, mu))