pennylane-qrack 0.21.1__tar.gz → 0.21.2__tar.gz
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 pennylane-qrack might be problematic. Click here for more details.
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/Makefile +1 -1
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/PKG-INFO +1 -1
- pennylane_qrack-0.21.2/catalyst/runtime/include/DataView.hpp +150 -0
- pennylane_qrack-0.21.2/catalyst/runtime/include/Exception.hpp +87 -0
- pennylane_qrack-0.21.2/catalyst/runtime/include/OQDRuntimeCAPI.h +56 -0
- pennylane_qrack-0.21.2/catalyst/runtime/include/QuantumDevice.hpp +576 -0
- pennylane_qrack-0.21.2/catalyst/runtime/include/RuntimeCAPI.h +117 -0
- pennylane_qrack-0.21.2/catalyst/runtime/include/Types.h +165 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/pennylane_qrack/_version.py +1 -1
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/pennylane_qrack.egg-info/PKG-INFO +1 -1
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/pennylane_qrack.egg-info/SOURCES.txt +6 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/CHANGELOG.md +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/CMakeLists.txt +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/LICENSE +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/MANIFEST.in +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/README.rst +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/pennylane_qrack/QrackAceDeviceConfig.toml +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/pennylane_qrack/QrackDeviceConfig.toml +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/pennylane_qrack/__init__.py +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/pennylane_qrack/qrack_ace_device.py +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/pennylane_qrack/qrack_device.cpp +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/pennylane_qrack/qrack_device.py +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/pennylane_qrack.egg-info/dependency_links.txt +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/pennylane_qrack.egg-info/entry_points.txt +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/pennylane_qrack.egg-info/requires.txt +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/pennylane_qrack.egg-info/top_level.txt +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/requirements.txt +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/setup.cfg +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/setup.py +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/tests/test_apply.py +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/tests/test_integration.py +0 -0
- {pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/tests/test_units.py +0 -0
|
@@ -24,7 +24,7 @@ help:
|
|
|
24
24
|
build-deps:
|
|
25
25
|
ifneq ($(OS),Windows_NT)
|
|
26
26
|
ifeq ($(QRACK_PRESENT),)
|
|
27
|
-
git clone https://github.com/unitaryfund/qrack.git; cd qrack; git checkout
|
|
27
|
+
git clone https://github.com/unitaryfund/qrack.git; cd qrack; git checkout 4b2d031d880a9ab3abbcc6dcd7d8a19464e2df89; cd ..
|
|
28
28
|
endif
|
|
29
29
|
mkdir -p qrack/build
|
|
30
30
|
ifeq ($(UNAME_S),Linux)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// Copyright 2023 Xanadu Quantum Technologies Inc.
|
|
2
|
+
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
#pragma once
|
|
16
|
+
|
|
17
|
+
#include <Exception.hpp>
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A multi-dimensional view for MemRef-like and std::vector<T> types.
|
|
21
|
+
*
|
|
22
|
+
* @tparam T The underlying data type
|
|
23
|
+
* @tparam R The Rank (R > 0)
|
|
24
|
+
*
|
|
25
|
+
* @note A forward iterator is implemented in this view for traversing over the entire
|
|
26
|
+
* elements of MemRef types rank-by-rank starting from the last dimension (R-1). For example,
|
|
27
|
+
* The DataView iterator for MemRef<T, 2> starts from index (0, 0) and traverses elements
|
|
28
|
+
* in the following order:
|
|
29
|
+
* (0, 0), ..., (0, sizes[1]-1), (1, 0), ..., (1, sizes[1]-1), ... (sizes[0]-1, sizes[1]-1).
|
|
30
|
+
*/
|
|
31
|
+
template <typename T, size_t R> class DataView {
|
|
32
|
+
private:
|
|
33
|
+
T *data_aligned;
|
|
34
|
+
size_t offset;
|
|
35
|
+
size_t sizes[R] = {0};
|
|
36
|
+
size_t strides[R] = {0};
|
|
37
|
+
|
|
38
|
+
public:
|
|
39
|
+
class iterator {
|
|
40
|
+
private:
|
|
41
|
+
const DataView<T, R> &view;
|
|
42
|
+
|
|
43
|
+
int64_t loc; // physical index
|
|
44
|
+
size_t indices[R] = {0};
|
|
45
|
+
|
|
46
|
+
public:
|
|
47
|
+
using iterator_category = std::forward_iterator_tag; // LCOV_EXCL_LINE
|
|
48
|
+
using value_type = T; // LCOV_EXCL_LINE
|
|
49
|
+
using difference_type = std::ptrdiff_t; // LCOV_EXCL_LINE
|
|
50
|
+
using pointer = T *; // LCOV_EXCL_LINE
|
|
51
|
+
using reference = T &; // LCOV_EXCL_LINE
|
|
52
|
+
|
|
53
|
+
iterator(const DataView<T, R> &_view, int64_t begin_idx) : view(_view), loc(begin_idx) {}
|
|
54
|
+
pointer operator->() const { return &view.data_aligned[loc]; }
|
|
55
|
+
reference operator*() const { return view.data_aligned[loc]; }
|
|
56
|
+
iterator &operator++()
|
|
57
|
+
{
|
|
58
|
+
int64_t next_axis = -1;
|
|
59
|
+
int64_t idx;
|
|
60
|
+
for (int64_t i = R; i > 0; --i) {
|
|
61
|
+
idx = i - 1;
|
|
62
|
+
RT_ASSERT(view.sizes[idx] > 0);
|
|
63
|
+
if (indices[idx]++ < view.sizes[idx] - 1) {
|
|
64
|
+
next_axis = idx;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
indices[idx] = 0;
|
|
68
|
+
loc -= (view.sizes[idx] - 1) * view.strides[idx];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
loc = next_axis == -1 ? -1 : loc + view.strides[next_axis];
|
|
72
|
+
return *this;
|
|
73
|
+
}
|
|
74
|
+
iterator operator++(int)
|
|
75
|
+
{
|
|
76
|
+
auto tmp = *this;
|
|
77
|
+
int64_t next_axis = -1;
|
|
78
|
+
int64_t idx;
|
|
79
|
+
for (int64_t i = R; i > 0; --i) {
|
|
80
|
+
idx = i - 1;
|
|
81
|
+
RT_ASSERT(view.sizes[idx] > 0);
|
|
82
|
+
if (indices[idx]++ < view.sizes[idx] - 1) {
|
|
83
|
+
next_axis = idx;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
indices[idx] = 0;
|
|
87
|
+
loc -= (view.sizes[idx] - 1) * view.strides[idx];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
loc = next_axis == -1 ? -1 : loc + view.strides[next_axis];
|
|
91
|
+
return tmp;
|
|
92
|
+
}
|
|
93
|
+
bool operator==(const iterator &other) const
|
|
94
|
+
{
|
|
95
|
+
return (loc == other.loc && view.data_aligned == other.view.data_aligned);
|
|
96
|
+
}
|
|
97
|
+
bool operator!=(const iterator &other) const { return !(*this == other); }
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
explicit DataView(std::vector<T> &buffer) : data_aligned(buffer.data()), offset(0)
|
|
101
|
+
{
|
|
102
|
+
static_assert(R == 1, "[Class: DataView] Assertion: R == 1");
|
|
103
|
+
sizes[0] = buffer.size();
|
|
104
|
+
strides[0] = 1;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
explicit DataView(T *_data_aligned, size_t _offset, const size_t *_sizes,
|
|
108
|
+
const size_t *_strides)
|
|
109
|
+
: data_aligned(_data_aligned), offset(_offset)
|
|
110
|
+
{
|
|
111
|
+
static_assert(R > 0, "[Class: DataView] Assertion: R > 0");
|
|
112
|
+
if (_sizes != nullptr && _strides != nullptr) {
|
|
113
|
+
for (size_t i = 0; i < R; i++) {
|
|
114
|
+
sizes[i] = _sizes[i];
|
|
115
|
+
strides[i] = _strides[i];
|
|
116
|
+
}
|
|
117
|
+
} // else sizes = {0}, strides = {0}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
[[nodiscard]] auto size() const -> size_t
|
|
121
|
+
{
|
|
122
|
+
if (!data_aligned) {
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
size_t tsize = 1;
|
|
127
|
+
for (size_t i = 0; i < R; i++) {
|
|
128
|
+
tsize *= sizes[i];
|
|
129
|
+
}
|
|
130
|
+
return tsize;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
template <typename... I> T &operator()(I... idxs) const
|
|
134
|
+
{
|
|
135
|
+
static_assert(sizeof...(idxs) == R,
|
|
136
|
+
"[Class: DataView] Error in Catalyst Runtime: Wrong number of indices");
|
|
137
|
+
size_t indices[] = {static_cast<size_t>(idxs)...};
|
|
138
|
+
|
|
139
|
+
size_t loc = offset;
|
|
140
|
+
for (size_t axis = 0; axis < R; axis++) {
|
|
141
|
+
RT_ASSERT(indices[axis] < sizes[axis]);
|
|
142
|
+
loc += indices[axis] * strides[axis];
|
|
143
|
+
}
|
|
144
|
+
return data_aligned[loc];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
iterator begin() { return iterator{*this, static_cast<int64_t>(offset)}; }
|
|
148
|
+
|
|
149
|
+
iterator end() { return iterator{*this, -1}; }
|
|
150
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Copyright 2023 Xanadu Quantum Technologies Inc.
|
|
2
|
+
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
#pragma once
|
|
16
|
+
|
|
17
|
+
#include <exception>
|
|
18
|
+
#include <iostream>
|
|
19
|
+
|
|
20
|
+
#include <sstream>
|
|
21
|
+
#include <string>
|
|
22
|
+
#include <type_traits>
|
|
23
|
+
#include <utility>
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @brief Macro that throws `RuntimeException` with given message.
|
|
27
|
+
*/
|
|
28
|
+
#define RT_FAIL(message) Catalyst::Runtime::_abort((message), __FILE__, __LINE__, __func__)
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @brief Macro that throws `RuntimeException` if expression evaluates
|
|
32
|
+
* to true.
|
|
33
|
+
*/
|
|
34
|
+
#define RT_FAIL_IF(expression, message) \
|
|
35
|
+
if ((expression)) { \
|
|
36
|
+
RT_FAIL(message); \
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @brief Macro that throws `RuntimeException` with the given expression
|
|
41
|
+
* and source location if expression evaluates to false.
|
|
42
|
+
*/
|
|
43
|
+
#define RT_ASSERT(expression) RT_FAIL_IF(!(expression), "Assertion: " #expression)
|
|
44
|
+
|
|
45
|
+
namespace Catalyst::Runtime {
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @brief This is the general exception thrown by Catalyst for runtime errors
|
|
49
|
+
* that is derived from `std::exception`.
|
|
50
|
+
*/
|
|
51
|
+
class RuntimeException : public std::exception {
|
|
52
|
+
private:
|
|
53
|
+
const std::string err_msg;
|
|
54
|
+
|
|
55
|
+
public:
|
|
56
|
+
explicit RuntimeException(std::string msg) noexcept
|
|
57
|
+
: err_msg{std::move(msg)} {} // LCOV_EXCL_LINE
|
|
58
|
+
~RuntimeException() override = default; // LCOV_EXCL_LINE
|
|
59
|
+
|
|
60
|
+
RuntimeException(const RuntimeException &) = default;
|
|
61
|
+
RuntimeException(RuntimeException &&) noexcept = default;
|
|
62
|
+
|
|
63
|
+
RuntimeException &operator=(const RuntimeException &) = delete;
|
|
64
|
+
RuntimeException &operator=(RuntimeException &&) = delete;
|
|
65
|
+
|
|
66
|
+
[[nodiscard]] auto what() const noexcept -> const char * override
|
|
67
|
+
{
|
|
68
|
+
return err_msg.c_str();
|
|
69
|
+
} // LCOV_EXCL_LINE
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @brief Throws a `RuntimeException` with the given error message.
|
|
74
|
+
*
|
|
75
|
+
* @note This is not supposed to be called directly.
|
|
76
|
+
*/
|
|
77
|
+
[[noreturn]] inline void _abort(const char *message, const char *file_name, size_t line,
|
|
78
|
+
const char *function_name)
|
|
79
|
+
{
|
|
80
|
+
std::stringstream sstream;
|
|
81
|
+
sstream << "[" << file_name << ":" << line << "][Function:" << function_name
|
|
82
|
+
<< "] Error in Catalyst Runtime: " << message;
|
|
83
|
+
|
|
84
|
+
throw RuntimeException(sstream.str());
|
|
85
|
+
} // LCOV_EXCL_LINE
|
|
86
|
+
|
|
87
|
+
} // namespace Catalyst::Runtime
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Copyright 2025 Xanadu Quantum Technologies Inc.
|
|
2
|
+
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
#pragma once
|
|
16
|
+
#ifndef OQDRUNTIMECAPI_H
|
|
17
|
+
#define OQDRUNTIMECAPI_H
|
|
18
|
+
|
|
19
|
+
#include <array>
|
|
20
|
+
#include <cstdint>
|
|
21
|
+
|
|
22
|
+
#include "Exception.hpp"
|
|
23
|
+
#include "Types.h"
|
|
24
|
+
|
|
25
|
+
#ifdef __cplusplus
|
|
26
|
+
extern "C" {
|
|
27
|
+
#endif
|
|
28
|
+
|
|
29
|
+
struct Beam {
|
|
30
|
+
int64_t transition_index;
|
|
31
|
+
double rabi;
|
|
32
|
+
double detuning;
|
|
33
|
+
std::array<int64_t, 3> polarization;
|
|
34
|
+
std::array<int64_t, 3> wavevector;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
struct Pulse {
|
|
38
|
+
Beam *beam;
|
|
39
|
+
size_t target;
|
|
40
|
+
double duration;
|
|
41
|
+
double phase;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// OQD Runtime Instructions
|
|
45
|
+
void __catalyst__oqd__rt__initialize();
|
|
46
|
+
void __catalyst__oqd__rt__finalize(const std::string &openapl_file_name);
|
|
47
|
+
void __catalyst__oqd__ion(const std::string &ion_specs);
|
|
48
|
+
void __catalyst__oqd__modes(const std::vector<std::string> &phonon_specs);
|
|
49
|
+
Pulse *__catalyst__oqd__pulse(QUBIT *qubit, double duration, double phase, Beam *beam);
|
|
50
|
+
void __catalyst__oqd__ParallelProtocol(Pulse **pulses, size_t n);
|
|
51
|
+
|
|
52
|
+
#ifdef __cplusplus
|
|
53
|
+
} // extern "C"
|
|
54
|
+
#endif
|
|
55
|
+
|
|
56
|
+
#endif
|
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
// Copyright 2022-2025 Xanadu Quantum Technologies Inc.
|
|
2
|
+
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
#pragma once
|
|
16
|
+
|
|
17
|
+
#include <complex>
|
|
18
|
+
#include <memory>
|
|
19
|
+
#include <optional>
|
|
20
|
+
#include <random>
|
|
21
|
+
#include <vector>
|
|
22
|
+
|
|
23
|
+
#include "DataView.hpp"
|
|
24
|
+
#include "Types.h"
|
|
25
|
+
|
|
26
|
+
// A helper template macro to generate the <IDENTIFIER>Factory function by
|
|
27
|
+
// calling <CONSTRUCTOR>(kwargs). Check the Custom Devices guideline for details:
|
|
28
|
+
// https://docs.pennylane.ai/projects/catalyst/en/stable/dev/custom_devices.html
|
|
29
|
+
#define GENERATE_DEVICE_FACTORY(IDENTIFIER, CONSTRUCTOR) \
|
|
30
|
+
extern "C" Catalyst::Runtime::QuantumDevice *IDENTIFIER##Factory(const char *kwargs) \
|
|
31
|
+
{ \
|
|
32
|
+
return new CONSTRUCTOR(std::string(kwargs)); \
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
namespace Catalyst::Runtime {
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @brief Interface class for Catalyst Runtime device backends.
|
|
39
|
+
*
|
|
40
|
+
* All Catalyst device plugins must implement this class and distribute it as a shared library.
|
|
41
|
+
* See https://docs.pennylane.ai/projects/catalyst/en/stable/dev/custom_devices.html for details.
|
|
42
|
+
*
|
|
43
|
+
* The `QuantumDevice` interface methods can broadly be categorized into device management functions
|
|
44
|
+
* that do not directly impact the computation or quantum state (think qubit management,
|
|
45
|
+
* execution configuration, or recording functionality), and computation functions like quantum
|
|
46
|
+
* gates and measurement procedures.
|
|
47
|
+
* In addition, not all methods are required to be implemented by a plugin, as some features are
|
|
48
|
+
* opt-in or not applicable to all device types. Optional features will be marked as such in the
|
|
49
|
+
* method description and contain a stub implementation.
|
|
50
|
+
*/
|
|
51
|
+
struct QuantumDevice {
|
|
52
|
+
QuantumDevice() = default; // LCOV_EXCL_LINE
|
|
53
|
+
virtual ~QuantumDevice() = default; // LCOV_EXCL_LINE
|
|
54
|
+
|
|
55
|
+
QuantumDevice &operator=(const QuantumDevice &) = delete;
|
|
56
|
+
QuantumDevice(const QuantumDevice &) = delete;
|
|
57
|
+
QuantumDevice(QuantumDevice &&) = delete;
|
|
58
|
+
QuantumDevice &operator=(QuantumDevice &&) = delete;
|
|
59
|
+
|
|
60
|
+
// ----------------------------------------
|
|
61
|
+
// QUBIT MANAGEMENT
|
|
62
|
+
// ----------------------------------------
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @brief Allocate an array of qubits.
|
|
66
|
+
*
|
|
67
|
+
* The operation should perform the necessary steps to make the given number of qubits
|
|
68
|
+
* available in a clean |0> state. At minimum, a device must be able to execute this
|
|
69
|
+
* call once before any quantum operations have been run. Handling multiple allocation calls
|
|
70
|
+
* is optional and only useful if the device intends to support dynamic qubit allocations
|
|
71
|
+
* (that is allocations during quantum program execution).
|
|
72
|
+
*
|
|
73
|
+
* The values returned by this operation are integer IDs that will be used to address the
|
|
74
|
+
* allocated qubits in subsequent operations (like gates or measurements). There are no
|
|
75
|
+
* restrictions on the values these IDs can take, but the IDs of all active qubits must not
|
|
76
|
+
* overlap, and once an ID is mapped to a particular qubit that ID must not change until the
|
|
77
|
+
* qubit is explicitly freed.
|
|
78
|
+
*
|
|
79
|
+
* While devices may choose not to distinguish between logical and device IDs, having a logical
|
|
80
|
+
* qubit labeling system can help catch program errors such as addressing previously freed
|
|
81
|
+
* qubits. An example implementation can be found in `QubitManager.hpp`. Devices are also free
|
|
82
|
+
* to disable a resource without physically freeing it, allowing for faster dynamic allocations.
|
|
83
|
+
*
|
|
84
|
+
* @param num_qubits The number of qubits to allocate.
|
|
85
|
+
*
|
|
86
|
+
* @return `std::vector<QubitIdType>` Array of qubit IDs.
|
|
87
|
+
*/
|
|
88
|
+
virtual auto AllocateQubits(size_t num_qubits) -> std::vector<QubitIdType> = 0;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @brief Release all qubits and reset device state.
|
|
92
|
+
*
|
|
93
|
+
* This operation can be considered a device-wide quantum resource release, regardless of how
|
|
94
|
+
* many allocation calls have been run (although on devices with static allocation there would
|
|
95
|
+
* only be one matching `AllocateQubits` - `ReleaseAllQubits` pair).
|
|
96
|
+
* After executing this call, a device should be in a state ready to allocate new qubits and
|
|
97
|
+
* execute another quantum program. Configurable execution parameters, whether explicit in
|
|
98
|
+
* the interface (like shots) or internal via the constructor kwargs, should be maintained
|
|
99
|
+
* in the same state as when entering this method.
|
|
100
|
+
*/
|
|
101
|
+
virtual void ReleaseAllQubits() = 0;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @brief Get the number of currently allocated qubits.
|
|
105
|
+
*
|
|
106
|
+
* @return `size_t`
|
|
107
|
+
*/
|
|
108
|
+
virtual auto GetNumQubits() const -> size_t = 0;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @brief (Optional) Allocate a qubit.
|
|
112
|
+
*
|
|
113
|
+
* Allocate a new qubit in the |0> state. This method is only needed for devices with dynamic
|
|
114
|
+
* qubit allocation.
|
|
115
|
+
*
|
|
116
|
+
* See `AllocateQubits` for more details on allocation semantics.
|
|
117
|
+
*
|
|
118
|
+
* @return `QubitIdType` Qubit ID.
|
|
119
|
+
*/
|
|
120
|
+
virtual auto AllocateQubit() -> QubitIdType
|
|
121
|
+
{
|
|
122
|
+
RT_FAIL("Dynamic qubit allocation is unsupported by device");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @brief (Optional) Release a qubit.
|
|
127
|
+
*
|
|
128
|
+
* Release the provided qubit. This method is only needed for devices with dynamic qubit
|
|
129
|
+
* allocation.
|
|
130
|
+
*
|
|
131
|
+
* Note that the interface does not require the qubit to be in a particular state. The behaviour
|
|
132
|
+
* for releasing an entangled qubit is left up to the device, and could include:
|
|
133
|
+
* - raising a runtime error (assuming the device can detect impure states)
|
|
134
|
+
* - continuing execution with an entangled but inaccessible qubit
|
|
135
|
+
* - resetting the qubit with or without measuring its state
|
|
136
|
+
*
|
|
137
|
+
* Opposite of `AllocateQubit`.
|
|
138
|
+
*
|
|
139
|
+
* @param qubit ID of the qubit to release.
|
|
140
|
+
*/
|
|
141
|
+
virtual void ReleaseQubit(QubitIdType qubit)
|
|
142
|
+
{
|
|
143
|
+
RT_FAIL("Dynamic qubit release is unsupported by device");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ----------------------------------------
|
|
147
|
+
// EXECUTION MANAGEMENT
|
|
148
|
+
// ----------------------------------------
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @brief Set the number of execution shots.
|
|
152
|
+
*
|
|
153
|
+
* The Runtime will call this function to set the number of times the quantum execution is to
|
|
154
|
+
* be repeated. Generally, it will be called once before any quantum instructions are run, but
|
|
155
|
+
* some devices may choose to support it at arbitrary points in the program if they have the
|
|
156
|
+
* capability to simulate shot noise.
|
|
157
|
+
*
|
|
158
|
+
* Devices with no or restricted support are encouraged to raise a runtime error.
|
|
159
|
+
*
|
|
160
|
+
* @param shots Shot number.
|
|
161
|
+
*/
|
|
162
|
+
virtual void SetDeviceShots(size_t shots) = 0;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* @brief Get the number of execution shots.
|
|
166
|
+
*
|
|
167
|
+
* @return `size_t` Shot number.
|
|
168
|
+
*/
|
|
169
|
+
virtual auto GetDeviceShots() const -> size_t = 0;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @brief (Optional) Set the PRNG of the device.
|
|
173
|
+
*
|
|
174
|
+
* The Catalyst runtime enables seeded program execution on non-hardware devices.
|
|
175
|
+
* A random number generator instance is managed by the runtime to predictably
|
|
176
|
+
* generate results for non-deterministic programs, such as those involving `Measure`
|
|
177
|
+
* calls.
|
|
178
|
+
* Devices implementing support for this feature do not need to use the provided
|
|
179
|
+
* PRNG instance as their sole source of randomness, but it is expected that the
|
|
180
|
+
* the same instance state will predictably and reproducibly generate the same
|
|
181
|
+
* program results (considering all random processes in the device).
|
|
182
|
+
* It is also expected that the provided PRNG state is evolved sufficiently so that two copies
|
|
183
|
+
* of a device provided with the same PRNG instance do not produce identical results when run
|
|
184
|
+
* in succession.
|
|
185
|
+
* Note that the provided PRNG instance is not thread-locked, and devices wishing to use it
|
|
186
|
+
* across internal threads will need to provide their own thread-safety.
|
|
187
|
+
*
|
|
188
|
+
* @param gen Pointer to a Catalyst-managed Mersenne Twister instance.
|
|
189
|
+
*/
|
|
190
|
+
virtual void SetDevicePRNG(std::mt19937 *gen) {};
|
|
191
|
+
|
|
192
|
+
// ----------------------------------------
|
|
193
|
+
// QUANTUM OPERATIONS
|
|
194
|
+
// ----------------------------------------
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @brief Apply an arbitrary quantum gate to the device.
|
|
198
|
+
*
|
|
199
|
+
* This instruction is opaque to the chosen quantum operation, which is specified via a
|
|
200
|
+
* string identifier. Additionally, an array of floating point parameters can be supplied,
|
|
201
|
+
* as well certain quantum modifiers, like the Hermitian adjoint (inverse) and control qubits.
|
|
202
|
+
* If the supplied combination of parameters is invalid or unsupported, the device should
|
|
203
|
+
* raise a runtime error.
|
|
204
|
+
*
|
|
205
|
+
* @param name A string identifier for the operation to apply.
|
|
206
|
+
* @param params Float parameters for parametric gates (may be empty).
|
|
207
|
+
* @param wires Qubits to apply the operation to.
|
|
208
|
+
* @param inverse Apply the inverse (Hermitian adjoint) of the operation.
|
|
209
|
+
* @param controlled_wires Control qubits applied to the operation.
|
|
210
|
+
* @param controlled_values Control values associated to the control qubits (equal length).
|
|
211
|
+
*/
|
|
212
|
+
virtual void NamedOperation(const std::string &name, const std::vector<double> ¶ms,
|
|
213
|
+
const std::vector<QubitIdType> &wires, bool inverse = false,
|
|
214
|
+
const std::vector<QubitIdType> &controlled_wires = {},
|
|
215
|
+
const std::vector<bool> &controlled_values = {}) = 0;
|
|
216
|
+
/**
|
|
217
|
+
* @brief Perform a computational-basis measurement on one qubit.
|
|
218
|
+
*
|
|
219
|
+
* This instruction is generally used for mid-circuit measurements, implementing an immediate,
|
|
220
|
+
* random projective measurement and producing a classical result as output. However, it may
|
|
221
|
+
* also be used in terminal measurements where the measurement processes below are unsupported.
|
|
222
|
+
*
|
|
223
|
+
* @param wire The qubit to measure.
|
|
224
|
+
* @param postselect Optional parameter to force the result to the provided state (roughly
|
|
225
|
+
* equivalent to post-selection).
|
|
226
|
+
*
|
|
227
|
+
* @return `Result` The measurement result.
|
|
228
|
+
*/
|
|
229
|
+
virtual auto Measure(QubitIdType wire, std::optional<int32_t> postselect) -> Result = 0;
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @brief (Optional) Apply an arbitrary unitary matrix to the device.
|
|
233
|
+
*
|
|
234
|
+
* Instead of identifying an operation by name, this instruction uses the mathematical
|
|
235
|
+
* representation of a quantum operator as a unitary matrix in the computational basis.
|
|
236
|
+
*
|
|
237
|
+
* See `NamedOperation` for additional gate semantics.
|
|
238
|
+
*
|
|
239
|
+
* @param matrix A 1D array representation of the matrix in row-major format.
|
|
240
|
+
* @param wires Qubits to apply the operation to.
|
|
241
|
+
* @param inverse Apply the inverse of the operation.
|
|
242
|
+
* @param controlled_wires Control qubits applied to the operation.
|
|
243
|
+
* @param controlled_values Control values associated to the control qubits (equal length).
|
|
244
|
+
*/
|
|
245
|
+
virtual void MatrixOperation(const std::vector<std::complex<double>> &matrix,
|
|
246
|
+
const std::vector<QubitIdType> &wires, bool inverse = false,
|
|
247
|
+
const std::vector<QubitIdType> &controlled_wires = {},
|
|
248
|
+
const std::vector<bool> &controlled_values = {})
|
|
249
|
+
{
|
|
250
|
+
RT_FAIL("MatrixOperation is unsupported by device");
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* @brief (Optional) Initialize qubits to a computational basis state.
|
|
255
|
+
*
|
|
256
|
+
* This instruction initializes a set of qubits to the provided computational basis state.
|
|
257
|
+
* Although the instruction is typically used for state initialization at the beginning of
|
|
258
|
+
* a quantum program, devices are encouraged to support arbitrary re-initialization if
|
|
259
|
+
* capable. Reinitialization on a subset of qubits is equivalent to deallocation -> allocation
|
|
260
|
+
* -> initialization on the same set of qubits.
|
|
261
|
+
* See `ReleaseQubit` for caveats around releasing / resetting entangled qubits.
|
|
262
|
+
*
|
|
263
|
+
* @param n Bitstring representation of the basis state |n>, stored as a Byte-array.
|
|
264
|
+
* @param wires The qubits to initialize.
|
|
265
|
+
*/
|
|
266
|
+
virtual void SetBasisState(DataView<int8_t, 1> &n, std::vector<QubitIdType> &wires)
|
|
267
|
+
{
|
|
268
|
+
RT_FAIL("SetBasisState is unsupported by device");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* @brief (Optional) Initialize qubits to an arbitrary quantum state.
|
|
273
|
+
*
|
|
274
|
+
* Like `SetBasisState`, but instead of initializing to a single computational basis state
|
|
275
|
+
* this instruction works with a vector of complex amplitudes, one for each possible basis
|
|
276
|
+
* state.
|
|
277
|
+
*
|
|
278
|
+
* @param state Quantum state vector of size 2^len(wires).
|
|
279
|
+
* @param wires The qubits to initialize.
|
|
280
|
+
*/
|
|
281
|
+
virtual void SetState(DataView<std::complex<double>, 1> &state, std::vector<QubitIdType> &wires)
|
|
282
|
+
{
|
|
283
|
+
RT_FAIL("SetState is unsupported by device");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ----------------------------------------
|
|
287
|
+
// QUANTUM OBSERVABLES
|
|
288
|
+
// ----------------------------------------
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* @brief (Optional) Construct a named observable.
|
|
292
|
+
*
|
|
293
|
+
* This operation instructs the device to generate a named observable based on the provided enum
|
|
294
|
+
* `id`, which will be one of {Identity, PauliX, PauliY, PauliZ, Hermitian}. The Hermitian kind
|
|
295
|
+
* uses additional data to define the observable from a 2D complex matrix, which is expected to
|
|
296
|
+
* be Hermitian. If this is not the case, the device is free to raise an error or produce
|
|
297
|
+
* undefined behaviour. Additionally, the operation accepts a list of target qubits whose length
|
|
298
|
+
* is expected to match the dimensionality of the observable.
|
|
299
|
+
*
|
|
300
|
+
* The observable system in the Catalyst Runtime relies primarily on the device's implementation
|
|
301
|
+
* for support. As such, the device is free to represent observables in any way they wish.
|
|
302
|
+
* Before using an observable in measurement processes, Catalyst will invoke one or more
|
|
303
|
+
* observable methods from the interface. The device is expected to process and cache the
|
|
304
|
+
* information in such a way that it is ready to use in a subsequent measurement process call.
|
|
305
|
+
* The device must return an ID than unambiguously identifies the requested observable, but this
|
|
306
|
+
* ID is not required to be unique across different calls supplied with the same information.
|
|
307
|
+
*
|
|
308
|
+
* @param id The name of the observable.
|
|
309
|
+
* @param matrix The matrix of data to use for a Hermitian observable (unused otherwise).
|
|
310
|
+
* @param wires Qubits the observable applies to.
|
|
311
|
+
*
|
|
312
|
+
* @return `ObsIdType` ID of the constructed observable.
|
|
313
|
+
*/
|
|
314
|
+
virtual auto Observable(ObsId id, const std::vector<std::complex<double>> &matrix,
|
|
315
|
+
const std::vector<QubitIdType> &wires) -> ObsIdType
|
|
316
|
+
{
|
|
317
|
+
RT_FAIL("Observable is unsupported by device");
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* @brief (Optional) Construct a tensor product of existing observables (prod).
|
|
322
|
+
*
|
|
323
|
+
* Given a list of observable IDs, construct the tensor product of all supplied observables.
|
|
324
|
+
* If wires overlap across observables, the device should raise an error unless it can produce
|
|
325
|
+
* sensible results.
|
|
326
|
+
*
|
|
327
|
+
* See `Observable` for additional details.
|
|
328
|
+
*
|
|
329
|
+
* @param obs The list of observables IDs.
|
|
330
|
+
*
|
|
331
|
+
* @return `ObsIdType` ID of the constructed observable.
|
|
332
|
+
*/
|
|
333
|
+
virtual auto TensorObservable(const std::vector<ObsIdType> &obs) -> ObsIdType
|
|
334
|
+
{
|
|
335
|
+
RT_FAIL("TensorObservable is unsupported by device");
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* @brief (Optional) Construct a linear combination of existing observables (sum).
|
|
340
|
+
*
|
|
341
|
+
* Given a list of observable IDs and associated coefficients, construct the linear combination
|
|
342
|
+
* the supplied terms. The coefficients and observables are expected to have the same length.
|
|
343
|
+
*
|
|
344
|
+
*
|
|
345
|
+
* See `Observable` for additional details.
|
|
346
|
+
*
|
|
347
|
+
* @param coeffs The list of coefficients.
|
|
348
|
+
* @param obs The list of observables IDs.
|
|
349
|
+
*
|
|
350
|
+
* @return `ObsIdType` ID of the constructed observable.
|
|
351
|
+
*/
|
|
352
|
+
virtual auto HamiltonianObservable(const std::vector<double> &coeffs,
|
|
353
|
+
const std::vector<ObsIdType> &obs) -> ObsIdType
|
|
354
|
+
{
|
|
355
|
+
RT_FAIL("HamiltonianObservable is unsupported by device");
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// ----------------------------------------
|
|
359
|
+
// MEASUREMENT PROCESSES
|
|
360
|
+
// ----------------------------------------
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* @brief (Optional) Compute raw samples on all qubits.
|
|
364
|
+
*
|
|
365
|
+
* Perform measurement sampling in the computational basis on all qubits. The number of samples
|
|
366
|
+
* to take is the current active shot count in the device (see also `SetDeviceShots`). When
|
|
367
|
+
* possible, the result should be produced without affecting the internal quantum state.
|
|
368
|
+
*
|
|
369
|
+
* The samples are taken individually on each qubit and stored in a 2D array with the shots
|
|
370
|
+
* being the outer dimension and the qubits being the inner dimension. For example, a 4-shot
|
|
371
|
+
* 2-qubit sample call might produce the following result:
|
|
372
|
+
* [[0, 1],
|
|
373
|
+
* [0, 0],
|
|
374
|
+
* [1, 1],
|
|
375
|
+
* [0, 0]]
|
|
376
|
+
* The samples are stored as double-precision floating-point numbers; for computational basis
|
|
377
|
+
* measurements this might be overkill, but allows for expanding this operation in the future
|
|
378
|
+
* to sample arbitrary observables and produce eigenvalues as sample results.
|
|
379
|
+
*
|
|
380
|
+
* The result of this operation must be written into the `samples` argument buffer.
|
|
381
|
+
*
|
|
382
|
+
* @param samples The pre-allocated buffer for the measurement samples.
|
|
383
|
+
*/
|
|
384
|
+
virtual void Sample(DataView<double, 2> &samples)
|
|
385
|
+
{
|
|
386
|
+
RT_FAIL("Sample is unsupported by device");
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* @brief (Optional) Compute raw samples for a quantum subsystem.
|
|
391
|
+
*
|
|
392
|
+
* Like `Sample`, but for a subset of currently allocated qubits.
|
|
393
|
+
*
|
|
394
|
+
* @param samples The pre-allocated buffer for the measurement samples.
|
|
395
|
+
* @param wires Qubits to compute samples for.
|
|
396
|
+
*/
|
|
397
|
+
virtual void PartialSample(DataView<double, 2> &samples, const std::vector<QubitIdType> &wires)
|
|
398
|
+
{
|
|
399
|
+
RT_FAIL("PartialSample is unsupported by device");
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* @brief (Optional) Compute the sample counts on all qubits.
|
|
404
|
+
*
|
|
405
|
+
* Perform measurement sampling in the computational basis and sum up the occurrence of each
|
|
406
|
+
* outcome. All currently allocated qubits should be sampled. The number of samples to take
|
|
407
|
+
* is the current active shot count in the device (see also `SetDeviceShots`). When possible,
|
|
408
|
+
* the result should be produced without affecting the internal quantum state.
|
|
409
|
+
*
|
|
410
|
+
* The potential measurement outcomes are returned as `eigvals`; currently the only
|
|
411
|
+
* supported mode uses computational basis states whose bitstring representation "01010110..."
|
|
412
|
+
* is first taken as an integer value, and then converted to a double-precision floating-point
|
|
413
|
+
* number. This effectively limits the number of qubits supported by this operation to 53, after
|
|
414
|
+
* which the basis states cannot be accurately represented by the floating-point format. In the
|
|
415
|
+
* future, this operation may be expanded to support eigenvalues of arbitrary observables as
|
|
416
|
+
* possible measurement outcomes, hence the floating-point datatype.
|
|
417
|
+
*
|
|
418
|
+
* The number of times of each measurement outcome is sampled is stored as a 64-bit integer in
|
|
419
|
+
* the same location in the `counts` array as the corresponding basis state in the `eigvals`
|
|
420
|
+
* array. The entries need not be in any particular order, since the measured states are
|
|
421
|
+
* returned alongside the counts.
|
|
422
|
+
*
|
|
423
|
+
* In effect, the result of this operation is a dictionary from measurement outcomes to counts,
|
|
424
|
+
* stored as a pair of dense 1D arrays, one for the keys and one for the values. It's important
|
|
425
|
+
* that all 2^n elements of the `eigvals` and `counts` arrays are written to, where n is the
|
|
426
|
+
* number of qubits, else the result will contain uninitialized data.
|
|
427
|
+
*
|
|
428
|
+
* The results of this operation must be written into the `eigvals` and `counts` argument
|
|
429
|
+
* buffers.
|
|
430
|
+
*
|
|
431
|
+
* @param eigvals The pre-allocated buffer for all measured states.
|
|
432
|
+
* @param counts The pre-allocated buffer for all measured counts.
|
|
433
|
+
*/
|
|
434
|
+
virtual void Counts(DataView<double, 1> &eigvals, DataView<int64_t, 1> &counts)
|
|
435
|
+
{
|
|
436
|
+
RT_FAIL("Counts is unsupported by device");
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* @brief (Optional) Compute the sample counts for a quantum subsystem.
|
|
441
|
+
*
|
|
442
|
+
* Like `Counts`, but for a subset of currently allocated qubits.
|
|
443
|
+
*
|
|
444
|
+
* @param eigvals The pre-allocated buffer for all measured states.
|
|
445
|
+
* @param counts The pre-allocated buffer for all measured counts.
|
|
446
|
+
* @param wires Qubits to compute sample counts for.
|
|
447
|
+
*/
|
|
448
|
+
virtual void PartialCounts(DataView<double, 1> &eigvals, DataView<int64_t, 1> &counts,
|
|
449
|
+
const std::vector<QubitIdType> &wires)
|
|
450
|
+
{
|
|
451
|
+
RT_FAIL("PartialCounts is unsupported by device");
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* @brief (Optional) Compute measurement probabilities on all qubits.
|
|
456
|
+
*
|
|
457
|
+
* The output of this operation is the probability distribution across computational basis
|
|
458
|
+
* states for all currently allocated qubits. When possible, the result should be produced
|
|
459
|
+
* without affecting the internal quantum state.
|
|
460
|
+
*
|
|
461
|
+
* The result of this operation must be written into the `probs` argument buffer.
|
|
462
|
+
*
|
|
463
|
+
* @param probs The pre-allocated buffer for the probabilities.
|
|
464
|
+
*/
|
|
465
|
+
virtual void Probs(DataView<double, 1> &probs) { RT_FAIL("Probs is unsupported by device"); }
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* @brief (Optional) Compute measurement probabilities for a quantum subsystem.
|
|
469
|
+
*
|
|
470
|
+
* Like `Probs`, but for a subset of currently allocated qubits.
|
|
471
|
+
*
|
|
472
|
+
* @param probs The pre-allocated buffer for the probabilities.
|
|
473
|
+
* @param wires Qubits to compute probabilities for.
|
|
474
|
+
*/
|
|
475
|
+
virtual void PartialProbs(DataView<double, 1> &probs, const std::vector<QubitIdType> &wires)
|
|
476
|
+
{
|
|
477
|
+
RT_FAIL("PartialProbs is unsupported by device");
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* @brief (Optional) Compute the expected value of an observable.
|
|
481
|
+
*
|
|
482
|
+
* The output of this operation is expectation value ⟨O⟩ of an observable O with respect to the
|
|
483
|
+
* current quantum state. When possible, the result should be produced without affecting the
|
|
484
|
+
* internal quantum state.
|
|
485
|
+
*
|
|
486
|
+
* See also `Observable` for observable semantics.
|
|
487
|
+
*
|
|
488
|
+
* @param obsKey The ID of the constructed observable.
|
|
489
|
+
*
|
|
490
|
+
* @return `double` The expectation value of the observable.
|
|
491
|
+
*/
|
|
492
|
+
virtual auto Expval(ObsIdType obsKey) -> double { RT_FAIL("Expval is unsupported by device"); }
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* @brief (Optional) Compute the variance of an observable.
|
|
496
|
+
*
|
|
497
|
+
* The output of this operation is variance ⟨O²⟩−⟨O⟩² of an observable O with respect to the
|
|
498
|
+
* current quantum state. When possible, the result should be produced without affecting the
|
|
499
|
+
* internal quantum state.
|
|
500
|
+
*
|
|
501
|
+
* See also `Observable` for observable semantics.
|
|
502
|
+
*
|
|
503
|
+
* @param obsKey The ID of the constructed observable.
|
|
504
|
+
*
|
|
505
|
+
* @return `double` The variance of the observable.
|
|
506
|
+
*/
|
|
507
|
+
virtual auto Var(ObsIdType obsKey) -> double { RT_FAIL("Var is unsupported by device"); }
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* @brief (Optional) Get the full quantum state of all qubits.
|
|
511
|
+
*
|
|
512
|
+
* Typically for devices with statevector simulation capabilities.
|
|
513
|
+
*
|
|
514
|
+
* The output of this operation is the quantum statevector in the computational basis
|
|
515
|
+
* on all currently allocated qubits. When possible, the result should be produced without
|
|
516
|
+
* affecting the internal quantum state.
|
|
517
|
+
*
|
|
518
|
+
* The result of this operation must be written into the `state` argument buffer.
|
|
519
|
+
*
|
|
520
|
+
* @param state Pre-allocated buffer for the quantum state.
|
|
521
|
+
*/
|
|
522
|
+
virtual void State(DataView<std::complex<double>, 1> &state)
|
|
523
|
+
{
|
|
524
|
+
RT_FAIL("State is unsupported by device");
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// ----------------------------------------
|
|
528
|
+
// QUANTUM DERIVATIVES
|
|
529
|
+
// ----------------------------------------
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* @brief (Optional) Compute the gradient/Jacobian of a quantum circuit.
|
|
533
|
+
*
|
|
534
|
+
* For devices with internal differentiation capabilities (e.g. simulator with reverse-mode AD).
|
|
535
|
+
*
|
|
536
|
+
* Performing differentiation inside a device can be advantageous for performance compared to
|
|
537
|
+
* device-agnostic methods like the Parameter-Shift technique. If supported, Catalyst will
|
|
538
|
+
* communicate the start and end of the "forward pass" via the `StartTapeRecording` and
|
|
539
|
+
* `StopTapeRecording` functions. The `Gradient` call then requests the "reverse pass" with
|
|
540
|
+
* respect to the recorded circuit and observables. A sample implementation of circuit
|
|
541
|
+
* recording is available in `CacheManager.hpp`.
|
|
542
|
+
*
|
|
543
|
+
* The output is an array of gradients (i.e. the Jacobian), one for each expectation value /
|
|
544
|
+
* observable in the recorded program. The length of the gradient is determined by the number
|
|
545
|
+
* of gate parameters in the recorded circuit, or by the optional `trainParams` argument. If
|
|
546
|
+
* provided, `trainParams` can customize which parameter values participate in differentiation.
|
|
547
|
+
*
|
|
548
|
+
* The result of this operation must be written into the `gradients` argument buffer.
|
|
549
|
+
*
|
|
550
|
+
* @param gradients The vector of pre-allocated `DataView<double, 1>*`
|
|
551
|
+
* to store the flattened Jacobian (one gradient per cached observable).
|
|
552
|
+
* @param trainParams The vector of trainable parameters; if empty, all parameters
|
|
553
|
+
* would be assumed trainable.
|
|
554
|
+
*/
|
|
555
|
+
virtual void Gradient(std::vector<DataView<double, 1>> &gradients,
|
|
556
|
+
const std::vector<size_t> &trainParams)
|
|
557
|
+
{
|
|
558
|
+
RT_FAIL("Differentiation is unsupported by device");
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* @brief (Optional) Start recording a quantum tape if provided.
|
|
563
|
+
*
|
|
564
|
+
* See `Gradient` for additional information.
|
|
565
|
+
*/
|
|
566
|
+
virtual void StartTapeRecording() { RT_FAIL("Differentiation is unsupported by device"); }
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* @brief (Optional) Stop recording a quantum tape if provided.
|
|
570
|
+
*
|
|
571
|
+
* See `Gradient` for additional information.
|
|
572
|
+
*/
|
|
573
|
+
virtual void StopTapeRecording() { RT_FAIL("Differentiation is unsupported by device"); }
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
} // namespace Catalyst::Runtime
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Copyright 2022-2023 Xanadu Quantum Technologies Inc.
|
|
2
|
+
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
#pragma once
|
|
16
|
+
#ifndef RUNTIMECAPI_H
|
|
17
|
+
#define RUNTIMECAPI_H
|
|
18
|
+
|
|
19
|
+
#include "Types.h"
|
|
20
|
+
|
|
21
|
+
#ifdef __cplusplus
|
|
22
|
+
extern "C" {
|
|
23
|
+
#endif
|
|
24
|
+
|
|
25
|
+
// Quantum Runtime Instructions
|
|
26
|
+
void __catalyst__rt__fail_cstr(const char *);
|
|
27
|
+
void __catalyst__rt__initialize(uint32_t *);
|
|
28
|
+
void __catalyst__rt__device_init(int8_t *, int8_t *, int8_t *, int64_t, bool);
|
|
29
|
+
void __catalyst__rt__device_release();
|
|
30
|
+
void __catalyst__rt__finalize();
|
|
31
|
+
void __catalyst__rt__toggle_recorder(bool);
|
|
32
|
+
void __catalyst__rt__print_state();
|
|
33
|
+
void __catalyst__rt__print_tensor(OpaqueMemRefT *, bool);
|
|
34
|
+
void __catalyst__rt__print_string(char *);
|
|
35
|
+
void __catalyst__rt__assert_bool(bool, char *);
|
|
36
|
+
int64_t __catalyst__rt__array_get_size_1d(QirArray *);
|
|
37
|
+
int8_t *__catalyst__rt__array_get_element_ptr_1d(QirArray *, int64_t);
|
|
38
|
+
|
|
39
|
+
QUBIT *__catalyst__rt__qubit_allocate();
|
|
40
|
+
QirArray *__catalyst__rt__qubit_allocate_array(int64_t);
|
|
41
|
+
void __catalyst__rt__qubit_release(QUBIT *);
|
|
42
|
+
void __catalyst__rt__qubit_release_array(QirArray *);
|
|
43
|
+
|
|
44
|
+
int64_t __catalyst__rt__num_qubits();
|
|
45
|
+
|
|
46
|
+
bool __catalyst__rt__result_equal(RESULT *, RESULT *);
|
|
47
|
+
RESULT *__catalyst__rt__result_get_one();
|
|
48
|
+
RESULT *__catalyst__rt__result_get_zero();
|
|
49
|
+
|
|
50
|
+
// Quantum Gate Set Instructions
|
|
51
|
+
void __catalyst__qis__SetState(MemRefT_CplxT_double_1d *, uint64_t, ...);
|
|
52
|
+
void __catalyst__qis__SetBasisState(MemRefT_int8_1d *, uint64_t, ...);
|
|
53
|
+
void __catalyst__qis__Identity(QUBIT *, const Modifiers *);
|
|
54
|
+
void __catalyst__qis__PauliX(QUBIT *, const Modifiers *);
|
|
55
|
+
void __catalyst__qis__PauliY(QUBIT *, const Modifiers *);
|
|
56
|
+
void __catalyst__qis__PauliZ(QUBIT *, const Modifiers *);
|
|
57
|
+
void __catalyst__qis__Hadamard(QUBIT *, const Modifiers *);
|
|
58
|
+
void __catalyst__qis__S(QUBIT *, const Modifiers *);
|
|
59
|
+
void __catalyst__qis__T(QUBIT *, const Modifiers *);
|
|
60
|
+
void __catalyst__qis__PhaseShift(double, QUBIT *, const Modifiers *);
|
|
61
|
+
void __catalyst__qis__RX(double, QUBIT *, const Modifiers *);
|
|
62
|
+
void __catalyst__qis__RY(double, QUBIT *, const Modifiers *);
|
|
63
|
+
void __catalyst__qis__RZ(double, QUBIT *, const Modifiers *);
|
|
64
|
+
void __catalyst__qis__Rot(double, double, double, QUBIT *, const Modifiers *);
|
|
65
|
+
void __catalyst__qis__CNOT(QUBIT *, QUBIT *, const Modifiers *);
|
|
66
|
+
void __catalyst__qis__CY(QUBIT *, QUBIT *, const Modifiers *);
|
|
67
|
+
void __catalyst__qis__CZ(QUBIT *, QUBIT *, const Modifiers *);
|
|
68
|
+
void __catalyst__qis__SWAP(QUBIT *, QUBIT *, const Modifiers *);
|
|
69
|
+
void __catalyst__qis__IsingXX(double, QUBIT *, QUBIT *, const Modifiers *);
|
|
70
|
+
void __catalyst__qis__IsingYY(double, QUBIT *, QUBIT *, const Modifiers *);
|
|
71
|
+
void __catalyst__qis__IsingXY(double, QUBIT *, QUBIT *, const Modifiers *);
|
|
72
|
+
void __catalyst__qis__IsingZZ(double, QUBIT *, QUBIT *, const Modifiers *);
|
|
73
|
+
void __catalyst__qis__ControlledPhaseShift(double, QUBIT *, QUBIT *, const Modifiers *);
|
|
74
|
+
void __catalyst__qis__CRX(double, QUBIT *, QUBIT *, const Modifiers *);
|
|
75
|
+
void __catalyst__qis__CRY(double, QUBIT *, QUBIT *, const Modifiers *);
|
|
76
|
+
void __catalyst__qis__CRZ(double, QUBIT *, QUBIT *, const Modifiers *);
|
|
77
|
+
void __catalyst__qis__MS(double, QUBIT *, QUBIT *, const Modifiers *);
|
|
78
|
+
void __catalyst__qis__CRot(double, double, double, QUBIT *, QUBIT *, const Modifiers *);
|
|
79
|
+
void __catalyst__qis__CSWAP(QUBIT *, QUBIT *, QUBIT *, const Modifiers *);
|
|
80
|
+
void __catalyst__qis__Toffoli(QUBIT *, QUBIT *, QUBIT *, const Modifiers *);
|
|
81
|
+
void __catalyst__qis__MultiRZ(double, const Modifiers *, int64_t, /*qubits*/...);
|
|
82
|
+
void __catalyst__qis__GlobalPhase(double, const Modifiers *);
|
|
83
|
+
void __catalyst__qis__ISWAP(QUBIT *, QUBIT *, const Modifiers *);
|
|
84
|
+
void __catalyst__qis__PSWAP(double, QUBIT *, QUBIT *, const Modifiers *);
|
|
85
|
+
|
|
86
|
+
// Struct pointer arguments for these instructions represent real arguments,
|
|
87
|
+
// as passing structs by value is too unreliable / compiler dependant.
|
|
88
|
+
void __catalyst__qis__QubitUnitary(MemRefT_CplxT_double_2d *, const Modifiers *, int64_t,
|
|
89
|
+
/*qubits*/...);
|
|
90
|
+
|
|
91
|
+
ObsIdType __catalyst__qis__NamedObs(int64_t, QUBIT *);
|
|
92
|
+
ObsIdType __catalyst__qis__HermitianObs(MemRefT_CplxT_double_2d *, int64_t, /*qubits*/...);
|
|
93
|
+
ObsIdType __catalyst__qis__TensorObs(int64_t, /*obsKeys*/...);
|
|
94
|
+
ObsIdType __catalyst__qis__HamiltonianObs(MemRefT_double_1d *, int64_t, /*obsKeys*/...);
|
|
95
|
+
|
|
96
|
+
// Struct pointers arguments here represent return values.
|
|
97
|
+
RESULT *__catalyst__qis__Measure(QUBIT *, int32_t);
|
|
98
|
+
double __catalyst__qis__Expval(ObsIdType);
|
|
99
|
+
double __catalyst__qis__Variance(ObsIdType);
|
|
100
|
+
void __catalyst__qis__Probs(MemRefT_double_1d *, int64_t, /*qubits*/...);
|
|
101
|
+
void __catalyst__qis__Sample(MemRefT_double_2d *, int64_t, /*qubits*/...);
|
|
102
|
+
void __catalyst__qis__Counts(PairT_MemRefT_double_int64_1d *, int64_t, /*qubits*/...);
|
|
103
|
+
void __catalyst__qis__State(MemRefT_CplxT_double_1d *, int64_t, /*qubits*/...);
|
|
104
|
+
void __catalyst__qis__Gradient(int64_t, /*results*/...);
|
|
105
|
+
void __catalyst__qis__Gradient_params(MemRefT_int64_1d *, int64_t, /*results*/...);
|
|
106
|
+
|
|
107
|
+
// MBQC operations
|
|
108
|
+
RESULT *__catalyst__mbqc__measure_in_basis(QUBIT *, uint32_t, double, int32_t);
|
|
109
|
+
|
|
110
|
+
// Async runtime error
|
|
111
|
+
void __catalyst__host__rt__unrecoverable_error();
|
|
112
|
+
|
|
113
|
+
#ifdef __cplusplus
|
|
114
|
+
} // extern "C"
|
|
115
|
+
#endif
|
|
116
|
+
|
|
117
|
+
#endif
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// Copyright 2022-2023 Xanadu Quantum Technologies Inc.
|
|
2
|
+
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
#pragma once
|
|
16
|
+
#ifndef TYPES_H
|
|
17
|
+
#define TYPES_H
|
|
18
|
+
|
|
19
|
+
#include <cmath>
|
|
20
|
+
#include <cstdint>
|
|
21
|
+
#include <limits>
|
|
22
|
+
|
|
23
|
+
#ifdef __cplusplus
|
|
24
|
+
extern "C" {
|
|
25
|
+
#endif
|
|
26
|
+
|
|
27
|
+
// Qubit, Result and Observable types
|
|
28
|
+
struct QUBIT;
|
|
29
|
+
using QubitIdType = intptr_t;
|
|
30
|
+
|
|
31
|
+
using RESULT = bool;
|
|
32
|
+
using Result = RESULT *;
|
|
33
|
+
using QirArray = void *;
|
|
34
|
+
|
|
35
|
+
using ObsIdType = intptr_t;
|
|
36
|
+
|
|
37
|
+
enum ObsId : int8_t {
|
|
38
|
+
Identity = 0,
|
|
39
|
+
PauliX,
|
|
40
|
+
PauliY,
|
|
41
|
+
PauliZ,
|
|
42
|
+
Hadamard,
|
|
43
|
+
Hermitian,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
enum ObsType : int8_t {
|
|
47
|
+
Basic = 0,
|
|
48
|
+
TensorProd,
|
|
49
|
+
Hamiltonian,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// complex<float> type
|
|
53
|
+
struct CplxT_float {
|
|
54
|
+
float real;
|
|
55
|
+
float imag;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// complex<double> type
|
|
59
|
+
struct CplxT_double {
|
|
60
|
+
double real;
|
|
61
|
+
double imag;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
enum NumericType : int8_t {
|
|
65
|
+
idx = 0,
|
|
66
|
+
i1,
|
|
67
|
+
i8,
|
|
68
|
+
i16,
|
|
69
|
+
i32,
|
|
70
|
+
i64,
|
|
71
|
+
f32,
|
|
72
|
+
f64,
|
|
73
|
+
c64,
|
|
74
|
+
c128,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// MemRefT<datatype, dimension=rank> type
|
|
78
|
+
struct OpaqueMemRefT {
|
|
79
|
+
int64_t rank;
|
|
80
|
+
void *descriptor;
|
|
81
|
+
NumericType datatype;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// MemRefT<complex<double>, dimension=1> type
|
|
85
|
+
struct MemRefT_CplxT_double_1d {
|
|
86
|
+
CplxT_double *data_allocated;
|
|
87
|
+
CplxT_double *data_aligned;
|
|
88
|
+
size_t offset;
|
|
89
|
+
size_t sizes[1];
|
|
90
|
+
size_t strides[1];
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// MemRefT<complex<double>, dimension=2> type
|
|
94
|
+
struct MemRefT_CplxT_double_2d {
|
|
95
|
+
CplxT_double *data_allocated;
|
|
96
|
+
CplxT_double *data_aligned;
|
|
97
|
+
size_t offset;
|
|
98
|
+
size_t sizes[2];
|
|
99
|
+
size_t strides[2];
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// MemRefT<double, dimension=1> type
|
|
103
|
+
struct MemRefT_double_1d {
|
|
104
|
+
double *data_allocated;
|
|
105
|
+
double *data_aligned;
|
|
106
|
+
size_t offset;
|
|
107
|
+
size_t sizes[1];
|
|
108
|
+
size_t strides[1];
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// MemRefT<double, dimension=2> type
|
|
112
|
+
struct MemRefT_double_2d {
|
|
113
|
+
double *data_allocated;
|
|
114
|
+
double *data_aligned;
|
|
115
|
+
size_t offset;
|
|
116
|
+
size_t sizes[2];
|
|
117
|
+
size_t strides[2];
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// MemRefT<int64_t, dimension=1> type
|
|
121
|
+
struct MemRefT_int64_1d {
|
|
122
|
+
int64_t *data_allocated;
|
|
123
|
+
int64_t *data_aligned;
|
|
124
|
+
size_t offset;
|
|
125
|
+
size_t sizes[1];
|
|
126
|
+
size_t strides[1];
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// MemRefT<int64_t, dimension=1> type
|
|
130
|
+
struct MemRefT_int8_1d {
|
|
131
|
+
int8_t *data_allocated;
|
|
132
|
+
int8_t *data_aligned;
|
|
133
|
+
size_t offset;
|
|
134
|
+
size_t sizes[1];
|
|
135
|
+
size_t strides[1];
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// PairT<MemRefT<double, dimension=1>, MemRefT<int64, dimension=2>> type
|
|
139
|
+
struct PairT_MemRefT_double_int64_1d {
|
|
140
|
+
struct MemRefT_double_1d first;
|
|
141
|
+
struct MemRefT_int64_1d second;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// Quantum operation modifiers
|
|
145
|
+
struct Modifiers {
|
|
146
|
+
bool adjoint;
|
|
147
|
+
size_t num_controlled;
|
|
148
|
+
QUBIT *controlled_wires;
|
|
149
|
+
bool *controlled_values;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
using CplxT_double = struct CplxT_double;
|
|
153
|
+
using MemRefT_CplxT_double_1d = struct MemRefT_CplxT_double_1d;
|
|
154
|
+
using MemRefT_CplxT_double_2d = struct MemRefT_CplxT_double_2d;
|
|
155
|
+
using MemRefT_double_1d = struct MemRefT_double_1d;
|
|
156
|
+
using MemRefT_double_2d = struct MemRefT_double_2d;
|
|
157
|
+
using MemRefT_int64_1d = struct MemRefT_int64_1d;
|
|
158
|
+
using PairT_MemRefT_double_int64_1d = struct PairT_MemRefT_double_int64_1d;
|
|
159
|
+
using Modifiers = struct Modifiers;
|
|
160
|
+
|
|
161
|
+
#ifdef __cplusplus
|
|
162
|
+
} // extern "C"
|
|
163
|
+
#endif
|
|
164
|
+
|
|
165
|
+
#endif
|
|
@@ -6,6 +6,12 @@ Makefile
|
|
|
6
6
|
README.rst
|
|
7
7
|
requirements.txt
|
|
8
8
|
setup.py
|
|
9
|
+
catalyst/runtime/include/DataView.hpp
|
|
10
|
+
catalyst/runtime/include/Exception.hpp
|
|
11
|
+
catalyst/runtime/include/OQDRuntimeCAPI.h
|
|
12
|
+
catalyst/runtime/include/QuantumDevice.hpp
|
|
13
|
+
catalyst/runtime/include/RuntimeCAPI.h
|
|
14
|
+
catalyst/runtime/include/Types.h
|
|
9
15
|
pennylane_qrack/QrackAceDeviceConfig.toml
|
|
10
16
|
pennylane_qrack/QrackDeviceConfig.toml
|
|
11
17
|
pennylane_qrack/__init__.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pennylane_qrack-0.21.1 → pennylane_qrack-0.21.2}/pennylane_qrack.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|