Skip to content

Commit 7ab419c

Browse files
authored
♻️🩹 fix and improve MQT Core linking (#446)
## Description A follow-up to #444 that fixes a particular linking issue and furthermore improves the overall library link time dependencies by removing the template code from the `StateGenerator` and moving the code to the `.cpp` file. ## Checklist: <!--- This checklist serves as a reminder of a couple of things that ensure your pull request will be merged swiftly. --> - [x] The pull request only contains commits that are related to it. - [x] I have added appropriate tests and documentation. - [x] I have made sure that all CI jobs on GitHub pass. - [x] The pull request introduces no new warnings and follows the project's style guidelines. Signed-off-by: burgholzer <burgholzer@me.com>
1 parent c634603 commit 7ab419c

File tree

5 files changed

+195
-179
lines changed

5 files changed

+195
-179
lines changed

include/checker/dd/simulation/StateGenerator.hpp

+12-141
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,13 @@
66
#pragma once
77

88
#include "StateType.hpp"
9-
#include "algorithms/RandomCliffordCircuit.hpp"
10-
#include "dd/DDDefinitions.hpp"
11-
#include "dd/DDpackageConfig.hpp"
12-
#include "dd/Package.hpp"
9+
#include "checker/dd/DDPackageConfigs.hpp"
1310
#include "dd/Package_fwd.hpp"
14-
#include "dd/Simulation.hpp" // IWYU pragma: keep
1511

16-
#include <array>
1712
#include <cassert>
18-
#include <cmath>
1913
#include <cstddef>
20-
#include <cstdint>
21-
#include <limits>
2214
#include <random>
23-
#include <tuple>
2415
#include <unordered_set>
25-
#include <vector>
2616

2717
namespace ec {
2818
class StateGenerator {
@@ -32,143 +22,24 @@ class StateGenerator {
3222
}
3323
StateGenerator() : StateGenerator(0U) {}
3424

35-
template <class Config = dd::DDPackageConfig>
3625
qc::VectorDD
37-
generateRandomState(dd::Package<Config>& dd, const std::size_t totalQubits,
38-
const std::size_t ancillaryQubits = 0U,
39-
const StateType type = StateType::ComputationalBasis) {
40-
switch (type) {
41-
case ec::StateType::Random1QBasis:
42-
return generateRandom1QBasisState(dd, totalQubits, ancillaryQubits);
43-
case ec::StateType::Stabilizer:
44-
return generateRandomStabilizerState(dd, totalQubits, ancillaryQubits);
45-
default:
46-
return generateRandomComputationalBasisState(dd, totalQubits,
47-
ancillaryQubits);
48-
}
49-
}
26+
generateRandomState(dd::Package<SimulationDDPackageConfig>& dd,
27+
std::size_t totalQubits, std::size_t ancillaryQubits = 0U,
28+
StateType type = StateType::ComputationalBasis);
5029

51-
template <class Config = dd::DDPackageConfig>
5230
qc::VectorDD generateRandomComputationalBasisState(
53-
dd::Package<Config>& dd, const std::size_t totalQubits,
54-
const std::size_t ancillaryQubits = 0U) {
55-
// determine how many qubits truly are random
56-
const std::size_t randomQubits = totalQubits - ancillaryQubits;
57-
std::vector<bool> stimulusBits(totalQubits, false);
58-
59-
// check if there still is a unique computational basis state
60-
if (constexpr auto bitwidth = std::numeric_limits<std::uint64_t>::digits;
61-
randomQubits <= (bitwidth - 1U)) {
62-
const auto maxStates = static_cast<std::uint64_t>(1U) << randomQubits;
63-
assert(generatedComputationalBasisStates.size() != maxStates);
64-
// generate a unique computational basis state
65-
std::uniform_int_distribution<std::uint64_t> distribution(0U,
66-
maxStates - 1U);
67-
auto [randomState, success] =
68-
generatedComputationalBasisStates.insert(distribution(mt));
69-
while (!success) {
70-
std::tie(randomState, success) =
71-
generatedComputationalBasisStates.insert(distribution(mt));
72-
}
31+
dd::Package<SimulationDDPackageConfig>& dd, std::size_t totalQubits,
32+
std::size_t ancillaryQubits = 0U);
7333

74-
// generate the bitvector corresponding to the random state
75-
for (std::size_t i = 0U; i < randomQubits; ++i) {
76-
if ((*randomState & (static_cast<std::uint64_t>(1U) << i)) != 0U) {
77-
stimulusBits[i] = true;
78-
}
79-
}
80-
} else {
81-
// check how many numbers are needed for each random state
82-
const auto nr = static_cast<std::size_t>(
83-
std::ceil(static_cast<double>(randomQubits) / bitwidth));
84-
// generate enough random numbers
85-
std::vector<std::mt19937_64::result_type> randomNumbers(nr, 0U);
86-
for (auto i = 0U; i < nr; ++i) {
87-
randomNumbers[i] = mt();
88-
}
89-
// generate the corresponding bitvector
90-
for (std::size_t i = 0U; i < randomQubits; ++i) {
91-
if ((randomNumbers[i / bitwidth] &
92-
(static_cast<std::uint_least64_t>(1U) << (i % bitwidth))) != 0U) {
93-
stimulusBits[i] = true;
94-
}
95-
}
96-
}
97-
98-
// return the appropriate decision diagram
99-
return dd.makeBasisState(totalQubits, stimulusBits);
100-
}
101-
102-
template <class Config = dd::DDPackageConfig>
10334
qc::VectorDD
104-
generateRandom1QBasisState(dd::Package<Config>& dd,
105-
const std::size_t totalQubits,
106-
const std::size_t ancillaryQubits = 0U) {
107-
// determine how many qubits truly are random
108-
const std::size_t randomQubits = totalQubits - ancillaryQubits;
109-
110-
// choose a random basis state for each qubit
111-
auto randomBasisState =
112-
std::vector<dd::BasisStates>(totalQubits, dd::BasisStates::zero);
113-
for (std::size_t i = 0U; i < randomQubits; ++i) {
114-
switch (random1QBasisDistribution(mt)) {
115-
case static_cast<std::size_t>(dd::BasisStates::zero):
116-
randomBasisState[i] = dd::BasisStates::zero;
117-
break;
118-
case static_cast<std::size_t>(dd::BasisStates::one):
119-
randomBasisState[i] = dd::BasisStates::one;
120-
break;
121-
case static_cast<std::size_t>(dd::BasisStates::plus):
122-
randomBasisState[i] = dd::BasisStates::plus;
123-
break;
124-
case static_cast<std::size_t>(dd::BasisStates::minus):
125-
randomBasisState[i] = dd::BasisStates::minus;
126-
break;
127-
case static_cast<std::size_t>(dd::BasisStates::right):
128-
randomBasisState[i] = dd::BasisStates::right;
129-
break;
130-
case static_cast<std::size_t>(dd::BasisStates::left):
131-
randomBasisState[i] = dd::BasisStates::left;
132-
break;
133-
default:
134-
break;
135-
}
136-
}
137-
138-
// return the appropriate decision diagram
139-
return dd.makeBasisState(totalQubits, randomBasisState);
140-
}
35+
generateRandom1QBasisState(dd::Package<SimulationDDPackageConfig>& dd,
36+
std::size_t totalQubits,
37+
std::size_t ancillaryQubits = 0U);
14138

142-
template <class Config = dd::DDPackageConfig>
14339
qc::VectorDD
144-
generateRandomStabilizerState(dd::Package<Config>& dd,
145-
const std::size_t totalQubits,
146-
const std::size_t ancillaryQubits = 0U) {
147-
// determine how many qubits truly are random
148-
const std::size_t randomQubits = totalQubits - ancillaryQubits;
149-
150-
// generate a random Clifford circuit with appropriate depth
151-
auto rcs = qc::RandomCliffordCircuit(
152-
randomQubits,
153-
static_cast<std::size_t>(std::round(std::log2(randomQubits))), mt());
154-
155-
// generate the associated stabilizer state by simulating the Clifford
156-
// circuit
157-
auto stabilizer = simulate(&rcs, dd.makeZeroState(randomQubits), dd);
158-
159-
// decrease the ref count right after so that it stays correct later on
160-
dd.decRef(stabilizer);
161-
162-
// add |0> edges for all the ancillary qubits
163-
auto initial = stabilizer;
164-
for (std::size_t p = randomQubits; p < totalQubits; ++p) {
165-
initial = dd.makeDDNode(static_cast<dd::Qubit>(p),
166-
std::array{initial, qc::VectorDD::zero()});
167-
}
168-
169-
// return the resulting decision diagram
170-
return initial;
171-
}
40+
generateRandomStabilizerState(dd::Package<SimulationDDPackageConfig>& dd,
41+
std::size_t totalQubits,
42+
std::size_t ancillaryQubits = 0U);
17243

17344
void seedGenerator(std::size_t s);
17445

src/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include
2424
target_link_libraries(
2525
${PROJECT_NAME}
2626
PUBLIC MQT::CoreDD MQT::CoreZX
27-
PRIVATE MQT::CoreCircuitOptimizer MQT::ProjectWarnings MQT::ProjectOptions)
27+
PRIVATE MQT::CoreCircuitOptimizer MQT::CoreAlgorithms MQT::ProjectWarnings MQT::ProjectOptions)
2828

2929
# add MQT alias
3030
add_library(MQT::QCEC ALIAS ${PROJECT_NAME})

src/checker/dd/simulation/StateGenerator.cpp

+145
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,158 @@
55

66
#include "checker/dd/simulation/StateGenerator.hpp"
77

8+
#include "algorithms/RandomCliffordCircuit.hpp"
9+
#include "checker/dd/DDPackageConfigs.hpp"
10+
#include "checker/dd/simulation/StateType.hpp"
11+
#include "dd/DDDefinitions.hpp"
12+
#include "dd/Package.hpp"
13+
#include "dd/Simulation.hpp"
14+
815
#include <algorithm>
916
#include <array>
17+
#include <cassert>
18+
#include <cmath>
1019
#include <cstddef>
20+
#include <cstdint>
1121
#include <functional>
22+
#include <limits>
1223
#include <random>
24+
#include <tuple>
25+
#include <vector>
1326

1427
namespace ec {
1528

29+
qc::VectorDD StateGenerator::generateRandomState(
30+
dd::Package<SimulationDDPackageConfig>& dd, const std::size_t totalQubits,
31+
const std::size_t ancillaryQubits, const StateType type) {
32+
switch (type) {
33+
case ec::StateType::Random1QBasis:
34+
return generateRandom1QBasisState(dd, totalQubits, ancillaryQubits);
35+
case ec::StateType::Stabilizer:
36+
return generateRandomStabilizerState(dd, totalQubits, ancillaryQubits);
37+
default:
38+
return generateRandomComputationalBasisState(dd, totalQubits,
39+
ancillaryQubits);
40+
}
41+
}
42+
43+
qc::VectorDD StateGenerator::generateRandomComputationalBasisState(
44+
dd::Package<SimulationDDPackageConfig>& dd, const std::size_t totalQubits,
45+
const std::size_t ancillaryQubits) {
46+
// determine how many qubits truly are random
47+
const std::size_t randomQubits = totalQubits - ancillaryQubits;
48+
std::vector<bool> stimulusBits(totalQubits, false);
49+
50+
// check if there still is a unique computational basis state
51+
if (constexpr auto bitwidth = std::numeric_limits<std::uint64_t>::digits;
52+
randomQubits <= (bitwidth - 1U)) {
53+
const auto maxStates = static_cast<std::uint64_t>(1U) << randomQubits;
54+
assert(generatedComputationalBasisStates.size() != maxStates);
55+
// generate a unique computational basis state
56+
std::uniform_int_distribution<std::uint64_t> distribution(0U,
57+
maxStates - 1U);
58+
auto [randomState, success] =
59+
generatedComputationalBasisStates.insert(distribution(mt));
60+
while (!success) {
61+
std::tie(randomState, success) =
62+
generatedComputationalBasisStates.insert(distribution(mt));
63+
}
64+
65+
// generate the bitvector corresponding to the random state
66+
for (std::size_t i = 0U; i < randomQubits; ++i) {
67+
if ((*randomState & (static_cast<std::uint64_t>(1U) << i)) != 0U) {
68+
stimulusBits[i] = true;
69+
}
70+
}
71+
} else {
72+
// check how many numbers are needed for each random state
73+
const auto nr = static_cast<std::size_t>(
74+
std::ceil(static_cast<double>(randomQubits) / bitwidth));
75+
// generate enough random numbers
76+
std::vector<std::mt19937_64::result_type> randomNumbers(nr, 0U);
77+
for (auto i = 0U; i < nr; ++i) {
78+
randomNumbers[i] = mt();
79+
}
80+
// generate the corresponding bitvector
81+
for (std::size_t i = 0U; i < randomQubits; ++i) {
82+
if ((randomNumbers[i / bitwidth] &
83+
(static_cast<std::uint_least64_t>(1U) << (i % bitwidth))) != 0U) {
84+
stimulusBits[i] = true;
85+
}
86+
}
87+
}
88+
89+
// return the appropriate decision diagram
90+
return dd.makeBasisState(totalQubits, stimulusBits);
91+
}
92+
93+
qc::VectorDD StateGenerator::generateRandom1QBasisState(
94+
dd::Package<SimulationDDPackageConfig>& dd, const std::size_t totalQubits,
95+
const std::size_t ancillaryQubits) {
96+
// determine how many qubits truly are random
97+
const std::size_t randomQubits = totalQubits - ancillaryQubits;
98+
99+
// choose a random basis state for each qubit
100+
auto randomBasisState =
101+
std::vector<dd::BasisStates>(totalQubits, dd::BasisStates::zero);
102+
for (std::size_t i = 0U; i < randomQubits; ++i) {
103+
switch (random1QBasisDistribution(mt)) {
104+
case static_cast<std::size_t>(dd::BasisStates::zero):
105+
randomBasisState[i] = dd::BasisStates::zero;
106+
break;
107+
case static_cast<std::size_t>(dd::BasisStates::one):
108+
randomBasisState[i] = dd::BasisStates::one;
109+
break;
110+
case static_cast<std::size_t>(dd::BasisStates::plus):
111+
randomBasisState[i] = dd::BasisStates::plus;
112+
break;
113+
case static_cast<std::size_t>(dd::BasisStates::minus):
114+
randomBasisState[i] = dd::BasisStates::minus;
115+
break;
116+
case static_cast<std::size_t>(dd::BasisStates::right):
117+
randomBasisState[i] = dd::BasisStates::right;
118+
break;
119+
case static_cast<std::size_t>(dd::BasisStates::left):
120+
randomBasisState[i] = dd::BasisStates::left;
121+
break;
122+
default:
123+
break;
124+
}
125+
}
126+
127+
// return the appropriate decision diagram
128+
return dd.makeBasisState(totalQubits, randomBasisState);
129+
}
130+
131+
qc::VectorDD StateGenerator::generateRandomStabilizerState(
132+
dd::Package<SimulationDDPackageConfig>& dd, const std::size_t totalQubits,
133+
const std::size_t ancillaryQubits) {
134+
// determine how many qubits truly are random
135+
const std::size_t randomQubits = totalQubits - ancillaryQubits;
136+
137+
// generate a random Clifford circuit with appropriate depth
138+
auto rcs = qc::RandomCliffordCircuit(
139+
randomQubits,
140+
static_cast<std::size_t>(std::round(std::log2(randomQubits))), mt());
141+
142+
// generate the associated stabilizer state by simulating the Clifford
143+
// circuit
144+
auto stabilizer = simulate(&rcs, dd.makeZeroState(randomQubits), dd);
145+
146+
// decrease the ref count right after so that it stays correct later on
147+
dd.decRef(stabilizer);
148+
149+
// add |0> edges for all the ancillary qubits
150+
auto initial = stabilizer;
151+
for (std::size_t p = randomQubits; p < totalQubits; ++p) {
152+
initial = dd.makeDDNode(static_cast<dd::Qubit>(p),
153+
std::array{initial, qc::VectorDD::zero()});
154+
}
155+
156+
// return the resulting decision diagram
157+
return initial;
158+
}
159+
16160
void StateGenerator::seedGenerator(const std::size_t s) {
17161
seed = s;
18162
if (seed == 0U) {
@@ -26,4 +170,5 @@ void StateGenerator::seedGenerator(const std::size_t s) {
26170
mt.seed(seed);
27171
}
28172
}
173+
29174
} // namespace ec

src/python/CMakeLists.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ pybind11_add_module(
66
OPT_SIZE
77
# Source code goes here
88
bindings.cpp)
9-
target_link_libraries(pyqcec PRIVATE MQT::QCEC MQT::CoreAlgorithms MQT::CorePython pybind11_json
10-
MQT::ProjectOptions MQT::ProjectWarnings)
9+
target_link_libraries(pyqcec PRIVATE MQT::QCEC MQT::CorePython pybind11_json MQT::ProjectOptions
10+
MQT::ProjectWarnings)
1111

1212
# Install directive for scikit-build-core
1313
install(

0 commit comments

Comments
 (0)