11
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
// See the License for the specific language governing permissions and
13
13
// limitations under the License.
14
- #pragma once
15
14
16
15
#include < numeric>
17
16
#include < utility>
23
22
24
23
#include " Error.hpp"
25
24
26
- #include < StateVectorLQubit .hpp>
25
+ #include < StateVectorCPU .hpp>
27
26
28
27
#include < iostream>
29
28
30
- namespace {
31
- using Pennylane::Util::AlignedAllocator;
32
- using Pennylane::Util::bestCPUMemoryModel;
33
- using Pennylane::Util::exp2;
34
- using Pennylane::Util::isPerfectPowerOf2;
35
- using Pennylane::Util::log2PerfectPower;
36
- using Pennylane::Util::ONE;
37
- using Pennylane::Util::squaredNorm;
38
- using Pennylane::Util::ZERO;
39
- } // namespace
40
-
41
- namespace Pennylane ::LightningQubit {
29
+ namespace Pennylane {
42
30
/* *
43
31
* @brief State-vector dynamic class.
44
32
*
@@ -47,16 +35,15 @@ namespace Pennylane::LightningQubit {
47
35
* quantum circuit simulation.
48
36
*
49
37
*/
50
- template <class fp_t = double >
51
- class StateVectorLQubitDynamic : public StateVectorLQubit < fp_t , StateVectorLQubitDynamic< fp_t >> {
38
+ template <class PrecisionT = double >
39
+ class StateVectorDynamicCPU : public StateVectorCPU <PrecisionT, StateVectorDynamicCPU<PrecisionT >> {
52
40
public:
53
- using PrecisionT = fp_t ;
54
- using ComplexT = std::complex<PrecisionT>;
55
- using MemoryStorageT = Pennylane::Util::MemoryStorageLocation::Internal ;
41
+ using BaseType = StateVectorCPU<PrecisionT, StateVectorDynamicCPU<PrecisionT>> ;
42
+
43
+ using ComplexPrecisionT = std::complex<PrecisionT> ;
56
44
57
45
private:
58
- using BaseType = StateVectorLQubit<PrecisionT, StateVectorLQubitDynamic<PrecisionT>>;
59
- std::vector<ComplexT, AlignedAllocator<ComplexT>> data_;
46
+ std::vector<ComplexPrecisionT, Util::AlignedAllocator<ComplexPrecisionT>> data_;
60
47
61
48
static constexpr PrecisionT epsilon_ = std::numeric_limits<PrecisionT>::epsilon() * 100 ;
62
49
@@ -75,23 +62,25 @@ class StateVectorLQubitDynamic : public StateVectorLQubit<fp_t, StateVectorLQubi
75
62
{
76
63
for (size_t i = 0 ; i < distance; i++) {
77
64
*second++ = std::move (*first);
78
- *first = ZERO<PrecisionT>();
65
+ *first = Util:: ZERO<PrecisionT>();
79
66
first++;
80
67
}
81
68
return second;
82
69
}
83
70
84
- inline void _scalar_mul_data (std::vector<ComplexT, AlignedAllocator<ComplexT>> &data,
85
- ComplexT scalar)
71
+ inline void _scalar_mul_data (
72
+ std::vector<ComplexPrecisionT, Util::AlignedAllocator<ComplexPrecisionT>> &data,
73
+ ComplexPrecisionT scalar)
86
74
{
87
75
std::transform (data.begin (), data.end (), data.begin (),
88
- [scalar](const ComplexT &elem) { return elem * scalar; });
76
+ [scalar](const ComplexPrecisionT &elem) { return elem * scalar; });
89
77
}
90
78
91
- inline void _normalize_data (std::vector<ComplexT, AlignedAllocator<ComplexT>> &data)
79
+ inline void
80
+ _normalize_data (std::vector<ComplexPrecisionT, Util::AlignedAllocator<ComplexPrecisionT>> &data)
92
81
{
93
- _scalar_mul_data (data,
94
- ONE<PrecisionT>() / std::sqrt (squaredNorm (data.data (), data.size ())));
82
+ _scalar_mul_data (data, Util::ONE<PrecisionT>() /
83
+ std::sqrt (Util:: squaredNorm (data.data (), data.size ())));
95
84
}
96
85
97
86
public:
@@ -102,29 +91,28 @@ class StateVectorLQubitDynamic : public StateVectorLQubit<fp_t, StateVectorLQubi
102
91
* @param threading Threading option the statevector to use
103
92
* @param memory_model Memory model the statevector will use
104
93
*/
105
- explicit StateVectorLQubitDynamic (size_t num_qubits,
106
- Threading threading = Threading::SingleThread,
107
- CPUMemoryModel memory_model = bestCPUMemoryModel())
94
+ explicit StateVectorDynamicCPU (size_t num_qubits, Threading threading = Threading::SingleThread,
95
+ CPUMemoryModel memory_model = bestCPUMemoryModel())
108
96
: BaseType{num_qubits, threading, memory_model},
109
- data_{exp2 (num_qubits), ZERO<PrecisionT>(),
110
- getAllocator<ComplexT >( // LCOV_EXCL_LINE
97
+ data_{Util:: exp2 (num_qubits), Util:: ZERO<PrecisionT>(),
98
+ getAllocator<ComplexPrecisionT >( // LCOV_EXCL_LINE
111
99
this ->memory_model_ )}
112
100
{
113
- data_[0 ] = ONE<PrecisionT>();
101
+ data_[0 ] = Util:: ONE<PrecisionT>();
114
102
}
115
103
116
104
/* *
117
105
* @brief Construct a statevector from another statevector
118
106
*
119
- * @tparam OtherDerived A derived type of StateVectorLQubit to use for
107
+ * @tparam OtherDerived A derived type of StateVectorCPU to use for
120
108
* construction.
121
109
* @param other Another statevector to construct the statevector from
122
110
*/
123
111
template <class OtherDerived >
124
- explicit StateVectorLQubitDynamic (const StateVectorLQubit <PrecisionT, OtherDerived> &other)
112
+ explicit StateVectorDynamicCPU (const StateVectorCPU <PrecisionT, OtherDerived> &other)
125
113
: BaseType(other.getNumQubits(), other.threading(), other.memoryModel()),
126
114
data_{other.getData (), other.getData () + other.getLength (),
127
- getAllocator<ComplexT >(this ->memory_model_ )}
115
+ getAllocator<ComplexPrecisionT >(this ->memory_model_ )}
128
116
{
129
117
}
130
118
@@ -136,13 +124,14 @@ class StateVectorLQubitDynamic : public StateVectorLQubit<fp_t, StateVectorLQubi
136
124
* @param threading Threading option the statevector to use
137
125
* @param memory_model Memory model the statevector will use
138
126
*/
139
- StateVectorLQubitDynamic (const ComplexT *other_data, size_t other_size,
140
- Threading threading = Threading::SingleThread,
141
- CPUMemoryModel memory_model = bestCPUMemoryModel())
142
- : BaseType(log2PerfectPower(other_size), threading, memory_model),
143
- data_{other_data, other_data + other_size, getAllocator<ComplexT>(this ->memory_model_ )}
127
+ StateVectorDynamicCPU (const ComplexPrecisionT *other_data, size_t other_size,
128
+ Threading threading = Threading::SingleThread,
129
+ CPUMemoryModel memory_model = bestCPUMemoryModel())
130
+ : BaseType(Util::log2PerfectPower(other_size), threading, memory_model),
131
+ data_{other_data, other_data + other_size,
132
+ getAllocator<ComplexPrecisionT>(this ->memory_model_ )}
144
133
{
145
- PL_ABORT_IF_NOT (isPerfectPowerOf2 (other_size),
134
+ PL_ABORT_IF_NOT (Util:: isPerfectPowerOf2 (other_size),
146
135
" The size of provided data must be a power of 2." );
147
136
}
148
137
@@ -156,79 +145,34 @@ class StateVectorLQubitDynamic : public StateVectorLQubit<fp_t, StateVectorLQubi
156
145
* @param memory_model Memory model the statevector will use
157
146
*/
158
147
template <class Alloc >
159
- explicit StateVectorLQubitDynamic (const std::vector<std::complex<PrecisionT>, Alloc> &other,
160
- Threading threading = Threading::SingleThread,
161
- CPUMemoryModel memory_model = bestCPUMemoryModel())
162
- : StateVectorLQubitDynamic (other.data(), other.size(), threading, memory_model)
148
+ explicit StateVectorDynamicCPU (const std::vector<std::complex<PrecisionT>, Alloc> &other,
149
+ Threading threading = Threading::SingleThread,
150
+ CPUMemoryModel memory_model = bestCPUMemoryModel())
151
+ : StateVectorDynamicCPU (other.data(), other.size(), threading, memory_model)
163
152
{
164
153
}
165
154
166
- StateVectorLQubitDynamic (const StateVectorLQubitDynamic &rhs) = default ;
167
- StateVectorLQubitDynamic (StateVectorLQubitDynamic &&) noexcept = default ;
168
-
169
- StateVectorLQubitDynamic &operator =(const StateVectorLQubitDynamic &) = default ;
170
- StateVectorLQubitDynamic &operator =(StateVectorLQubitDynamic &&) noexcept = default ;
171
-
172
- ~StateVectorLQubitDynamic () = default ;
173
-
174
- /* *
175
- * @brief Set the number of qubits represented by the statevector data.
176
- *
177
- * @return std::size_t
178
- */
179
- void setNumQubits (size_t qubits) noexcept { this ->num_qubits_ = qubits; }
180
-
181
- /* *
182
- * @brief Get underlying C-style data of the state-vector.
183
- */
184
- [[nodiscard]] auto getData () -> ComplexT * { return data_.data (); }
185
-
186
- /* *
187
- * @brief Get underlying C-style data of the state-vector.
188
- */
189
- [[nodiscard]] auto getData () const -> const ComplexT * { return data_.data (); }
155
+ StateVectorDynamicCPU (const StateVectorDynamicCPU &rhs) = default ;
156
+ StateVectorDynamicCPU (StateVectorDynamicCPU &&) noexcept = default ;
190
157
191
- /* *
192
- * @brief Get underlying data vector.
193
- */
194
- [[nodiscard]] auto getDataVector () -> std::vector<ComplexT, AlignedAllocator<ComplexT>> &
195
- {
196
- return data_;
197
- }
198
-
199
- /* *
200
- * @brief Get underlying data vector.
201
- */
202
- [[nodiscard]] auto getDataVector () const
203
- -> const std::vector<ComplexT, AlignedAllocator<ComplexT>> &
204
- {
205
- return data_;
206
- }
158
+ StateVectorDynamicCPU &operator =(const StateVectorDynamicCPU &) = default ;
159
+ StateVectorDynamicCPU &operator =(StateVectorDynamicCPU &&) noexcept = default ;
207
160
208
- /* *
209
- * @brief Update data of the class to new_data
210
- *
211
- * @param new_data data pointer to new data.
212
- * @param new_size size of underlying data storage.
213
- */
214
- void updateData (const ComplexT *new_data, size_t new_size)
215
- {
216
- PL_ASSERT (data_.size () == new_size);
217
- std::copy (new_data, new_data + new_size, data_.data ());
218
- }
161
+ ~StateVectorDynamicCPU () = default ;
219
162
220
163
/* *
221
164
* @brief Update data of the class to new_data
222
165
*
223
166
* @tparam Alloc Allocator type of std::vector to use for updating data.
224
167
* @param new_data std::vector contains data.
225
168
*/
226
- template <class Alloc > void updateData (const std::vector<ComplexT , Alloc> &new_data)
169
+ template <class Alloc > void updateData (const std::vector<ComplexPrecisionT , Alloc> &new_data)
227
170
{
228
- updateData (new_data.data (), new_data.size ());
171
+ assert (data_.size () == new_data.size ());
172
+ std::copy (new_data.data (), new_data.data () + new_data.size (), data_.data ());
229
173
}
230
174
231
- AlignedAllocator<ComplexT > allocator () const { return data_.get_allocator (); }
175
+ Util:: AlignedAllocator<ComplexPrecisionT > allocator () const { return data_.get_allocator (); }
232
176
233
177
[[nodiscard]] auto isValidWire (size_t wire) -> bool { return wire < this ->getNumQubits (); }
234
178
@@ -240,9 +184,9 @@ class StateVectorLQubitDynamic : public StateVectorLQubit<fp_t, StateVectorLQubi
240
184
* matrix after tracing out the complement of qubit `wire`.
241
185
*
242
186
* @param wire Index of the wire.
243
- * @return ComplexT
187
+ * @return ComplexPrecisionT
244
188
*/
245
- auto getSubsystemPurity (size_t wire) -> ComplexT
189
+ auto getSubsystemPurity (size_t wire) -> ComplexPrecisionT
246
190
{
247
191
PL_ABORT_IF_NOT (isValidWire (wire), " Invalid wire: The wire must be in the range of wires" );
248
192
@@ -257,11 +201,11 @@ class StateVectorLQubitDynamic : public StateVectorLQubit<fp_t, StateVectorLQubi
257
201
258
202
// The resulting 2x2 reduced density matrix of the complement system to
259
203
// qubit `wire`.
260
- std::vector<ComplexT > rho (4 , {0 , 0 });
204
+ std::vector<ComplexPrecisionT > rho (4 , {0 , 0 });
261
205
262
206
for (uint8_t i = 0 ; i < 2 ; i++) {
263
207
for (uint8_t j = 0 ; j < 2 ; j++) {
264
- ComplexT sum{0 , 0 };
208
+ ComplexPrecisionT sum{0 , 0 };
265
209
for (size_t k = 0 ; k < (sv_size / 2 ); k++) {
266
210
size_t idx_wire_0 = (/* upper_bits: */ (upper_mask & k) << 1UL ) +
267
211
/* lower_bits: */ (lower_mask & k);
@@ -278,7 +222,7 @@ class StateVectorLQubitDynamic : public StateVectorLQubit<fp_t, StateVectorLQubi
278
222
}
279
223
280
224
// Compute/Return the trace of rho**2
281
- return (rho[0 ] * rho[0 ]) + (ComplexT {2 , 0 } * rho[1 ] * rho[2 ]) + (rho[3 ] * rho[3 ]);
225
+ return (rho[0 ] * rho[0 ]) + (ComplexPrecisionT {2 , 0 } * rho[1 ] * rho[2 ]) + (rho[3 ] * rho[3 ]);
282
226
}
283
227
284
228
/* *
@@ -290,7 +234,7 @@ class StateVectorLQubitDynamic : public StateVectorLQubit<fp_t, StateVectorLQubi
290
234
*/
291
235
[[nodiscard]] auto checkSubsystemPurity (size_t wire, double eps = epsilon_) -> bool
292
236
{
293
- ComplexT purity = getSubsystemPurity (wire);
237
+ ComplexPrecisionT purity = getSubsystemPurity (wire);
294
238
return (std::abs (1.0 - purity.real ()) < eps) && (purity.imag () < eps);
295
239
}
296
240
@@ -335,8 +279,9 @@ class StateVectorLQubitDynamic : public StateVectorLQubit<fp_t, StateVectorLQubi
335
279
// Check if the reduced state-vector is the first-half
336
280
bool is_first_half = false ;
337
281
for (auto src = dst; src < data_.end (); std::advance (src, 2 * distance)) {
338
- is_first_half = std::any_of (src, src + static_cast <long long >(distance),
339
- [](ComplexT &e) { return e != ZERO<PrecisionT>(); });
282
+ is_first_half =
283
+ std::any_of (src, src + static_cast <long long >(distance),
284
+ [](ComplexPrecisionT &e) { return e != Util::ZERO<PrecisionT>(); });
340
285
if (is_first_half) {
341
286
break ;
342
287
}
@@ -366,8 +311,36 @@ class StateVectorLQubitDynamic : public StateVectorLQubit<fp_t, StateVectorLQubi
366
311
this ->setNumQubits (0 );
367
312
368
313
// the init state-vector
369
- data_.push_back (ONE<PrecisionT>());
314
+ data_.push_back (Util::ONE<PrecisionT>());
315
+ }
316
+
317
+ /* *
318
+ * @brief Get underlying C-style data of the state-vector.
319
+ */
320
+ [[nodiscard]] auto getData () -> ComplexPrecisionT * { return data_.data (); }
321
+
322
+ /* *
323
+ * @brief Get underlying C-style data of the state-vector.
324
+ */
325
+ [[nodiscard]] auto getData () const -> const ComplexPrecisionT * { return data_.data (); }
326
+
327
+ /* *
328
+ * @brief Get underlying data vector.
329
+ */
330
+ [[nodiscard]] auto getDataVector ()
331
+ -> std::vector<ComplexPrecisionT, Util::AlignedAllocator<ComplexPrecisionT>> &
332
+ {
333
+ return data_;
334
+ }
335
+
336
+ /* *
337
+ * @brief Get underlying data vector.
338
+ */
339
+ [[nodiscard]] auto getDataVector () const
340
+ -> const std::vector<ComplexPrecisionT, Util::AlignedAllocator<ComplexPrecisionT>> &
341
+ {
342
+ return data_;
370
343
}
371
344
};
372
345
373
- } // namespace Pennylane::LightningQubit
346
+ } // namespace Pennylane
0 commit comments