Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️🩹 fix and improve MQT Core linking #446

Merged
merged 1 commit into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 12 additions & 141 deletions include/checker/dd/simulation/StateGenerator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,13 @@
#pragma once

#include "StateType.hpp"
#include "algorithms/RandomCliffordCircuit.hpp"
#include "dd/DDDefinitions.hpp"
#include "dd/DDpackageConfig.hpp"
#include "dd/Package.hpp"
#include "checker/dd/DDPackageConfigs.hpp"
#include "dd/Package_fwd.hpp"
#include "dd/Simulation.hpp" // IWYU pragma: keep

#include <array>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <random>
#include <tuple>
#include <unordered_set>
#include <vector>

namespace ec {
class StateGenerator {
Expand All @@ -32,143 +22,24 @@ class StateGenerator {
}
StateGenerator() : StateGenerator(0U) {}

template <class Config = dd::DDPackageConfig>
qc::VectorDD
generateRandomState(dd::Package<Config>& dd, const std::size_t totalQubits,
const std::size_t ancillaryQubits = 0U,
const StateType type = StateType::ComputationalBasis) {
switch (type) {
case ec::StateType::Random1QBasis:
return generateRandom1QBasisState(dd, totalQubits, ancillaryQubits);
case ec::StateType::Stabilizer:
return generateRandomStabilizerState(dd, totalQubits, ancillaryQubits);
default:
return generateRandomComputationalBasisState(dd, totalQubits,
ancillaryQubits);
}
}
generateRandomState(dd::Package<SimulationDDPackageConfig>& dd,
std::size_t totalQubits, std::size_t ancillaryQubits = 0U,
StateType type = StateType::ComputationalBasis);

template <class Config = dd::DDPackageConfig>
qc::VectorDD generateRandomComputationalBasisState(
dd::Package<Config>& dd, const std::size_t totalQubits,
const std::size_t ancillaryQubits = 0U) {
// determine how many qubits truly are random
const std::size_t randomQubits = totalQubits - ancillaryQubits;
std::vector<bool> stimulusBits(totalQubits, false);

// check if there still is a unique computational basis state
if (constexpr auto bitwidth = std::numeric_limits<std::uint64_t>::digits;
randomQubits <= (bitwidth - 1U)) {
const auto maxStates = static_cast<std::uint64_t>(1U) << randomQubits;
assert(generatedComputationalBasisStates.size() != maxStates);
// generate a unique computational basis state
std::uniform_int_distribution<std::uint64_t> distribution(0U,
maxStates - 1U);
auto [randomState, success] =
generatedComputationalBasisStates.insert(distribution(mt));
while (!success) {
std::tie(randomState, success) =
generatedComputationalBasisStates.insert(distribution(mt));
}
dd::Package<SimulationDDPackageConfig>& dd, std::size_t totalQubits,
std::size_t ancillaryQubits = 0U);

// generate the bitvector corresponding to the random state
for (std::size_t i = 0U; i < randomQubits; ++i) {
if ((*randomState & (static_cast<std::uint64_t>(1U) << i)) != 0U) {
stimulusBits[i] = true;
}
}
} else {
// check how many numbers are needed for each random state
const auto nr = static_cast<std::size_t>(
std::ceil(static_cast<double>(randomQubits) / bitwidth));
// generate enough random numbers
std::vector<std::mt19937_64::result_type> randomNumbers(nr, 0U);
for (auto i = 0U; i < nr; ++i) {
randomNumbers[i] = mt();
}
// generate the corresponding bitvector
for (std::size_t i = 0U; i < randomQubits; ++i) {
if ((randomNumbers[i / bitwidth] &
(static_cast<std::uint_least64_t>(1U) << (i % bitwidth))) != 0U) {
stimulusBits[i] = true;
}
}
}

// return the appropriate decision diagram
return dd.makeBasisState(totalQubits, stimulusBits);
}

template <class Config = dd::DDPackageConfig>
qc::VectorDD
generateRandom1QBasisState(dd::Package<Config>& dd,
const std::size_t totalQubits,
const std::size_t ancillaryQubits = 0U) {
// determine how many qubits truly are random
const std::size_t randomQubits = totalQubits - ancillaryQubits;

// choose a random basis state for each qubit
auto randomBasisState =
std::vector<dd::BasisStates>(totalQubits, dd::BasisStates::zero);
for (std::size_t i = 0U; i < randomQubits; ++i) {
switch (random1QBasisDistribution(mt)) {
case static_cast<std::size_t>(dd::BasisStates::zero):
randomBasisState[i] = dd::BasisStates::zero;
break;
case static_cast<std::size_t>(dd::BasisStates::one):
randomBasisState[i] = dd::BasisStates::one;
break;
case static_cast<std::size_t>(dd::BasisStates::plus):
randomBasisState[i] = dd::BasisStates::plus;
break;
case static_cast<std::size_t>(dd::BasisStates::minus):
randomBasisState[i] = dd::BasisStates::minus;
break;
case static_cast<std::size_t>(dd::BasisStates::right):
randomBasisState[i] = dd::BasisStates::right;
break;
case static_cast<std::size_t>(dd::BasisStates::left):
randomBasisState[i] = dd::BasisStates::left;
break;
default:
break;
}
}

// return the appropriate decision diagram
return dd.makeBasisState(totalQubits, randomBasisState);
}
generateRandom1QBasisState(dd::Package<SimulationDDPackageConfig>& dd,
std::size_t totalQubits,
std::size_t ancillaryQubits = 0U);

template <class Config = dd::DDPackageConfig>
qc::VectorDD
generateRandomStabilizerState(dd::Package<Config>& dd,
const std::size_t totalQubits,
const std::size_t ancillaryQubits = 0U) {
// determine how many qubits truly are random
const std::size_t randomQubits = totalQubits - ancillaryQubits;

// generate a random Clifford circuit with appropriate depth
auto rcs = qc::RandomCliffordCircuit(
randomQubits,
static_cast<std::size_t>(std::round(std::log2(randomQubits))), mt());

// generate the associated stabilizer state by simulating the Clifford
// circuit
auto stabilizer = simulate(&rcs, dd.makeZeroState(randomQubits), dd);

// decrease the ref count right after so that it stays correct later on
dd.decRef(stabilizer);

// add |0> edges for all the ancillary qubits
auto initial = stabilizer;
for (std::size_t p = randomQubits; p < totalQubits; ++p) {
initial = dd.makeDDNode(static_cast<dd::Qubit>(p),
std::array{initial, qc::VectorDD::zero()});
}

// return the resulting decision diagram
return initial;
}
generateRandomStabilizerState(dd::Package<SimulationDDPackageConfig>& dd,
std::size_t totalQubits,
std::size_t ancillaryQubits = 0U);

void seedGenerator(std::size_t s);

Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include
target_link_libraries(
${PROJECT_NAME}
PUBLIC MQT::CoreDD MQT::CoreZX
PRIVATE MQT::CoreCircuitOptimizer MQT::ProjectWarnings MQT::ProjectOptions)
PRIVATE MQT::CoreCircuitOptimizer MQT::CoreAlgorithms MQT::ProjectWarnings MQT::ProjectOptions)

