emu-sv 1.0.0__py3-none-any.whl → 2.0.0__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.
- emu_sv/__init__.py +18 -22
- emu_sv/custom_callback_implementations.py +67 -45
- emu_sv/dense_operator.py +91 -90
- emu_sv/density_matrix_state.py +195 -0
- emu_sv/hamiltonian.py +18 -22
- emu_sv/state_vector.py +98 -68
- emu_sv/sv_backend.py +89 -71
- emu_sv/sv_config.py +93 -36
- emu_sv/utils.py +8 -0
- {emu_sv-1.0.0.dist-info → emu_sv-2.0.0.dist-info}/METADATA +2 -2
- emu_sv-2.0.0.dist-info/RECORD +13 -0
- emu_sv-1.0.0.dist-info/RECORD +0 -11
- {emu_sv-1.0.0.dist-info → emu_sv-2.0.0.dist-info}/WHEEL +0 -0
emu_sv/__init__.py
CHANGED
|
@@ -1,42 +1,38 @@
|
|
|
1
|
-
from
|
|
2
|
-
from emu_sv.dense_operator import DenseOperator
|
|
3
|
-
from emu_sv.sv_backend import SVBackend, SVConfig
|
|
4
|
-
from emu_base.base_classes import Results
|
|
5
|
-
from emu_base.base_classes.callback import AggregationType
|
|
1
|
+
from pulser.backend.results import Results
|
|
6
2
|
|
|
7
|
-
from
|
|
8
|
-
Callback,
|
|
3
|
+
from pulser.backend import (
|
|
9
4
|
BitStrings,
|
|
10
5
|
CorrelationMatrix,
|
|
11
6
|
Energy,
|
|
12
7
|
EnergyVariance,
|
|
8
|
+
EnergySecondMoment,
|
|
13
9
|
Expectation,
|
|
14
|
-
QubitDensity,
|
|
15
|
-
StateResult,
|
|
16
|
-
SecondMomentOfEnergy,
|
|
17
10
|
Fidelity,
|
|
11
|
+
Occupation,
|
|
12
|
+
StateResult,
|
|
18
13
|
)
|
|
19
14
|
|
|
15
|
+
from .dense_operator import DenseOperator
|
|
16
|
+
from .sv_backend import SVBackend, SVConfig
|
|
17
|
+
from .state_vector import StateVector, inner
|
|
18
|
+
|
|
20
19
|
__all__ = [
|
|
21
20
|
"__version__",
|
|
22
|
-
"StateVector",
|
|
23
|
-
"DenseOperator",
|
|
24
|
-
"inner",
|
|
25
|
-
"SVBackend",
|
|
26
|
-
"SVConfig",
|
|
27
|
-
"Callback",
|
|
28
21
|
"BitStrings",
|
|
29
22
|
"CorrelationMatrix",
|
|
23
|
+
"DenseOperator",
|
|
30
24
|
"Energy",
|
|
25
|
+
"EnergySecondMoment",
|
|
31
26
|
"EnergyVariance",
|
|
32
27
|
"Expectation",
|
|
33
28
|
"Fidelity",
|
|
34
|
-
"
|
|
35
|
-
"StateResult",
|
|
36
|
-
"SecondMomentOfEnergy",
|
|
37
|
-
"AggregationType",
|
|
29
|
+
"Occupation",
|
|
38
30
|
"Results",
|
|
31
|
+
"SVBackend",
|
|
32
|
+
"SVConfig",
|
|
33
|
+
"StateResult",
|
|
34
|
+
"StateVector",
|
|
35
|
+
"inner",
|
|
39
36
|
]
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
__version__ = "1.0.0"
|
|
38
|
+
__version__ = "2.0.0"
|
|
@@ -1,93 +1,115 @@
|
|
|
1
|
-
import math
|
|
2
1
|
import torch
|
|
3
2
|
|
|
4
|
-
from
|
|
5
|
-
from emu_base.base_classes.default_callbacks import (
|
|
6
|
-
QubitDensity,
|
|
7
|
-
EnergyVariance,
|
|
8
|
-
SecondMomentOfEnergy,
|
|
3
|
+
from pulser.backend import (
|
|
9
4
|
CorrelationMatrix,
|
|
5
|
+
EmulationConfig,
|
|
6
|
+
EnergySecondMoment,
|
|
7
|
+
EnergyVariance,
|
|
8
|
+
Occupation,
|
|
9
|
+
Energy,
|
|
10
10
|
)
|
|
11
|
-
from emu_base.base_classes.operator import Operator
|
|
12
11
|
|
|
13
|
-
from emu_sv import StateVector
|
|
12
|
+
from emu_sv.state_vector import StateVector
|
|
13
|
+
from emu_sv.dense_operator import DenseOperator
|
|
14
14
|
from emu_sv.hamiltonian import RydbergHamiltonian
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def
|
|
18
|
-
self:
|
|
17
|
+
def qubit_occupation_sv_impl(
|
|
18
|
+
self: Occupation,
|
|
19
|
+
*,
|
|
20
|
+
config: EmulationConfig,
|
|
21
|
+
state: StateVector,
|
|
22
|
+
hamiltonian: DenseOperator,
|
|
19
23
|
) -> torch.Tensor:
|
|
20
24
|
"""
|
|
21
|
-
Custom implementation of the
|
|
25
|
+
Custom implementation of the occupation ❬ψ|nᵢ|ψ❭ for the state vector solver.
|
|
22
26
|
"""
|
|
23
|
-
nqubits =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
qubit_density = torch.zeros(nqubits, dtype=torch.float64, device=state_tensor.device)
|
|
27
|
+
nqubits = state.n_qudits
|
|
28
|
+
occupation = torch.zeros(nqubits, dtype=torch.float64, device=state.vector.device)
|
|
27
29
|
for i in range(nqubits):
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
state_tensor = state.vector.view(2**i, 2, -1)
|
|
31
|
+
# nᵢ is a projector and therefore nᵢ == nᵢnᵢ
|
|
32
|
+
# ❬ψ|nᵢ|ψ❭ == ❬ψ|nᵢnᵢ|ψ❭ == ❬ψ|nᵢ * nᵢ|ψ❭ == ❬ϕ|ϕ❭ == |ϕ|**2
|
|
33
|
+
occupation[i] = torch.linalg.vector_norm(state_tensor[:, 1]) ** 2
|
|
34
|
+
return occupation.cpu()
|
|
30
35
|
|
|
31
36
|
|
|
32
37
|
def correlation_matrix_sv_impl(
|
|
33
38
|
self: CorrelationMatrix,
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
*,
|
|
40
|
+
config: EmulationConfig,
|
|
36
41
|
state: StateVector,
|
|
37
|
-
|
|
42
|
+
hamiltonian: DenseOperator,
|
|
38
43
|
) -> torch.Tensor:
|
|
39
44
|
"""
|
|
40
|
-
Custom implementation of the density-density correlation ❬ψ|nᵢnⱼ|ψ❭
|
|
41
|
-
|
|
45
|
+
Custom implementation of the density-density correlation ❬ψ|nᵢnⱼ|ψ❭
|
|
46
|
+
for the state vector solver.
|
|
42
47
|
TODO: extend to arbitrary two-point correlation ❬ψ|AᵢBⱼ|ψ❭
|
|
43
48
|
"""
|
|
44
|
-
nqubits =
|
|
45
|
-
state_tensor = state.vector.reshape((2,) * nqubits)
|
|
46
|
-
|
|
49
|
+
nqubits = state.n_qudits
|
|
47
50
|
correlation = torch.zeros(
|
|
48
|
-
nqubits, nqubits, dtype=torch.float64, device=
|
|
51
|
+
nqubits, nqubits, dtype=torch.float64, device=state.vector.device
|
|
49
52
|
)
|
|
50
53
|
|
|
51
54
|
for i in range(nqubits):
|
|
52
|
-
select_i =
|
|
55
|
+
select_i = state.vector.view(2**i, 2, -1)
|
|
56
|
+
select_i = select_i[:, 1]
|
|
53
57
|
for j in range(i, nqubits): # select the upper triangle
|
|
54
58
|
if i == j:
|
|
55
|
-
value =
|
|
59
|
+
value = torch.linalg.vector_norm(select_i) ** 2
|
|
60
|
+
correlation[j, j] = value
|
|
56
61
|
else:
|
|
57
|
-
|
|
62
|
+
select_i = select_i.view(2**i, 2 ** (j - i - 1), 2, -1)
|
|
63
|
+
select_ij = select_i[:, :, 1, :]
|
|
64
|
+
value = torch.linalg.vector_norm(select_ij) ** 2
|
|
65
|
+
correlation[i, j] = value
|
|
66
|
+
correlation[j, i] = value
|
|
58
67
|
|
|
59
|
-
|
|
60
|
-
correlation[j, i] = value
|
|
61
|
-
return correlation
|
|
68
|
+
return correlation.cpu()
|
|
62
69
|
|
|
63
70
|
|
|
64
71
|
def energy_variance_sv_impl(
|
|
65
72
|
self: EnergyVariance,
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
*,
|
|
74
|
+
config: EmulationConfig,
|
|
68
75
|
state: StateVector,
|
|
69
|
-
|
|
76
|
+
hamiltonian: RydbergHamiltonian,
|
|
70
77
|
) -> torch.Tensor:
|
|
71
78
|
"""
|
|
72
79
|
Custom implementation of the energy variance ❬ψ|H²|ψ❭-❬ψ|H|ψ❭² for the state vector solver.
|
|
73
80
|
"""
|
|
74
|
-
hstate =
|
|
81
|
+
hstate = hamiltonian * state.vector
|
|
75
82
|
h_squared = torch.vdot(hstate, hstate).real
|
|
76
83
|
energy = torch.vdot(state.vector, hstate).real
|
|
77
|
-
|
|
78
|
-
return
|
|
84
|
+
en_var: torch.Tensor = h_squared - energy**2
|
|
85
|
+
return en_var.cpu()
|
|
79
86
|
|
|
80
87
|
|
|
81
|
-
def
|
|
82
|
-
self:
|
|
83
|
-
|
|
84
|
-
|
|
88
|
+
def energy_second_moment_sv_impl(
|
|
89
|
+
self: EnergySecondMoment,
|
|
90
|
+
*,
|
|
91
|
+
config: EmulationConfig,
|
|
85
92
|
state: StateVector,
|
|
86
|
-
|
|
93
|
+
hamiltonian: RydbergHamiltonian,
|
|
87
94
|
) -> torch.Tensor:
|
|
88
95
|
"""
|
|
89
96
|
Custom implementation of the second moment of energy ❬ψ|H²|ψ❭
|
|
90
97
|
for the state vector solver.
|
|
91
98
|
"""
|
|
92
|
-
hstate =
|
|
93
|
-
|
|
99
|
+
hstate = hamiltonian * state.vector
|
|
100
|
+
en_2_mom: torch.Tensor = torch.vdot(hstate, hstate).real
|
|
101
|
+
return en_2_mom.cpu()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def energy_sv_impl(
|
|
105
|
+
self: Energy,
|
|
106
|
+
*,
|
|
107
|
+
config: EmulationConfig,
|
|
108
|
+
state: StateVector,
|
|
109
|
+
hamiltonian: RydbergHamiltonian,
|
|
110
|
+
) -> torch.Tensor:
|
|
111
|
+
"""
|
|
112
|
+
Custom implementation of the energy ❬ψ|H|ψ❭ for the state vector solver.
|
|
113
|
+
"""
|
|
114
|
+
en: torch.Tensor = hamiltonian.expect(state)
|
|
115
|
+
return en
|
emu_sv/dense_operator.py
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
import itertools
|
|
3
|
-
from
|
|
4
|
+
from functools import reduce
|
|
4
5
|
|
|
5
6
|
import torch
|
|
6
|
-
|
|
7
|
-
from
|
|
7
|
+
|
|
8
|
+
from typing import Sequence, Type
|
|
9
|
+
|
|
10
|
+
from emu_base import DEVICE_COUNT
|
|
8
11
|
from emu_sv.state_vector import StateVector
|
|
9
12
|
|
|
13
|
+
from pulser.backend import (
|
|
14
|
+
Operator,
|
|
15
|
+
State,
|
|
16
|
+
)
|
|
17
|
+
from pulser.backend.operator import FullOp, QuditOp
|
|
18
|
+
from pulser.backend.state import Eigenstate
|
|
19
|
+
|
|
10
20
|
dtype = torch.complex128
|
|
11
21
|
|
|
12
22
|
|
|
@@ -29,8 +39,8 @@ def _validate_operator_targets(operations: FullOp, nqubits: int) -> None:
|
|
|
29
39
|
)
|
|
30
40
|
|
|
31
41
|
|
|
32
|
-
class DenseOperator(Operator):
|
|
33
|
-
"""
|
|
42
|
+
class DenseOperator(Operator[complex, torch.Tensor, StateVector]):
|
|
43
|
+
"""DenseOperator in EMU-SV use dense matrices"""
|
|
34
44
|
|
|
35
45
|
def __init__(
|
|
36
46
|
self,
|
|
@@ -46,154 +56,145 @@ class DenseOperator(Operator):
|
|
|
46
56
|
|
|
47
57
|
def __matmul__(self, other: Operator) -> DenseOperator:
|
|
48
58
|
"""
|
|
49
|
-
|
|
50
|
-
self is applied after other.
|
|
59
|
+
Compose two DenseOperators via matrix multiplication.
|
|
51
60
|
|
|
52
61
|
Args:
|
|
53
|
-
other:
|
|
62
|
+
other: a DenseOperator instance.
|
|
54
63
|
|
|
55
64
|
Returns:
|
|
56
|
-
the
|
|
65
|
+
A new DenseOperator representing the product `self @ other`.
|
|
57
66
|
"""
|
|
58
67
|
assert isinstance(
|
|
59
68
|
other, DenseOperator
|
|
60
|
-
), "DenseOperator can only be multiplied with
|
|
61
|
-
|
|
69
|
+
), "DenseOperator can only be multiplied with a DenseOperator."
|
|
62
70
|
return DenseOperator(self.matrix @ other.matrix)
|
|
63
71
|
|
|
64
72
|
def __add__(self, other: Operator) -> DenseOperator:
|
|
65
73
|
"""
|
|
66
|
-
|
|
74
|
+
Element-wise addition of two DenseOperators.
|
|
67
75
|
|
|
68
76
|
Args:
|
|
69
|
-
other:
|
|
77
|
+
other: a DenseOperator instance.
|
|
70
78
|
|
|
71
79
|
Returns:
|
|
72
|
-
the
|
|
80
|
+
A new DenseOperator representing the sum.
|
|
73
81
|
"""
|
|
74
|
-
assert isinstance(
|
|
75
|
-
|
|
82
|
+
assert isinstance(
|
|
83
|
+
other, DenseOperator
|
|
84
|
+
), "DenseOperator can only be added to another DenseOperator."
|
|
76
85
|
return DenseOperator(self.matrix + other.matrix)
|
|
77
86
|
|
|
78
87
|
def __rmul__(self, scalar: complex) -> DenseOperator:
|
|
79
88
|
"""
|
|
80
|
-
|
|
89
|
+
Scalar multiplication of the DenseOperator.
|
|
81
90
|
|
|
82
91
|
Args:
|
|
83
|
-
scalar:
|
|
92
|
+
scalar: a number to scale the operator.
|
|
84
93
|
|
|
85
94
|
Returns:
|
|
86
|
-
|
|
95
|
+
A new DenseOperator scaled by the given scalar.
|
|
87
96
|
"""
|
|
88
97
|
|
|
89
|
-
return DenseOperator(self.matrix
|
|
98
|
+
return DenseOperator(scalar * self.matrix)
|
|
90
99
|
|
|
91
|
-
def
|
|
100
|
+
def apply_to(self, other: State) -> StateVector:
|
|
92
101
|
"""
|
|
93
|
-
|
|
102
|
+
Apply the DenseOperator to a given StateVector.
|
|
94
103
|
|
|
95
104
|
Args:
|
|
96
|
-
other:
|
|
105
|
+
other: a StateVector instance.
|
|
97
106
|
|
|
98
107
|
Returns:
|
|
99
|
-
the
|
|
108
|
+
A new StateVector after applying the operator.
|
|
100
109
|
"""
|
|
101
110
|
assert isinstance(
|
|
102
111
|
other, StateVector
|
|
103
|
-
), "DenseOperator can only be applied to
|
|
112
|
+
), "DenseOperator can only be applied to a StateVector."
|
|
104
113
|
|
|
105
114
|
return StateVector(self.matrix @ other.vector)
|
|
106
115
|
|
|
107
|
-
def expect(self, state: State) ->
|
|
116
|
+
def expect(self, state: State) -> torch.Tensor:
|
|
108
117
|
"""
|
|
109
|
-
Compute the expectation value of
|
|
118
|
+
Compute the expectation value of the operator with respect to a state.
|
|
110
119
|
|
|
111
120
|
Args:
|
|
112
|
-
state:
|
|
121
|
+
state: a StateVector instance.
|
|
113
122
|
|
|
114
123
|
Returns:
|
|
115
|
-
|
|
124
|
+
The expectation value as a float or complex number.
|
|
116
125
|
"""
|
|
117
126
|
assert isinstance(
|
|
118
127
|
state, StateVector
|
|
119
|
-
), "
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
**kwargs: Any,
|
|
132
|
-
) -> DenseOperator:
|
|
128
|
+
), "Only expectation values of StateVectors are supported."
|
|
129
|
+
|
|
130
|
+
return torch.vdot(state.vector, self.apply_to(state).vector).cpu()
|
|
131
|
+
|
|
132
|
+
@classmethod
|
|
133
|
+
def _from_operator_repr(
|
|
134
|
+
cls: Type[DenseOperator],
|
|
135
|
+
*,
|
|
136
|
+
eigenstates: Sequence[Eigenstate],
|
|
137
|
+
n_qudits: int,
|
|
138
|
+
operations: FullOp[complex],
|
|
139
|
+
) -> tuple[DenseOperator, FullOp[complex]]:
|
|
133
140
|
"""
|
|
134
|
-
|
|
141
|
+
Construct a DenseOperator from an operator representation.
|
|
135
142
|
|
|
136
143
|
Args:
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
operations: which bitstrings make up the state with what weight
|
|
140
|
-
operators: additional symbols to be used in operations
|
|
144
|
+
eigenstates: the eigenstates of the basis to use, e.g. ("r", "g") or ("0", "1").
|
|
145
|
+
n_qudits: number of qudits in the system.
|
|
146
|
+
operations: which bitstrings make up the state with what weight.
|
|
141
147
|
|
|
142
148
|
Returns:
|
|
143
|
-
|
|
149
|
+
A DenseOperator instance corresponding to the given representation.
|
|
144
150
|
"""
|
|
145
151
|
|
|
146
|
-
_validate_operator_targets(operations,
|
|
152
|
+
_validate_operator_targets(operations, n_qudits)
|
|
153
|
+
assert len(set(eigenstates)) == 2, "Only qubits are supported in EMU-SV."
|
|
147
154
|
|
|
148
|
-
operators_with_tensors: dict[str, torch.Tensor | QuditOp] = dict(
|
|
155
|
+
operators_with_tensors: dict[str, torch.Tensor | QuditOp] = dict()
|
|
149
156
|
|
|
150
|
-
|
|
151
|
-
if basis == {"r", "g"}:
|
|
157
|
+
if set(eigenstates) == {"r", "g"}:
|
|
152
158
|
# operators_with_tensors will now contain the basis for single qubit ops,
|
|
153
|
-
# and potentially user defined strings in terms of
|
|
159
|
+
# and potentially user defined strings in terms of {r, g} or {0, 1}
|
|
154
160
|
operators_with_tensors |= {
|
|
155
|
-
"gg": torch.tensor([[1.0, 0.0], [0.0, 0.0]], dtype=
|
|
156
|
-
"gr": torch.tensor([[0.0, 0.0], [1.0, 0.0]], dtype=
|
|
157
|
-
"rg": torch.tensor([[0.0, 1.0], [0.0, 0.0]], dtype=
|
|
158
|
-
"rr": torch.tensor([[0.0, 0.0], [0.0, 1.0]], dtype=
|
|
159
|
-
}
|
|
160
|
-
elif basis == {"0", "1"}:
|
|
161
|
-
# operators_with_tensors will now contain the basis for single qubit ops,
|
|
162
|
-
# and potentially user defined strings in terms of these
|
|
163
|
-
operators_with_tensors |= {
|
|
164
|
-
"00": torch.tensor([[1.0, 0.0], [0.0, 0.0]], dtype=torch.complex128),
|
|
165
|
-
"01": torch.tensor([[0.0, 0.0], [1.0, 0.0]], dtype=torch.complex128),
|
|
166
|
-
"10": torch.tensor([[0.0, 1.0], [0.0, 0.0]], dtype=torch.complex128),
|
|
167
|
-
"11": torch.tensor([[0.0, 0.0], [0.0, 1.0]], dtype=torch.complex128),
|
|
161
|
+
"gg": torch.tensor([[1.0, 0.0], [0.0, 0.0]], dtype=dtype),
|
|
162
|
+
"gr": torch.tensor([[0.0, 0.0], [1.0, 0.0]], dtype=dtype),
|
|
163
|
+
"rg": torch.tensor([[0.0, 1.0], [0.0, 0.0]], dtype=dtype),
|
|
164
|
+
"rr": torch.tensor([[0.0, 0.0], [0.0, 1.0]], dtype=dtype),
|
|
168
165
|
}
|
|
166
|
+
elif set(eigenstates) == {"0", "1"}:
|
|
167
|
+
raise NotImplementedError(
|
|
168
|
+
"{'0','1'} basis is related to XY Hamiltonian, which is not implemented"
|
|
169
|
+
)
|
|
169
170
|
else:
|
|
170
|
-
raise ValueError("
|
|
171
|
-
|
|
172
|
-
accum_res = torch.zeros(2**
|
|
173
|
-
for coeff,
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
171
|
+
raise ValueError("An unsupported basis of eigenstates has been provided.")
|
|
172
|
+
|
|
173
|
+
accum_res = torch.zeros(2**n_qudits, 2**n_qudits, dtype=dtype)
|
|
174
|
+
for coeff, oper_torch_with_target_qubits in operations:
|
|
175
|
+
|
|
176
|
+
def build_torch_operator_from_string(
|
|
177
|
+
oper: QuditOp | torch.Tensor,
|
|
178
|
+
) -> torch.Tensor:
|
|
179
|
+
if isinstance(oper, torch.Tensor):
|
|
180
|
+
return oper
|
|
181
|
+
|
|
182
|
+
result = torch.zeros((2, 2), dtype=dtype)
|
|
183
|
+
for opstr, coeff in oper.items():
|
|
184
|
+
tensor = build_torch_operator_from_string(
|
|
185
|
+
operators_with_tensors[opstr]
|
|
186
|
+
)
|
|
183
187
|
operators_with_tensors[opstr] = tensor
|
|
184
188
|
result += tensor * coeff
|
|
185
189
|
return result
|
|
186
190
|
|
|
187
|
-
|
|
191
|
+
single_qubit_gates = [torch.eye(2, dtype=dtype) for _ in range(n_qudits)]
|
|
188
192
|
|
|
189
|
-
for
|
|
190
|
-
factor =
|
|
191
|
-
for target_qubit in
|
|
192
|
-
|
|
193
|
+
for operator_torch, target_qubits in oper_torch_with_target_qubits:
|
|
194
|
+
factor = build_torch_operator_from_string(operator_torch)
|
|
195
|
+
for target_qubit in target_qubits:
|
|
196
|
+
single_qubit_gates[target_qubit] = factor
|
|
193
197
|
|
|
194
|
-
|
|
195
|
-
for single_qubit_operator in total_op_per_qubit[1:]:
|
|
196
|
-
dense_op = torch.kron(dense_op, single_qubit_operator)
|
|
198
|
+
accum_res += coeff * reduce(torch.kron, single_qubit_gates)
|
|
197
199
|
|
|
198
|
-
|
|
199
|
-
return DenseOperator(accum_res)
|
|
200
|
+
return DenseOperator(accum_res), operations
|