Gate and gate_typical
[1]:
import numpy as np
np.set_printoptions(linewidth=200)
Gate
hs in Quara. hs is a 2-dimensional numpy array.\(I/\sqrt{2} \mapsto X \cdot I/\sqrt{2} \cdot X^\dagger = I/\sqrt{2} = [1, 0, 0, 0]^T\) on basis \(B\).
\(X/\sqrt{2} \mapsto X \cdot X/\sqrt{2} \cdot X^\dagger = X/\sqrt{2} = [0, 1, 0, 0]^T\) on basis \(B\).
\(Y/\sqrt{2} \mapsto X \cdot Y/\sqrt{2} \cdot X^\dagger = -Y/\sqrt{2} = [0, 0, -1, 0]^T\) on basis \(B\).
\(Z/\sqrt{2} \mapsto X \cdot Z/\sqrt{2} \cdot X^\dagger = -Z/\sqrt{2} = [0, 0, 0, -1]^T\) on basis \(B\).
Therefore, the Hilbert-Schmidt matrix representation hs of \(X\) is \(\begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & -1 & 0 \\ 0 & 0 & 0 & -1 \end{bmatrix}\).
The methods for generating a Gate includes the following:
Generate from
gate_typicalmoduleGenerate Gate object directly
Generate from gate_typical module by specifying CompositeSystem and gate name (ex. “x”).
[2]:
from quara.objects.composite_system_typical import generate_composite_system
from quara.objects.gate_typical import generate_gate_from_gate_name
c_sys = generate_composite_system("qubit", 1)
gate = generate_gate_from_gate_name("x", c_sys)
print(gate)
Type:
Gate
Dim:
2
HS:
[[ 1. 0. 0. 0.]
[ 0. 1. 0. 0.]
[ 0. 0. -1. 0.]
[ 0. 0. 0. -1.]]
Generate Gate object directly using CompositeSystem and a numpy array.
[3]:
from quara.objects.composite_system import CompositeSystem
from quara.objects.elemental_system import ElementalSystem
from quara.objects.matrix_basis import get_normalized_pauli_basis
from quara.objects.gate import Gate
basis = get_normalized_pauli_basis(1)
e_sys = ElementalSystem(0, basis)
c_sys = CompositeSystem([e_sys])
hs = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, -1, 0], [0, 0, 0, -1]], dtype=np.float64)
gate = Gate(c_sys, hs)
print(gate)
Type:
Gate
Dim:
2
HS:
[[ 1. 0. 0. 0.]
[ 0. 1. 0. 0.]
[ 0. 0. -1. 0.]
[ 0. 0. 0. -1.]]
specific properties
The property hs of Gate is a 2-dimensional numpy array specified by the constructor argument hs.
[4]:
gate = Gate(c_sys, hs)
print(f"hs: \n{gate.hs}")
hs:
[[ 1. 0. 0. 0.]
[ 0. 1. 0. 0.]
[ 0. 0. -1. 0.]
[ 0. 0. 0. -1.]]
The property dim of Gate is the size of square matrix hs.
[5]:
print(f"dim: {gate.dim}")
print(f"size of square matrix hs: {int(np.sqrt(hs.shape[0]))}")
dim: 2
size of square matrix hs: 2
functions to check constraints
The is_eq_constraint_satisfied() function returns True, if and only if hs is TP(trace-preserving map), i.e. if and only if the first row of hs is equal to \([ 1, 0, \dots, 0 ]\).
[6]:
print(f"is_eq_constraint_satisfied(): {gate.is_eq_constraint_satisfied()}")
print(f"is_tp(): {gate.is_tp()}")
print(f"hs[0]: {gate.hs[0]}")
is_eq_constraint_satisfied(): True
is_tp(): True
hs[0]: [1. 0. 0. 0.]
The is_ineq_constraint_satisfied() function returns True, if and only if hs is CP(Complete-Positivity-Preserving), i.e. if and only if Choi matrix of hs is positive semidifinite matrix.
[7]:
print(f"is_ineq_constraint_satisfied(): {gate.is_ineq_constraint_satisfied()}")
print(f"is_cp(): {gate.is_cp()}")
is_ineq_constraint_satisfied(): True
is_cp(): True
projection functions
calc_proj_eq_constraint() function calculates the projection of Gate on equal constraint.hs with \([ 1, 0, \dots, 0 ]\).[8]:
hs = np.array(range(16), dtype=np.float64).reshape((4, 4))
print(f"hs: \n{gate.hs}")
hs:
[[ 1. 0. 0. 0.]
[ 0. 1. 0. 0.]
[ 0. 0. -1. 0.]
[ 0. 0. 0. -1.]]
[9]:
gate = Gate(c_sys, hs, is_physicality_required=False)
proj_gate = gate.calc_proj_eq_constraint()
print(f"hs: \n{proj_gate.hs}")
hs:
[[ 1. 0. 0. 0.]
[ 4. 5. 6. 7.]
[ 8. 9. 10. 11.]
[12. 13. 14. 15.]]
calc_proj_ineq_constraint() function calculates the projection of Gate with hs on inequal constraint as follows:
Let \(\text{Choi}\) be Choi matrix of
hsExecutes singular value decomposition on \(\text{Choi}\), \(\text{Choi} = U \Lambda U^{\dagger}\), where \(\Lambda = \text{diag}[\lambda_0, \dots , \lambda_{d-1}]\), and \(\lambda_{i} \in \mathbb{R}\).
\(\lambda^{\prime}_{i} := \begin{cases} \lambda_{i} & (\lambda_{i} \geq 0) \\ 0 & (\lambda_{i} < 0) \end{cases}\)
\(\Lambda^{\prime} = \text{diag}[\lambda^{\prime}_0, \dots , \lambda^{\prime}_{d-1}]\)
\(\text{Choi}^{\prime} = U \Lambda^{\prime} U^{\dagger}\)
Let \(\text{HS}^{\prime}\) be Hilbert-Schmidt matrix representation of \(\text{Choi}^{\prime}\)
The projection of Gate is Gate with
hs= \(\text{HS}^{\prime}\).
[10]:
gate = Gate(c_sys, hs, is_physicality_required=False)
proj_gate = gate.calc_proj_ineq_constraint()
print(f"hs: \n{proj_gate.hs}")
hs:
[[15.84558996 4.43570942 5.29265833 6.14960724]
[ 2.63097854 2.34702553 3.08437192 3.44443746]
[ 4.98440409 4.50900796 4.73510284 5.71575945]
[ 7.33782964 6.29370952 7.14039548 7.60980059]]
functions to transform parameters
to_stacked_vector() function returns a one-dimensional numpy array of all variables. This is equal to flattened hs.
[11]:
print(f"to_stacked_vector(): {gate.to_stacked_vector()}")
print(f"lattened hs: {gate.hs.flatten()}")
to_stacked_vector(): [ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15.]
lattened hs: [ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15.]
on_para_eq_constraint is True, then the first row of hs is equal to \([1, 0, \dots, 0]\). Thus, Gate is characterized by the second and subsequent rows of hs.to_var() function returns the flattened second and subsequent rows of hs, where on_para_eq_constraint is True.[12]:
# on_para_eq_constraint=True
gate = Gate(c_sys, hs, is_physicality_required=False, on_para_eq_constraint=True)
print(f"to_var(): {gate.to_var()}")
to_var(): [ 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15.]
[13]:
# on_para_eq_constraint=False
gate = Gate(c_sys, hs, is_physicality_required=False, on_para_eq_constraint=False)
print(f"to_var(): {gate.to_var()}")
to_var(): [ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15.]
functions to generate special objects
[14]:
zero_gate = gate.generate_zero_obj()
print(f"zero: \n{zero_gate.hs}")
origin_gate = gate.generate_origin_obj()
print(f"origin: \n{origin_gate.hs}")
zero:
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
origin:
[[1. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
supports arithmetic operations
[15]:
hs1 = np.array(range(16), dtype=np.float64).reshape((4, 4))
gate1 = Gate(c_sys, hs1, is_physicality_required=False)
hs2 = np.array(range(16, 32), dtype=np.float64).reshape((4, 4))
gate2 = Gate(c_sys, hs2, is_physicality_required=False)
print(gate1.hs)
print(gate2.hs)
[[ 0. 1. 2. 3.]
[ 4. 5. 6. 7.]
[ 8. 9. 10. 11.]
[12. 13. 14. 15.]]
[[16. 17. 18. 19.]
[20. 21. 22. 23.]
[24. 25. 26. 27.]
[28. 29. 30. 31.]]
[16]:
print(f"sum: \n{(gate1 + gate2).hs}")
print(f"subtraction: \n{(gate1 - gate2).hs}")
print(f"right multiplication: \n{(2 * gate1).hs}")
print(f"left multiplication: \n{(gate1 * 2).hs}")
print(f"division: \n{(gate1 / 2).hs}")
sum:
[[16. 18. 20. 22.]
[24. 26. 28. 30.]
[32. 34. 36. 38.]
[40. 42. 44. 46.]]
subtraction:
[[-16. -16. -16. -16.]
[-16. -16. -16. -16.]
[-16. -16. -16. -16.]
[-16. -16. -16. -16.]]
right multiplication:
[[ 0. 2. 4. 6.]
[ 8. 10. 12. 14.]
[16. 18. 20. 22.]
[24. 26. 28. 30.]]
left multiplication:
[[ 0. 2. 4. 6.]
[ 8. 10. 12. 14.]
[16. 18. 20. 22.]
[24. 26. 28. 30.]]
division:
[[0. 0.5 1. 1.5]
[2. 2.5 3. 3.5]
[4. 4.5 5. 5.5]
[6. 6.5 7. 7.5]]
calc_gradient functions
Calculates gradient of Gate with variable index.
[17]:
grad_gate = gate.calc_gradient(0)
print(f"hs: \n{grad_gate.hs}")
hs:
[[1. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
convert_basis function
Returns hs converted to the specified basis.
[18]:
from quara.objects.matrix_basis import get_comp_basis
gate = generate_gate_from_gate_name("x", c_sys)
converted_hs = gate.convert_basis(get_comp_basis())
print(f"hs: \n{converted_hs}")
hs:
[[-2.23711432e-17+0.j 0.00000000e+00+0.j 0.00000000e+00+0.j 1.00000000e+00+0.j]
[ 0.00000000e+00+0.j 0.00000000e+00+0.j 1.00000000e+00+0.j 0.00000000e+00+0.j]
[ 0.00000000e+00+0.j 1.00000000e+00+0.j 0.00000000e+00+0.j 0.00000000e+00+0.j]
[ 1.00000000e+00+0.j 0.00000000e+00+0.j 0.00000000e+00+0.j -2.23711432e-17+0.j]]
to_choi_matrix
Returns Choi matrix of Gate.
[19]:
gate = generate_gate_from_gate_name("x", c_sys)
print(f"to_choi_matrix(): \n{gate.to_choi_matrix()}")
print(f"to_choi_matrix_with_dict(): \n{gate.to_choi_matrix_with_dict()}")
print(f"to_choi_matrix_with_sparsity(): \n{gate.to_choi_matrix_with_sparsity()}")
to_choi_matrix():
[[0.+0.j 0.+0.j 0.+0.j 0.+0.j]
[0.+0.j 1.+0.j 1.+0.j 0.+0.j]
[0.+0.j 1.+0.j 1.+0.j 0.+0.j]
[0.+0.j 0.+0.j 0.+0.j 0.+0.j]]
to_choi_matrix_with_dict():
[[0.+0.j 0.+0.j 0.+0.j 0.+0.j]
[0.+0.j 1.+0.j 1.+0.j 0.+0.j]
[0.+0.j 1.+0.j 1.+0.j 0.+0.j]
[0.+0.j 0.+0.j 0.+0.j 0.+0.j]]
to_choi_matrix_with_sparsity():
[[0.+0.j 0.+0.j 0.+0.j 0.+0.j]
[0.+0.j 1.+0.j 1.+0.j 0.+0.j]
[0.+0.j 1.+0.j 1.+0.j 0.+0.j]
[0.+0.j 0.+0.j 0.+0.j 0.+0.j]]
to_kraus_matrices
Returns Kraus matrices of Gate.
[20]:
gate = generate_gate_from_gate_name("x", c_sys)
print(f"to_kraus_matrices(): \n{gate.to_kraus_matrices()}")
to_kraus_matrices():
[array([[0.+0.j, 1.+0.j],
[1.+0.j, 0.+0.j]])]
to_process_matrix
Returns process matrix of Gate.
[21]:
gate = generate_gate_from_gate_name("x", c_sys)
print(f"to_process_matrix(): \n{gate.to_process_matrix()}")
to_process_matrix():
[[-2.23711432e-17+0.j 0.00000000e+00+0.j 0.00000000e+00+0.j 0.00000000e+00+0.j]
[ 0.00000000e+00+0.j 1.00000000e+00+0.j 1.00000000e+00+0.j 0.00000000e+00+0.j]
[ 0.00000000e+00+0.j 1.00000000e+00+0.j 1.00000000e+00+0.j 0.00000000e+00+0.j]
[ 0.00000000e+00+0.j 0.00000000e+00+0.j 0.00000000e+00+0.j -2.23711432e-17+0.j]]
some utility functions
[22]:
print(f"is_tp(): {gate.is_tp()}")
print(f"is_cp(): {gate.is_cp()}")
is_tp(): True
is_cp(): True
gate_typical
generate_gate_object_from_gate_name_object_name() function in gate_typical module can easily generate objects related to Gate.generate_gate_object_from_gate_name_object_name() function has the following arguments:The string that can be specified for
gate_namecan be checked by executing theget_gate_names()function. The tensor product of state_name “a”, “b” is written “a_b”.object_namecan be the following string:“unitary_mat” - unitary matrix of the gate.
“gate_mat” - The Hilbert-Schmidt matrix representation of the gate.
“gate” - Gate object.
c_sys- CompositeSystem of objects related to Gate. Specify whenobject_nameis “gate”.is_physicality_required- Whether the generated object is physicality required, by default True.
[23]:
from quara.objects.gate_typical import (
get_gate_names,
generate_gate_object_from_gate_name_object_name,
)
#get_gate_names()
object_name = “unitary_mat”
[24]:
mat = generate_gate_object_from_gate_name_object_name("x", "unitary_mat")
print(mat)
[[0.+0.j 1.+0.j]
[1.+0.j 0.+0.j]]
object_name = “gate_mat”
[25]:
mat = generate_gate_object_from_gate_name_object_name("x", "gate_mat")
print(mat)
[[ 1. 0. 0. 0.]
[ 0. 1. 0. 0.]
[ 0. 0. -1. 0.]
[ 0. 0. 0. -1.]]
object_name = “gate”
[26]:
c_sys = generate_composite_system("qubit", 1)
gate = generate_gate_object_from_gate_name_object_name("x", "gate", c_sys=c_sys)
print(gate)
Type:
Gate
Dim:
2
HS:
[[ 1. 0. 0. 0.]
[ 0. 1. 0. 0.]
[ 0. 0. -1. 0.]
[ 0. 0. 0. -1.]]