tensorcircuit-nightly 1.3.0.dev20250903__py3-none-any.whl → 1.3.0.dev20250904__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of tensorcircuit-nightly might be problematic. Click here for more details.
- tensorcircuit/__init__.py +1 -1
- tensorcircuit/abstractcircuit.py +1 -0
- tensorcircuit/backends/abstract_backend.py +33 -5
- tensorcircuit/backends/jax_backend.py +3 -0
- tensorcircuit/backends/numpy_backend.py +3 -0
- tensorcircuit/backends/pytorch_backend.py +3 -0
- tensorcircuit/backends/tensorflow_backend.py +3 -0
- tensorcircuit/basecircuit.py +127 -75
- tensorcircuit/circuit.py +54 -52
- tensorcircuit/cons.py +1 -0
- tensorcircuit/densitymatrix.py +13 -8
- tensorcircuit/gates.py +28 -22
- tensorcircuit/mpscircuit.py +89 -59
- tensorcircuit/quantum.py +274 -105
- tensorcircuit/results/counts.py +45 -31
- tensorcircuit/simplify.py +3 -1
- tensorcircuit/timeevol.py +42 -33
- {tensorcircuit_nightly-1.3.0.dev20250903.dist-info → tensorcircuit_nightly-1.3.0.dev20250904.dist-info}/METADATA +1 -1
- {tensorcircuit_nightly-1.3.0.dev20250903.dist-info → tensorcircuit_nightly-1.3.0.dev20250904.dist-info}/RECORD +22 -22
- {tensorcircuit_nightly-1.3.0.dev20250903.dist-info → tensorcircuit_nightly-1.3.0.dev20250904.dist-info}/WHEEL +0 -0
- {tensorcircuit_nightly-1.3.0.dev20250903.dist-info → tensorcircuit_nightly-1.3.0.dev20250904.dist-info}/licenses/LICENSE +0 -0
- {tensorcircuit_nightly-1.3.0.dev20250903.dist-info → tensorcircuit_nightly-1.3.0.dev20250904.dist-info}/top_level.txt +0 -0
tensorcircuit/circuit.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Quantum circuit: the state simulator
|
|
2
|
+
Quantum circuit: the state simulator.
|
|
3
|
+
Supports qubit (dim=2) and qudit (3 <= dim <= 36) systems.
|
|
4
|
+
For string-encoded samples/counts, digits use 0–9A–Z where A=10, …, Z=35.
|
|
3
5
|
"""
|
|
4
6
|
|
|
5
7
|
# pylint: disable=invalid-name
|
|
@@ -13,8 +15,8 @@ import tensornetwork as tn
|
|
|
13
15
|
|
|
14
16
|
from . import gates
|
|
15
17
|
from . import channels
|
|
16
|
-
from .cons import backend, contractor, dtypestr, npdtype
|
|
17
|
-
from .quantum import QuOperator, identity
|
|
18
|
+
from .cons import backend, contractor, dtypestr, npdtype, _ALPHABET
|
|
19
|
+
from .quantum import QuOperator, identity, _infer_num_sites, onehot_d_tensor
|
|
18
20
|
from .simplify import _full_light_cone_cancel
|
|
19
21
|
from .basecircuit import BaseCircuit
|
|
20
22
|
|
|
@@ -23,7 +25,7 @@ Tensor = Any
|
|
|
23
25
|
|
|
24
26
|
|
|
25
27
|
class Circuit(BaseCircuit):
|
|
26
|
-
"""
|
|
28
|
+
r"""
|
|
27
29
|
``Circuit`` class.
|
|
28
30
|
Simple usage demo below.
|
|
29
31
|
|
|
@@ -45,14 +47,18 @@ class Circuit(BaseCircuit):
|
|
|
45
47
|
inputs: Optional[Tensor] = None,
|
|
46
48
|
mps_inputs: Optional[QuOperator] = None,
|
|
47
49
|
split: Optional[Dict[str, Any]] = None,
|
|
50
|
+
dim: Optional[int] = None,
|
|
48
51
|
) -> None:
|
|
49
|
-
"""
|
|
52
|
+
r"""
|
|
50
53
|
Circuit object based on state simulator.
|
|
54
|
+
Do not use this class with d!=2 directly, use tc.QuditCircuit instead for qudit systems.
|
|
51
55
|
|
|
52
56
|
:param nqubits: The number of qubits in the circuit.
|
|
53
57
|
:type nqubits: int
|
|
58
|
+
:param dim: The local Hilbert space dimension per site. Qudit is supported for 2 <= d <= 36.
|
|
59
|
+
:type dim: If None, the dimension of the circuit will be `2`, which is a qubit system.
|
|
54
60
|
:param inputs: If not None, the initial state of the circuit is taken as ``inputs``
|
|
55
|
-
instead of :math
|
|
61
|
+
instead of :math:`\vert 0 \rangle^n` qubits, defaults to None.
|
|
56
62
|
:type inputs: Optional[Tensor], optional
|
|
57
63
|
:param mps_inputs: QuVector for a MPS like initial wavefunction.
|
|
58
64
|
:type mps_inputs: Optional[QuOperator]
|
|
@@ -60,6 +66,7 @@ class Circuit(BaseCircuit):
|
|
|
60
66
|
``max_singular_values`` and ``max_truncation_err``.
|
|
61
67
|
:type split: Optional[Dict[str, Any]]
|
|
62
68
|
"""
|
|
69
|
+
self._d = 2 if dim is None else dim
|
|
63
70
|
self.inputs = inputs
|
|
64
71
|
self.mps_inputs = mps_inputs
|
|
65
72
|
self.split = split
|
|
@@ -70,18 +77,19 @@ class Circuit(BaseCircuit):
|
|
|
70
77
|
"inputs": inputs,
|
|
71
78
|
"mps_inputs": mps_inputs,
|
|
72
79
|
"split": split,
|
|
80
|
+
"dim": dim,
|
|
73
81
|
}
|
|
74
82
|
if (inputs is None) and (mps_inputs is None):
|
|
75
|
-
nodes = self.all_zero_nodes(nqubits)
|
|
83
|
+
nodes = self.all_zero_nodes(nqubits, dim=self._d)
|
|
76
84
|
self._front = [n.get_edge(0) for n in nodes]
|
|
77
85
|
elif inputs is not None: # provide input function
|
|
78
86
|
inputs = backend.convert_to_tensor(inputs)
|
|
79
87
|
inputs = backend.cast(inputs, dtype=dtypestr)
|
|
80
88
|
inputs = backend.reshape(inputs, [-1])
|
|
81
89
|
N = inputs.shape[0]
|
|
82
|
-
n =
|
|
90
|
+
n = _infer_num_sites(N, dim=self._d)
|
|
83
91
|
assert n == nqubits or n == 2 * nqubits
|
|
84
|
-
inputs = backend.reshape(inputs, [
|
|
92
|
+
inputs = backend.reshape(inputs, [self._d for _ in range(n)])
|
|
85
93
|
inputs = Gate(inputs)
|
|
86
94
|
nodes = [inputs]
|
|
87
95
|
self._front = [inputs.get_edge(i) for i in range(n)]
|
|
@@ -178,27 +186,14 @@ class Circuit(BaseCircuit):
|
|
|
178
186
|
|
|
179
187
|
:param index: The index of qubit that the Z direction postselection applied on.
|
|
180
188
|
:type index: int
|
|
181
|
-
:param keep:
|
|
189
|
+
:param keep: the post-selected digit in {0, ..., d-1}, defaults to be 0.
|
|
182
190
|
:type keep: int, optional
|
|
183
191
|
"""
|
|
184
192
|
# normalization not guaranteed
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
[1.0],
|
|
190
|
-
[0.0],
|
|
191
|
-
],
|
|
192
|
-
dtype=npdtype,
|
|
193
|
-
)
|
|
194
|
-
else:
|
|
195
|
-
gate = np.array(
|
|
196
|
-
[
|
|
197
|
-
[0.0],
|
|
198
|
-
[1.0],
|
|
199
|
-
],
|
|
200
|
-
dtype=npdtype,
|
|
201
|
-
)
|
|
193
|
+
gate = np.array(
|
|
194
|
+
[[0.0] if _idx != keep else [1.0] for _idx in range(self._d)],
|
|
195
|
+
dtype=npdtype,
|
|
196
|
+
)
|
|
202
197
|
|
|
203
198
|
mg1 = tn.Node(gate)
|
|
204
199
|
mg2 = tn.Node(gate)
|
|
@@ -479,7 +474,7 @@ class Circuit(BaseCircuit):
|
|
|
479
474
|
if get_gate_from_index is None:
|
|
480
475
|
raise ValueError("no `get_gate_from_index` implementation is provided")
|
|
481
476
|
g = get_gate_from_index(r, kraus)
|
|
482
|
-
g = backend.reshape(g, [
|
|
477
|
+
g = backend.reshape(g, [self._d for _ in range(sites * 2)])
|
|
483
478
|
self.any(*index, unitary=g, name=name) # type: ignore
|
|
484
479
|
return r
|
|
485
480
|
|
|
@@ -680,7 +675,7 @@ class Circuit(BaseCircuit):
|
|
|
680
675
|
Apply %s quantum channel on the circuit.
|
|
681
676
|
See :py:meth:`tensorcircuit.channels.%schannel`
|
|
682
677
|
|
|
683
|
-
:param index:
|
|
678
|
+
:param index: Site index that the gate applies on.
|
|
684
679
|
:type index: int.
|
|
685
680
|
:param status: uniform external random number between 0 and 1
|
|
686
681
|
:type status: Tensor
|
|
@@ -737,8 +732,8 @@ class Circuit(BaseCircuit):
|
|
|
737
732
|
:return: ``QuOperator`` object for the circuit unitary (open indices for the input state)
|
|
738
733
|
:rtype: QuOperator
|
|
739
734
|
"""
|
|
740
|
-
mps = identity([
|
|
741
|
-
c = Circuit(self._nqubits)
|
|
735
|
+
mps = identity([self._d for _ in range(self._nqubits)])
|
|
736
|
+
c = Circuit(self._nqubits, dim=self._d)
|
|
742
737
|
ns, es = self._copy()
|
|
743
738
|
c._nodes = ns
|
|
744
739
|
c._front = es
|
|
@@ -758,8 +753,8 @@ class Circuit(BaseCircuit):
|
|
|
758
753
|
:return: The circuit unitary matrix
|
|
759
754
|
:rtype: Tensor
|
|
760
755
|
"""
|
|
761
|
-
mps = identity([
|
|
762
|
-
c = Circuit(self._nqubits)
|
|
756
|
+
mps = identity([self._d for _ in range(self._nqubits)])
|
|
757
|
+
c = Circuit(self._nqubits, dim=self._d)
|
|
763
758
|
ns, es = self._copy()
|
|
764
759
|
c._nodes = ns
|
|
765
760
|
c._front = es
|
|
@@ -772,6 +767,9 @@ class Circuit(BaseCircuit):
|
|
|
772
767
|
"""
|
|
773
768
|
Take measurement on the given quantum lines by ``index``.
|
|
774
769
|
|
|
770
|
+
Return format:
|
|
771
|
+
- For d <= 36, the sample is a base-d string using 0–9A–Z (A=10,…).
|
|
772
|
+
|
|
775
773
|
:Example:
|
|
776
774
|
|
|
777
775
|
>>> c = tc.Circuit(3)
|
|
@@ -800,10 +798,7 @@ class Circuit(BaseCircuit):
|
|
|
800
798
|
if i != j:
|
|
801
799
|
e ^ edge2[i]
|
|
802
800
|
for i in range(len(sample)):
|
|
803
|
-
|
|
804
|
-
m = np.array([1, 0], dtype=npdtype)
|
|
805
|
-
else:
|
|
806
|
-
m = np.array([0, 1], dtype=npdtype)
|
|
801
|
+
m = onehot_d_tensor(sample[i], d=self._d)
|
|
807
802
|
nodes1.append(tn.Node(m))
|
|
808
803
|
nodes1[-1].get_edge(0) ^ edge1[index[i]]
|
|
809
804
|
nodes2.append(tn.Node(m))
|
|
@@ -814,15 +809,13 @@ class Circuit(BaseCircuit):
|
|
|
814
809
|
/ p
|
|
815
810
|
* contractor(nodes1, output_edge_order=[edge1[j], edge2[j]]).tensor
|
|
816
811
|
)
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
sample += "1"
|
|
825
|
-
p = p * (1 - pu)
|
|
812
|
+
probs = backend.real(backend.diagonal(rho))
|
|
813
|
+
probs /= backend.sum(probs)
|
|
814
|
+
outcome = backend.implicit_randc(self._d, shape=1, p=probs)
|
|
815
|
+
|
|
816
|
+
sample += _ALPHABET[outcome]
|
|
817
|
+
p *= float(probs[outcome])
|
|
818
|
+
|
|
826
819
|
if with_prob:
|
|
827
820
|
return sample, p
|
|
828
821
|
else:
|
|
@@ -842,6 +835,10 @@ class Circuit(BaseCircuit):
|
|
|
842
835
|
) -> Tensor:
|
|
843
836
|
"""
|
|
844
837
|
Compute the expectation of corresponding operators.
|
|
838
|
+
For qudit (d > 2),
|
|
839
|
+
ensure that operator tensor shapes are consistent with d (each site contributes two axes of size d).
|
|
840
|
+
|
|
841
|
+
Noise shorthand (via noise_conf) is qubit-only; for d>2, use explicit operators.
|
|
845
842
|
|
|
846
843
|
:Example:
|
|
847
844
|
|
|
@@ -883,8 +880,6 @@ class Circuit(BaseCircuit):
|
|
|
883
880
|
:return: Tensor with one element
|
|
884
881
|
:rtype: Tensor
|
|
885
882
|
"""
|
|
886
|
-
from .noisemodel import expectation_noisfy
|
|
887
|
-
|
|
888
883
|
if noise_conf is None:
|
|
889
884
|
# if not reuse:
|
|
890
885
|
# nodes1, edge1 = self._copy()
|
|
@@ -899,6 +894,8 @@ class Circuit(BaseCircuit):
|
|
|
899
894
|
nodes1 = _full_light_cone_cancel(nodes1)
|
|
900
895
|
return contractor(nodes1).tensor
|
|
901
896
|
else:
|
|
897
|
+
from .noisemodel import expectation_noisfy
|
|
898
|
+
|
|
902
899
|
return expectation_noisfy(
|
|
903
900
|
self,
|
|
904
901
|
*ops,
|
|
@@ -919,9 +916,11 @@ def expectation(
|
|
|
919
916
|
bra: Optional[Tensor] = None,
|
|
920
917
|
conj: bool = True,
|
|
921
918
|
normalization: bool = False,
|
|
919
|
+
dim: Optional[int] = None,
|
|
922
920
|
) -> Tensor:
|
|
923
921
|
"""
|
|
924
922
|
Compute :math:`\\langle bra\\vert ops \\vert ket\\rangle`.
|
|
923
|
+
For qudit systems (d>2), ops must be reshaped with per-site axes of length d.
|
|
925
924
|
|
|
926
925
|
Example 1 (:math:`bra` is same as :math:`ket`)
|
|
927
926
|
|
|
@@ -966,6 +965,8 @@ def expectation(
|
|
|
966
965
|
:type ket: Tensor
|
|
967
966
|
:param bra: :math:`bra`, defaults to None, which is the same as ``ket``.
|
|
968
967
|
:type bra: Optional[Tensor], optional
|
|
968
|
+
:param dim: dimension of the circuit (defaults to 2)
|
|
969
|
+
:type dim: int, optional
|
|
969
970
|
:param conj: :math:`bra` changes to the adjoint matrix of :math:`bra`, defaults to True.
|
|
970
971
|
:type conj: bool, optional
|
|
971
972
|
:param normalization: Normalize the :math:`ket` and :math:`bra`, defaults to False.
|
|
@@ -974,6 +975,7 @@ def expectation(
|
|
|
974
975
|
:return: The result of :math:`\\langle bra\\vert ops \\vert ket\\rangle`.
|
|
975
976
|
:rtype: Tensor
|
|
976
977
|
"""
|
|
978
|
+
dim = 2 if dim is None else dim
|
|
977
979
|
if bra is None:
|
|
978
980
|
bra = ket
|
|
979
981
|
if isinstance(ket, QuOperator):
|
|
@@ -987,7 +989,7 @@ def expectation(
|
|
|
987
989
|
for op, index in ops:
|
|
988
990
|
if not isinstance(op, tn.Node):
|
|
989
991
|
# op is only a matrix
|
|
990
|
-
op = backend.
|
|
992
|
+
op = backend.reshaped(op, dim)
|
|
991
993
|
op = gates.Gate(op)
|
|
992
994
|
if isinstance(index, int):
|
|
993
995
|
index = [index]
|
|
@@ -1011,8 +1013,8 @@ def expectation(
|
|
|
1011
1013
|
if conj is True:
|
|
1012
1014
|
bra = backend.conj(bra)
|
|
1013
1015
|
ket = backend.reshape(ket, [-1])
|
|
1014
|
-
ket = backend.
|
|
1015
|
-
bra = backend.
|
|
1016
|
+
ket = backend.reshaped(ket, dim)
|
|
1017
|
+
bra = backend.reshaped(bra, dim)
|
|
1016
1018
|
n = len(backend.shape_tuple(ket))
|
|
1017
1019
|
ket = Gate(ket)
|
|
1018
1020
|
bra = Gate(bra)
|
|
@@ -1024,7 +1026,7 @@ def expectation(
|
|
|
1024
1026
|
for op, index in ops:
|
|
1025
1027
|
if not isinstance(op, tn.Node):
|
|
1026
1028
|
# op is only a matrix
|
|
1027
|
-
op = backend.
|
|
1029
|
+
op = backend.reshaped(op, dim)
|
|
1028
1030
|
op = gates.Gate(op)
|
|
1029
1031
|
if isinstance(index, int):
|
|
1030
1032
|
index = [index]
|
tensorcircuit/cons.py
CHANGED
|
@@ -63,6 +63,7 @@ rdtypestr = "float32"
|
|
|
63
63
|
npdtype = np.complex64
|
|
64
64
|
backend: NumpyBackend = get_backend("numpy")
|
|
65
65
|
contractor = tn.contractors.auto
|
|
66
|
+
_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
66
67
|
# these above lines are just for mypy, it is not very good at evaluating runtime object
|
|
67
68
|
|
|
68
69
|
|
tensorcircuit/densitymatrix.py
CHANGED
|
@@ -17,7 +17,7 @@ from .channels import kraus_to_super_gate
|
|
|
17
17
|
from .circuit import Circuit
|
|
18
18
|
from .cons import backend, contractor, dtypestr
|
|
19
19
|
from .basecircuit import BaseCircuit
|
|
20
|
-
from .quantum import QuOperator
|
|
20
|
+
from .quantum import QuOperator, _infer_num_sites
|
|
21
21
|
|
|
22
22
|
Gate = gates.Gate
|
|
23
23
|
Tensor = Any
|
|
@@ -35,9 +35,11 @@ class DMCircuit(BaseCircuit):
|
|
|
35
35
|
dminputs: Optional[Tensor] = None,
|
|
36
36
|
mpo_dminputs: Optional[QuOperator] = None,
|
|
37
37
|
split: Optional[Dict[str, Any]] = None,
|
|
38
|
+
dim: Optional[int] = None,
|
|
38
39
|
) -> None:
|
|
39
40
|
"""
|
|
40
41
|
The density matrix simulator based on tensornetwork engine.
|
|
42
|
+
Do not use this class with d!=2 directly
|
|
41
43
|
|
|
42
44
|
:param nqubits: Number of qubits
|
|
43
45
|
:type nqubits: int
|
|
@@ -55,6 +57,7 @@ class DMCircuit(BaseCircuit):
|
|
|
55
57
|
``max_singular_values`` and ``max_truncation_err``.
|
|
56
58
|
:type split: Optional[Dict[str, Any]]
|
|
57
59
|
"""
|
|
60
|
+
self._d = 2 if dim is None else dim
|
|
58
61
|
if not empty:
|
|
59
62
|
if (
|
|
60
63
|
(inputs is None)
|
|
@@ -73,9 +76,9 @@ class DMCircuit(BaseCircuit):
|
|
|
73
76
|
inputs = backend.cast(inputs, dtype=dtypestr)
|
|
74
77
|
inputs = backend.reshape(inputs, [-1])
|
|
75
78
|
N = inputs.shape[0]
|
|
76
|
-
n =
|
|
79
|
+
n = _infer_num_sites(N, self._d)
|
|
77
80
|
assert n == nqubits
|
|
78
|
-
inputs = backend.reshape(inputs, [
|
|
81
|
+
inputs = backend.reshape(inputs, [self._d for _ in range(n)])
|
|
79
82
|
inputs_gate = Gate(inputs)
|
|
80
83
|
self._nodes = [inputs_gate]
|
|
81
84
|
self.coloring_nodes(self._nodes)
|
|
@@ -94,7 +97,9 @@ class DMCircuit(BaseCircuit):
|
|
|
94
97
|
elif dminputs is not None:
|
|
95
98
|
dminputs = backend.convert_to_tensor(dminputs)
|
|
96
99
|
dminputs = backend.cast(dminputs, dtype=dtypestr)
|
|
97
|
-
dminputs = backend.reshape(
|
|
100
|
+
dminputs = backend.reshape(
|
|
101
|
+
dminputs, [self._d for _ in range(2 * nqubits)]
|
|
102
|
+
)
|
|
98
103
|
dminputs_gate = Gate(dminputs)
|
|
99
104
|
nodes = [dminputs_gate]
|
|
100
105
|
self._front = [dminputs_gate.get_edge(i) for i in range(2 * nqubits)]
|
|
@@ -217,7 +222,7 @@ class DMCircuit(BaseCircuit):
|
|
|
217
222
|
dd = dmc.densitymatrix()
|
|
218
223
|
circuits.append(dd)
|
|
219
224
|
tensor = reduce(add, circuits)
|
|
220
|
-
tensor = backend.
|
|
225
|
+
tensor = backend.reshaped(tensor, d=self._d)
|
|
221
226
|
self._nodes = [Gate(tensor)]
|
|
222
227
|
dangling = [e for e in self._nodes[0]]
|
|
223
228
|
self._front = dangling
|
|
@@ -255,7 +260,7 @@ class DMCircuit(BaseCircuit):
|
|
|
255
260
|
t = contractor(nodes, output_edge_order=d_edges)
|
|
256
261
|
else:
|
|
257
262
|
t = nodes[0]
|
|
258
|
-
dm = backend.
|
|
263
|
+
dm = backend.reshapem(t.tensor)
|
|
259
264
|
if check:
|
|
260
265
|
self.check_density_matrix(dm)
|
|
261
266
|
return dm
|
|
@@ -274,7 +279,7 @@ class DMCircuit(BaseCircuit):
|
|
|
274
279
|
dm = self.densitymatrix()
|
|
275
280
|
e, v = backend.eigh(dm)
|
|
276
281
|
np.testing.assert_allclose(
|
|
277
|
-
e[:-1], backend.zeros([
|
|
282
|
+
e[:-1], backend.zeros([self._d**self._nqubits - 1]), atol=1e-5
|
|
278
283
|
)
|
|
279
284
|
return v[:, -1]
|
|
280
285
|
|
|
@@ -375,7 +380,7 @@ class DMCircuit2(DMCircuit):
|
|
|
375
380
|
# index = [index[0] for _ in range(len(kraus))]
|
|
376
381
|
super_op = kraus_to_super_gate(kraus)
|
|
377
382
|
nlegs = 4 * len(index)
|
|
378
|
-
super_op = backend.reshape(super_op, [
|
|
383
|
+
super_op = backend.reshape(super_op, [self._d for _ in range(nlegs)])
|
|
379
384
|
super_op = Gate(super_op)
|
|
380
385
|
o2i = int(nlegs / 2)
|
|
381
386
|
r2l = int(nlegs / 4)
|
tensorcircuit/gates.py
CHANGED
|
@@ -34,6 +34,12 @@ one_state = np.array([0.0, 1.0], dtype=npdtype)
|
|
|
34
34
|
plus_state = 1.0 / np.sqrt(2) * (zero_state + one_state)
|
|
35
35
|
minus_state = 1.0 / np.sqrt(2) * (zero_state - one_state)
|
|
36
36
|
|
|
37
|
+
# Common elements as np.ndarray objects
|
|
38
|
+
_i00 = np.array([[1.0, 0.0], [0.0, 0.0]])
|
|
39
|
+
_i01 = np.array([[0.0, 1.0], [0.0, 0.0]])
|
|
40
|
+
_i10 = np.array([[0.0, 0.0], [1.0, 0.0]])
|
|
41
|
+
_i11 = np.array([[0.0, 0.0], [0.0, 1.0]])
|
|
42
|
+
|
|
37
43
|
# Common single qubit gates as np.ndarray objects
|
|
38
44
|
_h_matrix = 1 / np.sqrt(2) * np.array([[1.0, 1.0], [1.0, -1.0]])
|
|
39
45
|
_i_matrix = np.array([[1.0, 0.0], [0.0, 1.0]])
|
|
@@ -229,7 +235,7 @@ def num_to_tensor(*num: Union[float, Tensor], dtype: Optional[str] = None) -> An
|
|
|
229
235
|
# TODO(@YHPeter): fix __doc__ for same function with different names
|
|
230
236
|
|
|
231
237
|
l = []
|
|
232
|
-
if
|
|
238
|
+
if dtype is None:
|
|
233
239
|
dtype = dtypestr
|
|
234
240
|
for n in num:
|
|
235
241
|
if not backend.is_tensor(n):
|
|
@@ -245,7 +251,7 @@ array_to_tensor = num_to_tensor
|
|
|
245
251
|
|
|
246
252
|
|
|
247
253
|
def gate_wrapper(m: Tensor, n: Optional[str] = None) -> Gate:
|
|
248
|
-
if
|
|
254
|
+
if n is None:
|
|
249
255
|
n = "unknowngate"
|
|
250
256
|
m = m.astype(npdtype)
|
|
251
257
|
return Gate(deepcopy(m), name=n)
|
|
@@ -255,7 +261,7 @@ class GateF:
|
|
|
255
261
|
def __init__(
|
|
256
262
|
self, m: Tensor, n: Optional[str] = None, ctrl: Optional[List[int]] = None
|
|
257
263
|
):
|
|
258
|
-
if
|
|
264
|
+
if n is None:
|
|
259
265
|
n = "unknowngate"
|
|
260
266
|
self.m = m
|
|
261
267
|
self.n = n
|
|
@@ -310,7 +316,7 @@ class GateF:
|
|
|
310
316
|
|
|
311
317
|
return Gate(cu, name="c" + self.n)
|
|
312
318
|
|
|
313
|
-
if
|
|
319
|
+
if self.ctrl is None:
|
|
314
320
|
ctrl = [1]
|
|
315
321
|
else:
|
|
316
322
|
ctrl = [1] + self.ctrl
|
|
@@ -330,7 +336,7 @@ class GateF:
|
|
|
330
336
|
# TODO(@refraction-ray): ctrl convention to be finally determined
|
|
331
337
|
return Gate(ocu, name="o" + self.n)
|
|
332
338
|
|
|
333
|
-
if
|
|
339
|
+
if self.ctrl is None:
|
|
334
340
|
ctrl = [0]
|
|
335
341
|
else:
|
|
336
342
|
ctrl = [0] + self.ctrl
|
|
@@ -349,7 +355,7 @@ class GateVF(GateF):
|
|
|
349
355
|
n: Optional[str] = None,
|
|
350
356
|
ctrl: Optional[List[int]] = None,
|
|
351
357
|
):
|
|
352
|
-
if
|
|
358
|
+
if n is None:
|
|
353
359
|
n = "unknowngate"
|
|
354
360
|
self.f = f
|
|
355
361
|
self.n = n
|
|
@@ -483,7 +489,7 @@ def phase_gate(theta: float = 0) -> Gate:
|
|
|
483
489
|
:rtype: Gate
|
|
484
490
|
"""
|
|
485
491
|
theta = array_to_tensor(theta)
|
|
486
|
-
i00, i11 = array_to_tensor(
|
|
492
|
+
i00, i11 = array_to_tensor(_i00, _i11)
|
|
487
493
|
unitary = i00 + backend.exp(1.0j * theta) * i11
|
|
488
494
|
return Gate(unitary)
|
|
489
495
|
|
|
@@ -512,7 +518,7 @@ def get_u_parameter(m: Tensor) -> Tuple[float, float, float]:
|
|
|
512
518
|
return theta, phi, lbd
|
|
513
519
|
|
|
514
520
|
|
|
515
|
-
def u_gate(theta: float = 0, phi: float = 0, lbd: float = 0) -> Gate:
|
|
521
|
+
def u_gate(theta: float = 0.0, phi: float = 0.0, lbd: float = 0.0) -> Gate:
|
|
516
522
|
r"""
|
|
517
523
|
IBMQ U gate following the converntion of OpenQASM3.0.
|
|
518
524
|
See `OpenQASM doc <https://openqasm.com/language/gates.html#built-in-gates>`_
|
|
@@ -533,12 +539,7 @@ def u_gate(theta: float = 0, phi: float = 0, lbd: float = 0) -> Gate:
|
|
|
533
539
|
:rtype: Gate
|
|
534
540
|
"""
|
|
535
541
|
theta, phi, lbd = array_to_tensor(theta, phi, lbd)
|
|
536
|
-
i00, i01, i10, i11 = array_to_tensor(
|
|
537
|
-
np.array([[1, 0], [0, 0]]),
|
|
538
|
-
np.array([[0, 1], [0, 0]]),
|
|
539
|
-
np.array([[0, 0], [1, 0]]),
|
|
540
|
-
np.array([[0, 0], [0, 1]]),
|
|
541
|
-
)
|
|
542
|
+
i00, i01, i10, i11 = array_to_tensor(_i00, _i01, _i10, _i11)
|
|
542
543
|
unitary = (
|
|
543
544
|
backend.cos(theta / 2) * i00
|
|
544
545
|
- backend.exp(1.0j * lbd) * backend.sin(theta / 2) * i01
|
|
@@ -548,7 +549,7 @@ def u_gate(theta: float = 0, phi: float = 0, lbd: float = 0) -> Gate:
|
|
|
548
549
|
return Gate(unitary)
|
|
549
550
|
|
|
550
551
|
|
|
551
|
-
def r_gate(theta: float = 0, alpha: float = 0, phi: float = 0) -> Gate:
|
|
552
|
+
def r_gate(theta: float = 0.0, alpha: float = 0.0, phi: float = 0.0) -> Gate:
|
|
552
553
|
r"""
|
|
553
554
|
General single qubit rotation gate
|
|
554
555
|
|
|
@@ -582,7 +583,7 @@ def r_gate(theta: float = 0, alpha: float = 0, phi: float = 0) -> Gate:
|
|
|
582
583
|
# r = r_gate
|
|
583
584
|
|
|
584
585
|
|
|
585
|
-
def rx_gate(theta: float = 0) -> Gate:
|
|
586
|
+
def rx_gate(theta: float = 0.0) -> Gate:
|
|
586
587
|
r"""
|
|
587
588
|
Rotation gate along :math:`x` axis.
|
|
588
589
|
|
|
@@ -603,7 +604,7 @@ def rx_gate(theta: float = 0) -> Gate:
|
|
|
603
604
|
# rx = rx_gate
|
|
604
605
|
|
|
605
606
|
|
|
606
|
-
def ry_gate(theta: float = 0) -> Gate:
|
|
607
|
+
def ry_gate(theta: float = 0.0) -> Gate:
|
|
607
608
|
r"""
|
|
608
609
|
Rotation gate along :math:`y` axis.
|
|
609
610
|
|
|
@@ -624,7 +625,7 @@ def ry_gate(theta: float = 0) -> Gate:
|
|
|
624
625
|
# ry = ry_gate
|
|
625
626
|
|
|
626
627
|
|
|
627
|
-
def rz_gate(theta: float = 0) -> Gate:
|
|
628
|
+
def rz_gate(theta: float = 0.0) -> Gate:
|
|
628
629
|
r"""
|
|
629
630
|
Rotation gate along :math:`z` axis.
|
|
630
631
|
|
|
@@ -645,7 +646,7 @@ def rz_gate(theta: float = 0) -> Gate:
|
|
|
645
646
|
# rz = rz_gate
|
|
646
647
|
|
|
647
648
|
|
|
648
|
-
def rgate_theoretical(theta: float = 0, alpha: float = 0, phi: float = 0) -> Gate:
|
|
649
|
+
def rgate_theoretical(theta: float = 0.0, alpha: float = 0.0, phi: float = 0.0) -> Gate:
|
|
649
650
|
r"""
|
|
650
651
|
Rotation gate implemented by matrix exponential. The output is the same as `rgate`.
|
|
651
652
|
|
|
@@ -723,7 +724,7 @@ def iswap_gate(theta: float = 1.0) -> Gate:
|
|
|
723
724
|
# iswap = iswap_gate
|
|
724
725
|
|
|
725
726
|
|
|
726
|
-
def cr_gate(theta: float = 0, alpha: float = 0, phi: float = 0) -> Gate:
|
|
727
|
+
def cr_gate(theta: float = 0.0, alpha: float = 0.0, phi: float = 0.0) -> Gate:
|
|
727
728
|
r"""
|
|
728
729
|
Controlled rotation gate. When the control qubit is 1, `rgate` is applied to the target qubit.
|
|
729
730
|
|
|
@@ -775,7 +776,7 @@ def random_two_qubit_gate() -> Gate:
|
|
|
775
776
|
return Gate(deepcopy(unitary), name="R2Q")
|
|
776
777
|
|
|
777
778
|
|
|
778
|
-
def any_gate(unitary: Tensor, name: str = "any") -> Gate:
|
|
779
|
+
def any_gate(unitary: Tensor, name: str = "any", dim: Optional[int] = None) -> Gate:
|
|
779
780
|
"""
|
|
780
781
|
Note one should provide the gate with properly reshaped.
|
|
781
782
|
|
|
@@ -783,6 +784,8 @@ def any_gate(unitary: Tensor, name: str = "any") -> Gate:
|
|
|
783
784
|
:type unitary: Tensor
|
|
784
785
|
:param name: The name of the gate.
|
|
785
786
|
:type name: str
|
|
787
|
+
:param dim: The dimension of the gate.
|
|
788
|
+
:type dim: int
|
|
786
789
|
:return: the resulted gate
|
|
787
790
|
:rtype: Gate
|
|
788
791
|
"""
|
|
@@ -791,7 +794,10 @@ def any_gate(unitary: Tensor, name: str = "any") -> Gate:
|
|
|
791
794
|
unitary.tensor = backend.cast(unitary.tensor, dtypestr)
|
|
792
795
|
return unitary
|
|
793
796
|
unitary = backend.cast(unitary, dtypestr)
|
|
794
|
-
|
|
797
|
+
if dim is None or dim == 2:
|
|
798
|
+
unitary = backend.reshape2(unitary)
|
|
799
|
+
else:
|
|
800
|
+
unitary = backend.reshaped(unitary, dim)
|
|
795
801
|
# nleg = int(np.log2(backend.sizen(unitary)))
|
|
796
802
|
# unitary = backend.reshape(unitary, [2 for _ in range(nleg)])
|
|
797
803
|
return Gate(unitary, name=name)
|