# add MQT alias
add_library(MQT::QCEC ALIAS ${PROJECT_NAME})
Expand Down
145 changes: 145 additions & 0 deletions src/checker/dd/simulation/StateGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,158 @@

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

#include "algorithms/RandomCliffordCircuit.hpp"
#include "checker/dd/DDPackageConfigs.hpp"
#include "checker/dd/simulation/StateType.hpp"
#include "dd/DDDefinitions.hpp"
#include "dd/Package.hpp"
#include "dd/Simulation.hpp"

#include <algorithm>
#include <array>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <limits>
#include <random>
#include <tuple>
#include <vector>

namespace ec {

qc::VectorDD StateGenerator::generateRandomState(
dd::Package<SimulationDDPackageConfig>& dd, const std::size_t totalQubits,
const std::size_t ancillaryQubits, const StateType type) {
switch (type) {
case ec::StateType::Random1QBasis:
return generateRandom1QBasisState(dd, totalQubits, ancillaryQubits);
case ec::StateType::Stabilizer:
return generateRandomStabilizerState(dd, totalQubits, ancillaryQubits);
default:
return generateRandomComputationalBasisState(dd, totalQubits,
ancillaryQubits);
}
}

qc::VectorDD StateGenerator::generateRandomComputationalBasisState(
dd::Package<SimulationDDPackageConfig>& dd, const std::size_t totalQubits,
const std::size_t ancillaryQubits) {
// determine how many qubits truly are random
const std::size_t randomQubits = totalQubits - ancillaryQubits;
std::vector<bool> stimulusBits(totalQubits, false);

// check if there still is a unique computational basis state
if (constexpr auto bitwidth = std::numeric_limits<std::uint64_t>::digits;
randomQubits <= (bitwidth - 1U)) {
const auto maxStates = static_cast<std::uint64_t>(1U) << randomQubits;
assert(generatedComputationalBasisStates.size() != maxStates);
// generate a unique computational basis state
std::uniform_int_distribution<std::uint64_t> distribution(0U,
maxStates - 1U);
auto [randomState, success] =
generatedComputationalBasisStates.insert(distribution(mt));
while (!success) {
std::tie(randomState, success) =
generatedComputationalBasisStates.insert(distribution(mt));
}

// generate the bitvector corresponding to the random state
for (std::size_t i = 0U; i < randomQubits; ++i) {
if ((*randomState & (static_cast<std::uint64_t>(1U) << i)) != 0U) {
stimulusBits[i] = true;
}
}
} else {
// check how many numbers are needed for each random state
const auto nr = static_cast<std::size_t>(
std::ceil(static_cast<double>(randomQubits) / bitwidth));
// generate enough random numbers
std::vector<std::mt19937_64::result_type> randomNumbers(nr, 0U);
for (auto i = 0U; i < nr; ++i) {
randomNumbers[i] = mt();
}
// generate the corresponding bitvector
for (std::size_t i = 0U; i < randomQubits; ++i) {
if ((randomNumbers[i / bitwidth] &
(static_cast<std::uint_least64_t>(1U) << (i % bitwidth))) != 0U) {
stimulusBits[i] = true;
}
}
}

// return the appropriate decision diagram
return dd.makeBasisState(totalQubits, stimulusBits);
}

qc::VectorDD StateGenerator::generateRandom1QBasisState(
dd::Package<SimulationDDPackageConfig>& dd, const std::size_t totalQubits,
const std::size_t ancillaryQubits) {
// determine how many qubits truly are random
const std::size_t randomQubits = totalQubits - ancillaryQubits;

// choose a random basis state for each qubit
auto randomBasisState =
std::vector<dd::BasisStates>(totalQubits, dd::BasisStates::zero);
for (std::size_t i = 0U; i < randomQubits; ++i) {
switch (random1QBasisDistribution(mt)) {
case static_cast<std::size_t>(dd::BasisStates::zero):
randomBasisState[i] = dd::BasisStates::zero;
break;
case static_cast<std::size_t>(dd::BasisStates::one):
randomBasisState[i] = dd::BasisStates::one;
break;
case static_cast<std::size_t>(dd::BasisStates::plus):
randomBasisState[i] = dd::BasisStates::plus;
break;
case static_cast<std::size_t>(dd::BasisStates::minus):
randomBasisState[i] = dd::BasisStates::minus;
break;
case static_cast<std::size_t>(dd::BasisStates::right):
randomBasisState[i] = dd::BasisStates::right;
break;
case static_cast<std::size_t>(dd::BasisStates::left):
randomBasisState[i] = dd::BasisStates::left;
break;
default:
break;
}
}

// return the appropriate decision diagram
return dd.makeBasisState(totalQubits, randomBasisState);
}

qc::VectorDD StateGenerator::generateRandomStabilizerState(
dd::Package<SimulationDDPackageConfig>& dd, const std::size_t totalQubits,
const std::size_t ancillaryQubits) {
// determine how many qubits truly are random
const std::size_t randomQubits = totalQubits - ancillaryQubits;

// generate a random Clifford circuit with appropriate depth
auto rcs = qc::RandomCliffordCircuit(
randomQubits,
static_cast<std::size_t>(std::round(std::log2(randomQubits))), mt());

// generate the associated stabilizer state by simulating the Clifford
// circuit
auto stabilizer = simulate(&rcs, dd.makeZeroState(randomQubits), dd);

// decrease the ref count right after so that it stays correct later on
dd.decRef(stabilizer);

// add |0> edges for all the ancillary qubits
auto initial = stabilizer;
for (std::size_t p = randomQubits; p < totalQubits; ++p) {
initial = dd.makeDDNode(static_cast<dd::Qubit>(p),
std::array{initial, qc::VectorDD::zero()});
}

// return the resulting decision diagram
return initial;
}

void StateGenerator::seedGenerator(const std::size_t s) {
seed = s;
if (seed == 0U) {
Expand All @@ -26,4 +170,5 @@ void StateGenerator::seedGenerator(const std::size_t s) {
mt.seed(seed);
}
}

} // namespace ec
4 changes: 2 additions & 2 deletions src/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ pybind11_add_module(
OPT_SIZE
# Source code goes here
bindings.cpp)
target_link_libraries(pyqcec PRIVATE MQT::QCEC MQT::CoreAlgorithms MQT::CorePython pybind11_json
MQT::ProjectOptions MQT::ProjectWarnings)
target_link_libraries(pyqcec PRIVATE MQT::QCEC MQT::CorePython pybind11_json MQT::ProjectOptions
MQT::ProjectWarnings)

# Install directive for scikit-build-core
install(
Expand Down
Loading
Loading