import copy
import itertools
from functools import reduce
from operator import add
from typing import List, Tuple, Optional
import numpy as np
from scipy.sparse import csr_matrix
import quara.utils.matrix_util as mutil
from quara.utils.matrix_util import vdot
from quara.objects.composite_system import CompositeSystem, ElementalSystem
from quara.objects.matrix_basis import (
SparseMatrixBasis,
MatrixBasis,
get_normalized_pauli_basis,
)
from quara.settings import Settings
from quara.objects.qoperation import QOperation
[docs]class Gate(QOperation):
def __init__(
self,
c_sys: CompositeSystem,
hs: np.ndarray,
is_physicality_required: bool = True,
is_estimation_object: bool = True,
on_para_eq_constraint: bool = True,
on_algo_eq_constraint: bool = True,
on_algo_ineq_constraint: bool = True,
mode_proj_order: str = "eq_ineq",
eps_proj_physical: float = None,
eps_truncate_imaginary_part: float = None,
):
"""Constructor
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
hs : np.ndarray
HS representation of this gate.
is_physicality_required : bool, optional
checks whether the gate is physically wrong, by default True.
if at least one of the following conditions is ``False``, the gate is physically wrong:
- gate is TP(trace-preserving map).
- gate is CP(Complete-Positivity-Preserving).
If you want to ignore the above requirements and create a Gate object, set ``is_physicality_required`` to ``False``.
Raises
------
ValueError
HS representation is not square matrix.
ValueError
dim of HS representation is not square number.
ValueError
HS representation is not real matrix.
ValueError
dim of HS representation does not equal dim of CompositeSystem.
ValueError
``is_physicality_required`` is ``True`` and the gate is not physically correct.
"""
super().__init__(
c_sys=c_sys,
is_physicality_required=is_physicality_required,
is_estimation_object=is_estimation_object,
on_para_eq_constraint=on_para_eq_constraint,
on_algo_eq_constraint=on_algo_eq_constraint,
on_algo_ineq_constraint=on_algo_ineq_constraint,
mode_proj_order=mode_proj_order,
eps_proj_physical=eps_proj_physical,
eps_truncate_imaginary_part=eps_truncate_imaginary_part,
)
self._hs: np.ndarray = hs
# whether HS representation is square matrix
size = self._hs.shape
if size[0] != size[1]:
raise ValueError(f"HS must be square matrix. size of HS is {size}")
# whether dim of HS representation is square number
self._dim: int = int(np.sqrt(size[0]))
if self._dim ** 2 != size[0]:
raise ValueError(f"dim of HS must be square number. dim of HS is {size[0]}")
# whether HS representation is real matrix
if self._hs.dtype != np.float64:
raise ValueError(f"HS must be real matrix. dtype of HS is {self._hs.dtype}")
# whether dim of HS equals dim of CompositeSystem
if self._dim != self.composite_system.dim:
raise ValueError(
f"dim of HS must equal dim of CompositeSystem. dim of HS is {self._dim}. dim of CompositeSystem is {self.composite_system.dim}"
)
# whether the gate is physically correct
if self.is_physicality_required and not self.is_physical():
raise ValueError("the gate is not physically correct.")
def _info(self):
info = {}
info["Type"] = self.__class__.__name__
info["Dim"] = self.dim
info["HS"] = self.hs
return info
@property
def dim(self):
"""returns dim of gate.
Returns
-------
int
dim of gate.
"""
return self._dim
@property
def hs(self):
"""returns HS representation of gate.
Returns
-------
np.ndarray
HS representation of gate.
"""
return self._hs
[docs] def is_eq_constraint_satisfied(self, atol: float = None):
return self.is_tp(atol)
[docs] def is_ineq_constraint_satisfied(self, atol: float = None):
return self.is_cp(atol)
[docs] def set_zero(self):
self._hs = np.zeros(self._hs.shape, dtype=np.float64)
self._is_physicality_required = False
def _generate_zero_obj(self):
new_hs = np.zeros(self.hs.shape, dtype=np.float64)
return new_hs
def _generate_origin_obj(self):
size = self.hs.shape
new_hs = np.zeros(size)
new_hs[0][0] = 1
return new_hs
[docs] def to_var(self) -> np.ndarray:
return convert_hs_to_var(
c_sys=self.composite_system,
hs=self.hs,
on_para_eq_constraint=self.on_para_eq_constraint,
)
[docs] def to_stacked_vector(self) -> np.ndarray:
return self.hs.flatten()
def _embed_qoperation_from_qutrits_to_qubits(
self, perm_matrix, c_sys_qubits
) -> QOperation:
num_qutrits = self.composite_system.num_e_sys
mats_qutrits = self.to_kraus_matrices()
coeff = 1 / np.sqrt(len(mats_qutrits))
# calc matrices for qubits
mats_qubits = []
for mat_qutrits in mats_qutrits:
mat_qubits = QOperation._calc_matrix_from_qutrits_to_qubits(
num_qutrits, perm_matrix, mat_qutrits, coeff
)
mats_qubits.append(mat_qubits)
# gerenera qoperation for qubits
hs = to_hs_from_kraus_matrices(
c_sys_qubits,
mats_qubits,
eps_truncate_imaginary_part=self.eps_truncate_imaginary_part,
)
new_qope = Gate(
c_sys_qubits,
hs,
is_physicality_required=self.is_physicality_required,
is_estimation_object=self.is_estimation_object,
on_para_eq_constraint=self.on_para_eq_constraint,
on_algo_eq_constraint=self.on_algo_eq_constraint,
on_algo_ineq_constraint=self.on_algo_ineq_constraint,
mode_proj_order=self.mode_proj_order,
eps_proj_physical=self.eps_proj_physical,
eps_truncate_imaginary_part=self.eps_truncate_imaginary_part,
)
return new_qope
[docs] def calc_gradient(self, var_index: int) -> "Gate":
gate = calc_gradient_from_gate(
self.composite_system,
self.hs,
var_index,
is_estimation_object=self.is_estimation_object,
on_para_eq_constraint=self.on_para_eq_constraint,
on_algo_eq_constraint=self.on_algo_eq_constraint,
on_algo_ineq_constraint=self.on_algo_ineq_constraint,
mode_proj_order=self.mode_proj_order,
eps_proj_physical=self.eps_proj_physical,
eps_truncate_imaginary_part=self.eps_truncate_imaginary_part,
)
return gate
[docs] def calc_proj_eq_constraint(self) -> "Gate":
hs = copy.deepcopy(self.hs)
hs[0][0] = 1
hs[0][1:] = 0
new_gate = Gate(
c_sys=self.composite_system,
hs=hs,
is_physicality_required=self.is_physicality_required,
is_estimation_object=self.is_estimation_object,
on_para_eq_constraint=self.on_para_eq_constraint,
on_algo_eq_constraint=self.on_algo_eq_constraint,
on_algo_ineq_constraint=self.on_algo_ineq_constraint,
mode_proj_order=self.mode_proj_order,
eps_proj_physical=self.eps_proj_physical,
eps_truncate_imaginary_part=self.eps_truncate_imaginary_part,
)
return new_gate
[docs] @staticmethod
def calc_proj_eq_constraint_with_var(
c_sys: CompositeSystem,
var: np.ndarray,
on_para_eq_constraint: bool = True,
) -> np.ndarray:
"""calculates the projection of Gate on equal constraint.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this variables.
var : np.ndarray
variables.
on_para_eq_constraint : bool, optional
whether this variables is on parameter equality constraint, by default True.
Returns
-------
np.ndarray
the projection of Gate on equal constraint.
"""
if on_para_eq_constraint:
new_var = var
else:
new_var = copy.deepcopy(var)
new_var[0] = 1
new_var[1 : c_sys.dim ** 2] = 0
return new_var
[docs] def calc_proj_ineq_constraint(self) -> "Gate":
# calc engenvalues and engenvectors
choi_matrix = self.to_choi_matrix_with_sparsity()
eigenvals, eigenvecs = np.linalg.eigh(choi_matrix)
# project
diag = np.diag(eigenvals)
diag[diag < 0] = 0
# calc new HS
new_choi_matrix = eigenvecs @ diag @ eigenvecs.T.conjugate()
new_hs = to_hs_from_choi_with_sparsity(self.composite_system, new_choi_matrix)
# create new Gate
new_gate = Gate(
c_sys=self.composite_system,
hs=new_hs,
is_physicality_required=self.is_physicality_required,
is_estimation_object=self.is_estimation_object,
on_para_eq_constraint=self.on_para_eq_constraint,
on_algo_eq_constraint=self.on_algo_eq_constraint,
on_algo_ineq_constraint=self.on_algo_ineq_constraint,
mode_proj_order=self.mode_proj_order,
eps_proj_physical=self.eps_proj_physical,
eps_truncate_imaginary_part=self.eps_truncate_imaginary_part,
)
return new_gate
[docs] @staticmethod
def calc_proj_ineq_constraint_with_var(
c_sys: CompositeSystem,
var: np.ndarray,
on_para_eq_constraint: bool = True,
eps_truncate_imaginary_part: float = None,
) -> np.ndarray:
"""calculates the projection of Gate on inequal constraint.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this variables.
var : np.ndarray
variables.
on_para_eq_constraint : bool, optional
whether this variables is on parameter equality constraint, by default True.
eps_truncate_imaginary_part : float, optional
threshold to truncate imaginary part, by default :func:`~quara.settings.Settings.get_atol`
Returns
-------
np.ndarray
the projection of Gate on equal constraint.
"""
# calc engenvalues and engenvectors
choi_matrix = to_choi_from_var(c_sys, var, on_para_eq_constraint)
eigenvals, eigenvecs = np.linalg.eigh(choi_matrix)
# project
diag = np.diag(eigenvals)
diag[diag < 0] = 0
# calc new HS
new_choi_matrix = eigenvecs @ diag @ eigenvecs.T.conjugate()
new_hs = to_hs_from_choi_with_sparsity(
c_sys,
new_choi_matrix,
eps_truncate_imaginary_part=eps_truncate_imaginary_part,
)
# HS to var
new_var = convert_hs_to_var(c_sys, new_hs, on_para_eq_constraint)
return new_var
def _add_vec(self, other) -> np.ndarray:
new_hs = self.hs + other.hs
return new_hs
def _sub_vec(self, other) -> np.ndarray:
new_hs = self.hs - other.hs
return new_hs
def _mul_vec(self, other):
new_hs = self.hs * other
return new_hs
def _truediv_vec(self, other):
new_hs = self.hs / other
return new_hs
[docs] def get_basis(self) -> MatrixBasis:
"""returns MatrixBasis of gate.
Returns
-------
MatrixBasis
MatrixBasis of gate.
"""
return self.composite_system.basis()
[docs] def is_tp(self, atol: float = None) -> bool:
"""returns whether the gate is TP(trace-preserving map).
Parameters
----------
atol : float, optional
the absolute tolerance parameter, uses :func:`~quara.settings.Settings.get_atol` by default.
this function checks ``absolute(trace after mapped - trace before mapped) <= atol``.
Returns
-------
bool
True where the gate is TP, False otherwise.
"""
return is_tp(self.composite_system, self.hs, atol)
[docs] def is_cp(self, atol: float = None) -> bool:
"""returns whether gate is CP(Complete-Positivity-Preserving).
Parameters
----------
atol : float, optional
the absolute tolerance parameter, uses :func:`~quara.settings.Settings.get_atol` by default.
this function ignores eigenvalues close zero.
Returns
-------
bool
True where gate is CP, False otherwise.
"""
return is_cp(self.composite_system, self.hs, atol)
[docs] def convert_basis(self, other_basis: MatrixBasis) -> np.ndarray:
"""returns HS representation for ``other_basis``.
Parameters
----------
other_basis : MatrixBasis
basis.
Returns
-------
np.ndarray
HS representation for ``other_basis``.
"""
converted_hs = convert_hs(self.hs, self.composite_system.basis(), other_basis)
return converted_hs
[docs] def convert_to_comp_basis(self, mode: str = "row_major") -> np.ndarray:
"""returns HS representation for computational basis.
Parameters
----------
mode : str, optional
specify whether the order of basis is "row_major" or "column_major", by default "row_major".
Returns
-------
np.ndarray
HS representation for computational basis.
"""
converted_hs = convert_hs(
self.hs,
self.composite_system.basis(),
self.composite_system.comp_basis(mode=mode),
)
return converted_hs
[docs] def to_choi_matrix(self) -> np.ndarray:
"""returns Choi matrix of gate.
Returns
-------
np.ndarray
Choi matrix of gate.
"""
return to_choi_from_hs(self.composite_system, self.hs)
[docs] def to_choi_matrix_with_dict(self) -> np.ndarray:
"""returns Choi matrix of gate.
this function uses the scipy.sparse module.
Returns
-------
np.ndarray
Choi matrix of gate.
"""
return to_choi_from_hs_with_dict(self.composite_system, self.hs)
[docs] def to_choi_matrix_with_sparsity(self) -> np.ndarray:
"""returns Choi matrix of gate.
this function uses the scipy.sparse module.
Returns
-------
np.ndarray
Choi matrix of gate.
"""
return to_choi_from_hs_with_sparsity(self.composite_system, self._hs)
[docs] def to_kraus_matrices(self) -> List[np.ndarray]:
"""returns Kraus matrices of gate.
this function returns Kraus matrices as list of ``np.ndarray`` with ``dtype=np.complex128``.
the list is sorted large eigenvalue order.
if HS of gate is not CP, then returns empty list because Kraus matrices does not exist.
Returns
-------
List[np.ndarray]
Kraus matrices of gate.
"""
return to_kraus_matrices_from_hs(
self.composite_system, self.hs, self.eps_proj_physical
)
[docs] def to_process_matrix(self) -> np.ndarray:
"""returns process matrix of gate.
Returns
-------
np.ndarray
process matrix of gate.
"""
return to_process_matrix_from_hs(self.composite_system, self.hs)
def _generate_from_var_func(self):
return convert_var_to_gate
def _copy(self):
return copy.deepcopy(self.hs)
[docs] @staticmethod
def convert_var_to_stacked_vector(
c_sys: CompositeSystem,
var: np.ndarray,
on_para_eq_constraint: bool = True,
) -> np.ndarray:
"""converts variables of gate to stacked vector of gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
var : np.ndarray
variables of gate.
on_para_eq_constraint : bool, optional
uses equal constraints, by default True.
Returns
-------
np.ndarray
stacked vector of gate.
"""
if on_para_eq_constraint:
head = np.zeros(c_sys.dim ** 2)
head[0] = 1
stacked_vector = np.insert(var, 0, head)
else:
stacked_vector = var
return stacked_vector
[docs] @staticmethod
def convert_stacked_vector_to_var(
c_sys: CompositeSystem,
stacked_vector: np.ndarray,
on_para_eq_constraint: bool = True,
) -> np.ndarray:
"""converts stacked vector of gate to variables of gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
stacked_vector : np.ndarray
stacked vector of gate.
on_para_eq_constraint : bool, optional
uses equal constraints, by default True.
Returns
-------
np.ndarray
variables of gate.
"""
return (
np.delete(stacked_vector, np.s_[: c_sys.dim ** 2])
if on_para_eq_constraint
else stacked_vector
)
[docs]def is_tp(c_sys: CompositeSystem, hs: np.ndarray, atol: float = None) -> bool:
"""returns whether the gate is TP(trace-preserving map).
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
hs : np.ndarray
HS representation of this gate.
atol : float, optional
the absolute tolerance parameter, uses :func:`~quara.settings.Settings.get_atol` by default.
this function checks ``absolute(trace after mapped - trace before mapped) <= atol``.
Returns
-------
bool
True where the gate is TP, False otherwise.
"""
atol = Settings.get_atol() if atol is None else atol
if c_sys.is_orthonormal_hermitian_0thprop_identity is True:
# if A:HS representation of gate, then A:TP <=> the first row of A is [1, 0,..., 0].
expected_row = np.zeros((c_sys.dim ** 2))
expected_row[0] = 1
return np.allclose(hs[0], expected_row, atol=atol, rtol=0.0)
else:
# if A:HS representation of gate, then A:TP <=> Tr[A(B_\alpha)] = Tr[B_\alpha] for all basis.
for index, basis in enumerate(c_sys.basis()):
# calculate Tr[B_\alpha]
trace_before_mapped = basis.diagonal().sum()
# calculate Tr[A(B_\alpha)]
vec = np.zeros((c_sys.dim ** 2))
vec[index] = 1
vec_after_mapped = hs @ vec
density = np.zeros((c_sys.dim, c_sys.dim), dtype=np.complex128)
for coefficient, basis in zip(vec_after_mapped, c_sys.basis()):
density += coefficient * basis
trace_after_mapped = np.trace(density)
# check Tr[A(B_\alpha)] = Tr[B_\alpha]
tp_for_basis = np.isclose(
trace_after_mapped, trace_before_mapped, atol=atol, rtol=0.0
)
if not tp_for_basis:
return False
return True
[docs]def is_cp(c_sys: CompositeSystem, hs: np.ndarray, atol: float = None) -> bool:
"""returns whether gate is CP(Complete-Positivity-Preserving).
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
hs : np.ndarray
HS representation of this gate.
atol : float, optional
the absolute tolerance parameter, uses :func:`~quara.settings.Settings.get_atol` by default.
this function ignores eigenvalues close zero.
Returns
-------
bool
True where gate is CP, False otherwise.
"""
atol = Settings.get_atol() if atol is None else atol
# "A is CP" <=> "C(A) >= 0"
return mutil.is_positive_semidefinite(
to_choi_from_hs_with_sparsity(c_sys, hs), atol=atol
)
[docs]def to_choi_from_hs(c_sys: CompositeSystem, hs: np.ndarray) -> np.ndarray:
"""converts HS representation to Choi matrix of this gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
hs : np.ndarray
HS representation of this gate.
Returns
-------
np.ndarray
Choi matrix of this gate.
"""
# C(A) = \sum_{\alpha, \beta} HS(A)_{\alpha, \beta} B_\alpha \otimes \overline{B_\beta}
num_basis = len(c_sys.basis())
tmp_list = []
for alpha, beta in itertools.product(range(num_basis), range(num_basis)):
bb = c_sys.basis_basisconjugate((alpha, beta))
if type(bb) == csr_matrix:
bb = bb.toarray()
tmp = hs[alpha][beta] * bb
tmp_list.append(tmp)
# summing
choi = reduce(add, tmp_list)
return choi
[docs]def to_choi_from_hs_with_dict(c_sys: CompositeSystem, hs: np.ndarray) -> np.ndarray:
"""converts HS representation to Choi matrix of this gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
hs : np.ndarray
HS representation of this gate.
Returns
-------
np.ndarray
Choi matrix of this gate.
"""
num_basis = len(c_sys.basis())
choi = np.zeros((num_basis, num_basis), dtype=np.complex128)
for i, j in itertools.product(range(num_basis), range(num_basis)):
non_zeros = c_sys.dict_from_hs_to_choi.get((i, j), [])
for alpha, beta, coefficient in non_zeros:
choi[i, j] += hs[alpha, beta] * coefficient
return choi
[docs]def to_choi_from_hs_with_sparsity(c_sys: CompositeSystem, hs: np.ndarray) -> np.ndarray:
"""converts HS representation to Choi matrix of this gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
hs : np.ndarray
HS representation of this gate.
Returns
-------
np.ndarray
Choi matrix of this gate.
"""
choi_vec = c_sys.basis_basisconjugate_T_sparse.dot(hs.flatten())
choi = choi_vec.reshape((c_sys.dim ** 2, c_sys.dim ** 2))
return choi
[docs]def to_hs_from_choi(c_sys: CompositeSystem, choi: np.ndarray) -> np.ndarray:
"""converts Choi matrix to HS representation of this gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
choi : np.ndarray
Choi matrix of this gate.
Returns
-------
np.ndarray
HS representation of this gate.
"""
num_basis = len(c_sys.basis().basis)
hs = np.zeros((num_basis, num_basis), dtype=np.float64)
for alpha, beta in itertools.product(range(num_basis), range(num_basis)):
b_bc = c_sys.basis_basisconjugate((alpha, beta))
b_bc_dag = np.conjugate(b_bc.T)
tr = (b_bc_dag @ choi).diagonal().sum()
hs[alpha, beta] = tr.real.astype(np.float64)
return hs
[docs]def to_hs_from_choi_with_dict(
c_sys: CompositeSystem, choi: np.ndarray, eps_truncate_imaginary_part: float = None
) -> np.ndarray:
"""converts Choi matrix to HS representation of this gate.
this function uses dict to calculate fast.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
choi : np.ndarray
Choi matrix of this gate.
eps_truncate_imaginary_part : float, optional
threshold to truncate imaginary part, by default :func:`~quara.settings.Settings.get_atol`
Returns
-------
np.ndarray
HS representation of this gate.
"""
num_basis = len(c_sys.basis())
hs = np.zeros((num_basis, num_basis), dtype=np.complex128)
for alpha, beta in itertools.product(range(num_basis), range(num_basis)):
non_zeros = c_sys.dict_from_choi_to_hs.get((alpha, beta), [])
for i, j, coefficient in non_zeros:
hs[alpha, beta] += coefficient * choi[j, i]
return mutil.truncate_hs(
hs, eps_truncate_imaginary_part=eps_truncate_imaginary_part
)
[docs]def to_hs_from_choi_with_sparsity(
c_sys: CompositeSystem,
choi: np.ndarray,
eps_truncate_imaginary_part: float = None,
) -> np.ndarray:
"""converts Choi matrix to HS representation of this gate.
this function uses the scipy.sparse module.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
choi : np.ndarray
Choi matrix of this gate.
eps_truncate_imaginary_part : float, optional
threshold to truncate imaginary part, by default :func:`~quara.settings.Settings.get_atol`
Returns
-------
np.ndarray
HS representation of this gate.
"""
hs_vec = c_sys.basisconjugate_basis_sparse.dot(mutil.flatten(choi))
hs = hs_vec.reshape((c_sys.dim ** 2, c_sys.dim ** 2))
return mutil.truncate_hs(
hs, eps_truncate_imaginary_part=eps_truncate_imaginary_part
)
[docs]def to_kraus_matrices_from_hs(
c_sys: CompositeSystem, hs: np.ndarray, atol: float = None
) -> List[np.ndarray]:
"""returns Kraus matrices of gate.
this function returns Kraus matrices as list of ``np.ndarray`` with ``dtype=np.complex128``.
the list is sorted large eigenvalue order.
if HS of gate is not CP, then returns empty list because Kraus matrices does not exist.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
hs : np.ndarray
HS representation of this gate.
atol : float, optional
the absolute tolerance parameter, uses :func:`~quara.settings.Settings.get_atol` by default.
this function ignores eigenvalues close zero.
Returns
-------
List[np.ndarray]
Kraus matrices of gate.
"""
atol = Settings.get_atol() if atol is None else atol
if not is_cp(c_sys, hs, atol):
return []
# step1. calc the eigenvalue decomposition of Choi matrix.
# Choi = \sum_{\alpha} c_{\alpha} |c_{\alpha}><c_{\alpha}| s.t. c_{\alpha} are eigenvalues and |c_{\alpha}> are eigenvectors of orthogonal basis.
choi = to_choi_from_hs_with_sparsity(c_sys, hs)
eigen_vals, eigen_vecs = np.linalg.eigh(choi)
eigens = [
(eigen_vals[index], eigen_vecs[:, index]) for index in range(len(eigen_vals))
]
# filter non-zero eigen values
eigens = [
(eigen_val, eigen_vec)
for (eigen_val, eigen_vec) in eigens
if not np.isclose(eigen_val, 0, atol=Settings.get_atol())
]
# sort large eigenvalue order
eigens = sorted(eigens, key=lambda x: x[0], reverse=True)
# step2. calc Kraus representaion.
# K_{\alpha} = \sqrt{c_{\alpha}} unvec(|c_{\alpha}>)
_kraus = [
np.sqrt(eigen_val) * eigen_vec.reshape((c_sys.dim, c_sys.dim))
for (eigen_val, eigen_vec) in eigens
]
# step3: fix phase
kraus = []
for k in _kraus:
# k_00 = k[0][0]
# ang = np.angle(k_00)
# _k = (np.e ** (-1j * ang)) * k
# kraus.append(_k)
for i, value in enumerate(k.flatten()):
if value == 0:
continue
elif value < 0:
e_i_theta = value / abs(value)
_k = (1 / e_i_theta) * k
# _k = (np.e ** (-1j * ang)) * k
kraus.append(_k)
break
else:
kraus.append(k)
break
else:
kraus.append(k)
return kraus
[docs]def to_hs_from_kraus_matrices(
c_sys: CompositeSystem,
kraus: List[np.ndarray],
eps_truncate_imaginary_part: float = None,
) -> np.ndarray:
"""returns HS representation of this gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
kraus : List[np.ndarray]
Kraus matrices of gate.
eps_truncate_imaginary_part : float, optional
threshold to truncate imaginary part, by default :func:`~quara.settings.Settings.get_atol`
Returns
-------
np.ndarray
HS representation of this gate.
"""
kraus_tensor = [np.kron(mat, mat.conjugate()) for mat in kraus]
hs_cb = sum(kraus_tensor)
hs = convert_hs(hs_cb, c_sys.comp_basis(), c_sys.basis())
return mutil.truncate_hs(
hs, eps_truncate_imaginary_part=eps_truncate_imaginary_part
)
[docs]def to_process_matrix_from_hs(
c_sys: CompositeSystem,
hs: np.ndarray,
) -> np.ndarray:
"""returns process matrix of gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
hs : np.ndarray
HS representation of this gate.
Returns
-------
np.ndarray
process matrix of gate.
"""
# \chi_{\alpha, \beta}(A) = Tr[(B_{\alpha}^{\dagger} \otimes B_{\beta}^T) HS(A)] for computational basis.
comp_basis = c_sys.comp_basis()
hs_comp = convert_hs(hs, c_sys.basis(), comp_basis)
process_matrix = [
(mutil.kron(B_alpha.conj().T, B_beta.T) @ hs_comp).diagonal().sum()
for B_alpha, B_beta in itertools.product(comp_basis, comp_basis)
]
return np.array(process_matrix).reshape((c_sys.dim ** 2, c_sys.dim ** 2))
[docs]def to_choi_from_var(
c_sys: CompositeSystem,
var: np.ndarray,
on_para_eq_constraint: bool = True,
) -> np.ndarray:
"""converts variables to Choi matrix.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
var : np.ndarray
variables.
on_para_eq_constraint : bool, optional
whether this gate is on parameter equality constraint, by default True
Returns
-------
np.ndarray
Choi matrix of this gate.
"""
# var to hs
hs = convert_var_to_hs(c_sys, var, on_para_eq_constraint)
# hs to Choi matrix
choi = to_choi_from_hs_with_sparsity(c_sys, hs)
return choi
[docs]def to_var_from_choi(
c_sys: CompositeSystem,
choi: np.ndarray,
on_para_eq_constraint: bool = True,
) -> np.ndarray:
"""converts Choi matrix to variables.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
choi : np.ndarray
Choi matrix of this gate.
on_para_eq_constraint : bool, optional
whether this gate is on parameter equality constraint, by default True
Returns
-------
np.ndarray
variables.
"""
# Choi matrix to hs
hs = to_choi_from_hs_with_sparsity(c_sys, choi)
# hs to var
var = convert_hs_to_var(c_sys, hs, on_para_eq_constraint)
return var
[docs]def convert_var_index_to_gate_index(
c_sys: CompositeSystem, var_index: int, on_para_eq_constraint: bool = True
) -> Tuple[int, int]:
"""converts variable index to gate index.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
var_index : int
variable index.
on_para_eq_constraint : bool, optional
uses equal constraints, by default True.
Returns
-------
Tuple[int, int]
gate index.
first value of tuple is row number of HS representation of this gate.
second value of tuple is column number of HS representation of this gate.
"""
dim = c_sys.dim
(row, col) = divmod(var_index, dim ** 2)
if on_para_eq_constraint:
row += 1
return (row, col)
[docs]def convert_gate_index_to_var_index(
c_sys: CompositeSystem,
gate_index: Tuple[int, int],
on_para_eq_constraint: bool = True,
) -> int:
"""converts gate index to variable index.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
gate_index : Tuple[int, int]
gate index.
first value of tuple is row number of HS representation of this gate.
second value of tuple is column number of HS representation of this gate.
on_para_eq_constraint : bool, optional
uses equal constraints, by default True.
Returns
-------
int
variable index.
"""
dim = c_sys.dim
(row, col) = gate_index
var_index = (
(dim ** 2) * (row - 1) + col
if on_para_eq_constraint
else (dim ** 2) * row + col
)
return var_index
[docs]def convert_var_to_hs(
c_sys: CompositeSystem,
var: np.ndarray,
on_para_eq_constraint: bool = True,
) -> np.ndarray:
"""converts variables of gate to HS representation.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
var : np.ndarray
variables of gate.
on_para_eq_constraint : bool, optional
uses equal constraints, by default True.
Returns
-------
np.ndarray
HS representation of this gate.
"""
dim = c_sys.dim
size = (dim ** 2 - 1, dim ** 2) if on_para_eq_constraint else (dim ** 2, dim ** 2)
reshaped = var.reshape(size)
hs = (
np.insert(reshaped, 0, np.eye(1, dim ** 2), axis=0)
if on_para_eq_constraint
else reshaped
)
return hs
[docs]def convert_var_to_gate(
c_sys: CompositeSystem,
var: np.ndarray,
is_physicality_required: bool = True,
is_estimation_object: bool = True,
on_para_eq_constraint: bool = True,
on_algo_eq_constraint: bool = True,
on_algo_ineq_constraint: bool = True,
mode_proj_order: str = "eq_ineq",
eps_proj_physical: float = None,
eps_truncate_imaginary_part: float = None,
) -> Gate:
"""converts vec of variables to gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
var : np.ndarray
vec of variables.
on_para_eq_constraint : bool, optional
uses equal constraints, by default True.
Returns
-------
Gate
converted gate.
"""
hs = convert_var_to_hs(c_sys, var, on_para_eq_constraint)
gate = Gate(
c_sys,
hs,
is_physicality_required=is_physicality_required,
is_estimation_object=is_estimation_object,
on_para_eq_constraint=on_para_eq_constraint,
on_algo_eq_constraint=on_algo_eq_constraint,
on_algo_ineq_constraint=on_algo_ineq_constraint,
mode_proj_order=mode_proj_order,
eps_proj_physical=eps_proj_physical,
eps_truncate_imaginary_part=eps_truncate_imaginary_part,
)
return gate
[docs]def convert_hs_to_var(
c_sys: CompositeSystem, hs: np.ndarray, on_para_eq_constraint: bool = True
) -> np.ndarray:
"""converts hs of gate to vec of variables.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
hs : np.ndarray
HS representation of this gate.
on_para_eq_constraint : bool, optional
uses equal constraints, by default True.
Returns
-------
np.ndarray
vec of variables.
"""
var = np.delete(hs, 0, axis=0).flatten() if on_para_eq_constraint else hs.flatten()
return var
[docs]def calc_gradient_from_gate(
c_sys: CompositeSystem,
hs: np.ndarray,
var_index: int,
is_estimation_object: bool = True,
on_para_eq_constraint: bool = True,
on_algo_eq_constraint: bool = True,
on_algo_ineq_constraint: bool = True,
mode_proj_order: str = "eq_ineq",
eps_proj_physical: float = None,
eps_truncate_imaginary_part: float = None,
) -> Gate:
"""calculates gradient from gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem of this gate.
hs : np.ndarray
HS representation of this gate.
var_index : int
variable index.
on_para_eq_constraint : bool, optional
uses equal constraints, by default True.
Returns
-------
Gate
Gate with gradient as hs.
"""
gradient = np.zeros((c_sys.dim ** 2, c_sys.dim ** 2), dtype=np.float64)
gate_index = convert_var_index_to_gate_index(
c_sys, var_index, on_para_eq_constraint
)
gradient[gate_index] = 1
gate = Gate(
c_sys,
gradient,
is_physicality_required=False,
is_estimation_object=is_estimation_object,
on_para_eq_constraint=on_para_eq_constraint,
on_algo_eq_constraint=on_algo_eq_constraint,
on_algo_ineq_constraint=on_algo_ineq_constraint,
mode_proj_order=mode_proj_order,
eps_proj_physical=eps_proj_physical,
eps_truncate_imaginary_part=eps_truncate_imaginary_part,
)
return gate
[docs]def is_hp(hs: np.ndarray, basis: MatrixBasis, atol: float = None) -> bool:
"""returns whether gate is HP(Hermiticity-Preserving).
HP <=> HS on Hermitian basis is real matrix.
therefore converts input basis to Pauli basis, and checks whetever converted HS is real matrix.
Parameters
----------
hs : np.ndarray
HS representation of gate.
basis : MatrixBasis
basis of HS representation.
atol : float, optional
the absolute tolerance parameter, uses :func:`~quara.settings.Settings.get_atol` by default.
this function checks ``absolute(imaginary part of matrix - zero matrix) <= atol``.
Returns
-------
bool
True where gate is EP, False otherwise.
"""
atol = Settings.get_atol() if atol is None else atol
# convert Hermitian basis(Pauli basis)
hs_converted = convert_hs(hs, basis, get_normalized_pauli_basis())
# whetever converted HS is real matrix(imaginary part is zero matrix)
zero_matrix = np.zeros(hs_converted.shape)
return np.allclose(hs_converted.imag, zero_matrix, atol=atol, rtol=0.0)
[docs]def calc_agf(g: Gate, u: Gate) -> np.float64:
"""returns AGF(Average Gate Fidelity) and ``g`` and ``u``.
Parameters
----------
g : Gate
L-TP-CP map.
u : Gate
unitary gate.
Returns
-------
np.float64
AGF.
Raises
------
ValueError
HS representation of ``u`` is not Hermitian.
"""
# check type
if type(g) != Gate or type(u) != Gate:
raise ValueError(
f"type of g and u must be Gate. type of g={type(g)}, type of u={type(u)}"
)
# u: unitary gate <=> HS(u) is unitary
# whetever HS(u) is unitary
if not mutil.is_unitary(u.hs):
raise ValueError("gate u must be unitary")
# let trace = Tr[HS(u)^{\dagger}HS(g)]
# AGF = 1-\frac{d^2-trace}{d(d+1)}
d = u.dim
trace = np.vdot(u.hs, g.hs)
agf = 1 - (d ** 2 - trace) / (d * (d + 1))
return agf
[docs]def convert_hs(
from_hs: np.ndarray, from_basis: MatrixBasis, to_basis: MatrixBasis
) -> np.ndarray:
"""returns HS representation for ``to_basis``
Parameters
----------
from_hs : np.ndarray
HS representation before convert.
from_basis : MatrixBasis
basis before convert.
to_basis : MatrixBasis
basis after convert.
Returns
-------
np.ndarray
HS representation for ``to_basis``.
Raises
------
ValueError
``from_hs`` is not square matrix.
ValueError
dim of ``from_hs`` is not square number.
ValueError
dim of ``from_basis`` does not equal dim of ``to_basis``.
ValueError
length of ``from_basis`` does not equal length of ``to_basis``.
"""
### parameter check
# whether HS is square matrix
size = from_hs.shape
if size[0] != size[1]:
raise ValueError(f"HS must be square matrix. size of HS is {size}")
# whether dim of HS is square number
dim: int = int(np.sqrt(size[0]))
if dim ** 2 != size[0]:
raise ValueError(f"dim of HS must be square number. dim of HS is {size[0]}")
# whether dim of from_basis equals dim of to_basis
if from_basis.dim != to_basis.dim:
raise ValueError(
f"dim of from_basis must equal dim of to_basis. dim of from_basis is {from_basis.dim}. dim of to_basis is {to_basis.dim}"
)
# whether length of from_basis equals length of to_basis
if len(from_basis) != len(to_basis):
raise ValueError(
f"length of from_basis must equal length of to_basis. length of from_basis is {len(from_basis)}. length of to_basis is {len(to_basis)}"
)
### main logic
# U_{\alpha,\bata} := Tr[to_basis_{\alpha}^{\dagger} @ from_basis_{\beta}]
trans_matrix = [
vdot(B_alpha, B_beta)
for B_alpha, B_beta in itertools.product(to_basis, from_basis)
]
U = np.array(trans_matrix).reshape(from_basis.dim ** 2, from_basis.dim ** 2)
to_hs = U @ from_hs @ U.conj().T
return to_hs
def _get_1q_gate_from_hs_on_pauli_basis(
matrix: np.ndarray, c_sys: CompositeSystem
) -> Gate:
# whether dim of CompositeSystem equals 2
if c_sys.dim != 2:
raise ValueError(
f"dim of CompositeSystem must equals 2. dim of CompositeSystem is {c_sys.dim}"
)
# convert "HS representation in Pauli basis" to "HS representation in basis of CompositeSystem"
hs = convert_hs(matrix, get_normalized_pauli_basis(), c_sys.basis())
gate = Gate(c_sys, hs.real.astype(np.float64))
return gate
[docs]def get_i(c_sys: CompositeSystem) -> Gate:
"""returns identity gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem containing gate.
Returns
-------
Gate
identity gate.
"""
hs = np.eye(c_sys.dim ** 2, dtype=np.float64)
gate = Gate(c_sys, hs)
return gate
[docs]def get_x(c_sys: CompositeSystem) -> Gate:
"""returns Pauli X gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem containing gate.
Returns
-------
Gate
Pauli X gate.
Raises
------
ValueError
CompositeSystem is not 1quit.
ValueError
dim of CompositeSystem does not equal 2
"""
# whether CompositeSystem is 1 qubit
size = len(c_sys._elemental_systems)
if size != 1:
raise ValueError(f"CompositeSystem must be 1 qubit. it is {size} qubits")
matrix = np.array(
[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, -1, 0], [0, 0, 0, -1]], dtype=np.float64
)
gate = _get_1q_gate_from_hs_on_pauli_basis(matrix, c_sys)
return gate
[docs]def get_y(c_sys: CompositeSystem) -> Gate:
"""returns Pauli Y gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem containing gate.
Returns
-------
Gate
Pauli Y gate.
Raises
------
ValueError
CompositeSystem is not 1quit.
ValueError
dim of CompositeSystem does not equal 2
"""
# whether CompositeSystem is 1 qubit
size = len(c_sys._elemental_systems)
if size != 1:
raise ValueError(f"CompositeSystem must be 1 qubit. it is {size} qubits")
matrix = np.array(
[[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]], dtype=np.float64
)
gate = _get_1q_gate_from_hs_on_pauli_basis(matrix, c_sys)
return gate
[docs]def get_z(c_sys: CompositeSystem) -> Gate:
"""returns Pauli Z gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem containing gate.
Returns
-------
Gate
Pauli Z gate.
Raises
------
ValueError
CompositeSystem is not 1quit.
ValueError
dim of CompositeSystem does not equal 2
"""
# whether CompositeSystem is 1 qubit
size = len(c_sys._elemental_systems)
if size != 1:
raise ValueError(f"CompositeSystem must be 1 qubit. it is {size} qubits")
matrix = np.array(
[[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]], dtype=np.float64
)
gate = _get_1q_gate_from_hs_on_pauli_basis(matrix, c_sys)
return gate
[docs]def get_h(c_sys: CompositeSystem) -> Gate:
"""returns H gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem containing gate.
Returns
-------
Gate
H gate.
Raises
------
ValueError
CompositeSystem is not 1quit.
ValueError
dim of CompositeSystem does not equal 2
"""
# whether CompositeSystem is 1 qubit
size = len(c_sys._elemental_systems)
if size != 1:
raise ValueError(f"CompositeSystem must be 1 qubit. it is {size} qubits")
matrix = np.array(
[[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, -1, 0], [0, 1, 0, 0]], dtype=np.float64
)
gate = _get_1q_gate_from_hs_on_pauli_basis(matrix, c_sys)
return gate
[docs]def get_root_x(c_sys: CompositeSystem) -> Gate:
"""returns root of X gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem containing gate.
Returns
-------
Gate
root of X gate.
Raises
------
ValueError
CompositeSystem is not 1quit.
ValueError
dim of CompositeSystem does not equal 2
"""
# whether CompositeSystem is 1 qubit
size = len(c_sys._elemental_systems)
if size != 1:
raise ValueError(f"CompositeSystem must be 1 qubit. it is {size} qubits")
matrix = np.array(
[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, -1], [0, 0, 1, 0]], dtype=np.float64
)
gate = _get_1q_gate_from_hs_on_pauli_basis(matrix, c_sys)
return gate
[docs]def get_root_y(c_sys: CompositeSystem) -> Gate:
"""returns root of Y gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem containing gate.
Returns
-------
Gate
root of Y gate.
Raises
------
ValueError
CompositeSystem is not 1quit.
ValueError
dim of CompositeSystem does not equal 2
"""
# whether CompositeSystem is 1 qubit
size = len(c_sys._elemental_systems)
if size != 1:
raise ValueError(f"CompositeSystem must be 1 qubit. it is {size} qubits")
matrix = np.array(
[[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, -1, 0, 0]], dtype=np.float64
)
gate = _get_1q_gate_from_hs_on_pauli_basis(matrix, c_sys)
return gate
[docs]def get_s(c_sys: CompositeSystem) -> Gate:
"""returns S gate(root of Z).
Parameters
----------
c_sys : CompositeSystem
CompositeSystem containing gate.
Returns
-------
Gate
S gate(root of Z).
Raises
------
ValueError
CompositeSystem is not 1quit.
ValueError
dim of CompositeSystem does not equal 2
"""
# whether CompositeSystem is 1 qubit
size = len(c_sys._elemental_systems)
if size != 1:
raise ValueError(f"CompositeSystem must be 1 qubit. it is {size} qubits")
matrix = np.array(
[[1, 0, 0, 0], [0, 0, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], dtype=np.float64
)
gate = _get_1q_gate_from_hs_on_pauli_basis(matrix, c_sys)
return gate
[docs]def get_sdg(c_sys: CompositeSystem) -> Gate:
"""returns dagger of S gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem containing gate.
Returns
-------
Gate
dagger of S gate.
Raises
------
ValueError
CompositeSystem is not 1quit.
ValueError
dim of CompositeSystem does not equal 2
"""
# whether CompositeSystem is 1 qubit
size = len(c_sys._elemental_systems)
if size != 1:
raise ValueError(f"CompositeSystem must be 1 qubit. it is {size} qubits")
matrix = np.array(
[[1, 0, 0, 0], [0, 0, 1, 0], [0, -1, 0, 0], [0, 0, 0, 1]], dtype=np.float64
)
gate = _get_1q_gate_from_hs_on_pauli_basis(matrix, c_sys)
return gate
[docs]def get_t(c_sys: CompositeSystem) -> Gate:
"""returns T gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem containing gate.
Returns
-------
Gate
T gate.
Raises
------
ValueError
CompositeSystem is not 1quit.
ValueError
dim of CompositeSystem does not equal 2
"""
# whether CompositeSystem is 1 qubit
size = len(c_sys._elemental_systems)
if size != 1:
raise ValueError(f"CompositeSystem must be 1 qubit. it is {size} qubits")
matrix = np.array(
[
[1, 0, 0, 0],
[0, 1 / np.sqrt(2), -1 / np.sqrt(2), 0],
[0, 1 / np.sqrt(2), 1 / np.sqrt(2), 0],
[0, 0, 0, 1],
],
dtype=np.float64,
)
gate = _get_1q_gate_from_hs_on_pauli_basis(matrix, c_sys)
return gate
[docs]def get_cnot(c_sys: CompositeSystem, control: ElementalSystem) -> Gate:
"""returns CNOT gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem containing gate.
control : ElementalSystem
ElementalSystem of control qubit.
Returns
-------
Gate
CNOT gate.
Raises
------
ValueError
CompositeSystem is not 2quits.
ValueError
dim of CompositeSystem does not equal 4.
"""
# whether CompositeSystem is 2 qubits
size = len(c_sys._elemental_systems)
if size != 2:
raise ValueError(f"CompositeSystem must be 2 qubits. it is {size} qubits")
# whether dim of CompositeSystem equals 4
if c_sys.dim != 4:
raise ValueError(
f"dim of CompositeSystem must equals 4. dim of CompositeSystem is {c_sys.dim}"
)
if control.name == c_sys.elemental_systems[0].name:
# control bit is 1st qubit
hs_comp_basis = np.array(
[
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
],
dtype=np.float64,
)
else:
# control bit is 2nd qubit
hs_comp_basis = np.array(
[
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
],
dtype=np.float64,
)
hs_for_c_sys = convert_hs(
hs_comp_basis, c_sys.comp_basis(), c_sys.basis()
).real.astype(np.float64)
gate = Gate(c_sys, hs_for_c_sys)
return gate
[docs]def get_cz(c_sys: CompositeSystem) -> Gate:
"""returns CZ gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem containing gate.
Returns
-------
Gate
CZ gate.
Raises
------
ValueError
CompositeSystem is not 2quits.
ValueError
dim of CompositeSystem does not equal 4.
"""
# whether CompositeSystem is 2 qubits
size = len(c_sys._elemental_systems)
if size != 2:
raise ValueError(f"CompositeSystem must be 2 qubits. it is {size} qubits")
# whether dim of CompositeSystem equals 4
if c_sys.dim != 4:
raise ValueError(
f"dim of CompositeSystem must equals 4. dim of CompositeSystem is {c_sys.dim}"
)
hs_comp_basis = np.array(
[
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
],
dtype=np.float64,
)
hs_for_c_sys = convert_hs(
hs_comp_basis, c_sys.comp_basis(), c_sys.basis()
).real.astype(np.float64)
gate = Gate(c_sys, hs_for_c_sys)
return gate
[docs]def get_swap(c_sys: CompositeSystem) -> Gate:
"""returns SWAP gate.
Parameters
----------
c_sys : CompositeSystem
CompositeSystem containing gate.
Returns
-------
Gate
SWAP gate.
Raises
------
ValueError
CompositeSystem is not 2quits.
ValueError
dim of CompositeSystem does not equal 4
"""
# whether CompositeSystem is 2 qubits
size = len(c_sys._elemental_systems)
if size != 2:
raise ValueError(f"CompositeSystem must be 2 qubits. it is {size} qubits")
# whether dim of CompositeSystem equals 4
if c_sys.dim != 4:
raise ValueError(
f"dim of CompositeSystem must equals 4. dim of CompositeSystem is {c_sys.dim}"
)
hs_comp_basis = np.array(
[
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
],
dtype=np.float64,
)
hs_for_c_sys = convert_hs(
hs_comp_basis, c_sys.comp_basis(), c_sys.basis()
).real.astype(np.float64)
gate = Gate(c_sys, hs_for_c_sys)
return gate
[docs]def get_depolarizing_channel(p: float, c_sys: Optional[CompositeSystem] = None) -> Gate:
if not (0 <= p <= 1):
message = "`p` must be between 0 and 1."
raise ValueError(message)
if not c_sys:
e_sys = ElementalSystem(0, get_normalized_pauli_basis())
c_sys = CompositeSystem([e_sys])
# 1, 1-p, 1-p, ... 1-p
source = np.array([1] + [1 - p] * (c_sys.dim ** 2 - 1), dtype=np.float64)
hs = np.diag(source)
gate = Gate(hs=hs, c_sys=c_sys)
return gate
[docs]def get_x_rotation(theta: float, c_sys: Optional[CompositeSystem] = None) -> Gate:
hs = np.array(
[
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, np.cos(theta), -np.sin(theta)],
[0, 0, np.sin(theta), np.cos(theta)],
],
dtype=np.float64,
)
if not c_sys:
e_sys = ElementalSystem(0, get_normalized_pauli_basis())
c_sys = CompositeSystem([e_sys])
gate = Gate(hs=hs, c_sys=c_sys)
return gate
[docs]def get_amplitutde_damping_channel(
gamma: float, c_sys: Optional[CompositeSystem] = None
) -> Gate:
hs = np.array(
[
[1, 0, 0, 0],
[0, np.sqrt(1 - gamma), 0, 0],
[0, 0, np.sqrt(1 - gamma), 0],
[gamma, 0, 0, 1 - gamma],
],
dtype=np.float64,
)
if not c_sys:
e_sys = ElementalSystem(0, get_normalized_pauli_basis())
c_sys = CompositeSystem([e_sys])
gate = Gate(hs=hs, c_sys=c_sys)
return gate