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

✨ Add State Preparation Algorithm #543

Merged
merged 101 commits into from
Feb 21, 2025
Merged
Changes from 1 commit
Commits
Show all changes
101 commits
Select commit Hold shift + click to select a range
1c891fd
Added State Preparation algorithm in new file, added to Cmake
M-J-Hochreiter Jan 31, 2024
6918a79
Merge branch 'main' into main
M-J-Hochreiter Mar 7, 2024
ee328e7
Split StatePreparation into hpp, cpp file
M-J-Hochreiter Jan 13, 2025
43aa73f
Merge remote-tracking branch 'upstream/main'
M-J-Hochreiter Jan 13, 2025
d8f3abd
Added StandardOperation to include, as library changed
M-J-Hochreiter Jan 13, 2025
d7e69f4
🎨 pre-commit fixes
pre-commit-ci[bot] Jan 13, 2025
84b5041
Refactored StatePreparation files to match the other algorithms
M-J-Hochreiter Jan 14, 2025
881cab7
🎨 pre-commit fixes
pre-commit-ci[bot] Jan 14, 2025
a6f4ff1
removed old StatePreparation file
M-J-Hochreiter Jan 14, 2025
9cbc8ce
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Jan 14, 2025
4918724
Compiler error and linting
M-J-Hochreiter Jan 14, 2025
066c4a3
🎨 pre-commit fixes
pre-commit-ci[bot] Jan 14, 2025
1a72f17
Fixed all build issues and linting problems
M-J-Hochreiter Jan 14, 2025
4119d2b
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Jan 14, 2025
63a81d3
🎨 pre-commit fixes
pre-commit-ci[bot] Jan 14, 2025
d29032f
Fixed last compile error
M-J-Hochreiter Jan 14, 2025
d0b7559
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Jan 14, 2025
fc06910
Made zero check more robust
M-J-Hochreiter Jan 14, 2025
9cbec23
🎨 pre-commit fixes
pre-commit-ci[bot] Jan 14, 2025
c41302d
fixed equality check on floating point with bit manipulation
M-J-Hochreiter Jan 16, 2025
036f68a
🎨 pre-commit fixes
pre-commit-ci[bot] Jan 16, 2025
b91836f
empty check with proper function
M-J-Hochreiter Jan 16, 2025
8a8899d
🎨 pre-commit fixes
pre-commit-ci[bot] Jan 16, 2025
d50e1e3
Merge remote-tracking branch 'upstream/main'
M-J-Hochreiter Jan 16, 2025
f281a75
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Jan 16, 2025
79a62f4
fixed new flatten operation function, added first test structure
M-J-Hochreiter Jan 17, 2025
1d862ac
🎨 pre-commit fixes
pre-commit-ci[bot] Jan 17, 2025
95d1832
removed test suffix
M-J-Hochreiter Jan 17, 2025
743da30
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Jan 17, 2025
1e4aba1
Merge remote-tracking branch 'upstream/main'
M-J-Hochreiter Jan 26, 2025
84a32fa
first few signature changes, docstring adapted
M-J-Hochreiter Jan 27, 2025
971d880
🎨 pre-commit fixes
pre-commit-ci[bot] Jan 27, 2025
54a703f
Merge remote-tracking branch 'upstream/main'
M-J-Hochreiter Feb 3, 2025
2efb7d1
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Feb 3, 2025
1436938
Trying to directly access control qubit and increment it
M-J-Hochreiter Feb 3, 2025
f46cbdf
🎨 pre-commit fixes
pre-commit-ci[bot] Feb 3, 2025
15d4535
fixed seg fault due to modification while looping over container
M-J-Hochreiter Feb 5, 2025
0cff4ca
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Feb 5, 2025
dad0cf5
cx gate control and target args have changed in lib -> flipped args w…
M-J-Hochreiter Feb 5, 2025
9a7dc3e
changed all double comparisons with 0 to use EPS
M-J-Hochreiter Feb 5, 2025
b3209bb
Changed to different constructor when creating StandardOperation as w…
M-J-Hochreiter Feb 5, 2025
a90ada4
removed OpType constructor and used the enum for RY,RZ gates
M-J-Hochreiter Feb 5, 2025
cabd608
🎨 pre-commit fixes
pre-commit-ci[bot] Feb 5, 2025
fa35de4
used reverse of QuantumComputation instead of std
M-J-Hochreiter Feb 5, 2025
ce53695
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Feb 5, 2025
e22906a
changed unsigned int casts to use predefined Qubit
M-J-Hochreiter Feb 5, 2025
3d657b1
Changed localNumQubits to be int by casting it directly, avoiding all…
M-J-Hochreiter Feb 5, 2025
d25f2bc
🎨 pre-commit fixes
pre-commit-ci[bot] Feb 5, 2025
6e96ded
changed localNumQubits from size_t to Qubit, to avoid implicit conver…
M-J-Hochreiter Feb 5, 2025
e973bc6
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Feb 5, 2025
432e357
🎨 pre-commit fixes
pre-commit-ci[bot] Feb 5, 2025
d526a9a
Made EPS a configurable parameter of StatePreparation
M-J-Hochreiter Feb 6, 2025
d7b1177
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Feb 6, 2025
c0f4827
🎨 pre-commit fixes
pre-commit-ci[bot] Feb 6, 2025
93a2374
fixed last EPS missing
M-J-Hochreiter Feb 6, 2025
c4bc7f5
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Feb 6, 2025
4f36ece
moved multiplex comment into docstring
M-J-Hochreiter Feb 6, 2025
50f7a60
the last flattening of the circuit does not seem to affect the outcome
M-J-Hochreiter Feb 6, 2025
4ce0865
changed all push_backs to emplace_back
M-J-Hochreiter Feb 6, 2025
d6010d3
🎨 pre-commit fixes
pre-commit-ci[bot] Feb 6, 2025
50a2c1c
messed up order of const and reference
M-J-Hochreiter Feb 6, 2025
71d8da0
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Feb 6, 2025
9394465
🎨 pre-commit fixes
pre-commit-ci[bot] Feb 6, 2025
6858cd0
reserved space of vectors where possible
M-J-Hochreiter Feb 6, 2025
987bcc1
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Feb 6, 2025
e574dc9
directly constructing control set instead of vector first
M-J-Hochreiter Feb 6, 2025
d93cec0
Fixed unnecessary temporary object using emplace
M-J-Hochreiter Feb 6, 2025
bccc324
Merge remote-tracking branch 'upstream/main'
M-J-Hochreiter Feb 10, 2025
ff662c2
Fixed linting suggestions
M-J-Hochreiter Feb 10, 2025
5d052a5
naming convention
M-J-Hochreiter Feb 10, 2025
b298897
added first tests, global phase not working
M-J-Hochreiter Feb 12, 2025
1a01248
Merge branch 'main' of https://github.com/cda-tum/mqt-core
M-J-Hochreiter Feb 17, 2025
33d4ced
🐛 properly account for global phases in simulation
burgholzer Feb 17, 2025
42d6eaa
Merge branch 'main' of https://github.com/cda-tum/mqt-core
M-J-Hochreiter Feb 18, 2025
7f8e302
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Feb 18, 2025
bacf6ba
Fixed negative globalphase, added all tests
M-J-Hochreiter Feb 18, 2025
247cf45
🎨 pre-commit fixes
pre-commit-ci[bot] Feb 18, 2025
785512b
fixed linting issues
M-J-Hochreiter Feb 18, 2025
dcbb4c4
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Feb 18, 2025
7eb85d3
🎨 pre-commit fixes
pre-commit-ci[bot] Feb 18, 2025
82d9741
fixed last linting issue
M-J-Hochreiter Feb 18, 2025
2cce01f
used wrong include
M-J-Hochreiter Feb 18, 2025
b914e03
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Feb 18, 2025
952977e
🎨 pre-commit fixes
pre-commit-ci[bot] Feb 18, 2025
e8b4d57
removed empty test
M-J-Hochreiter Feb 18, 2025
d521ec6
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Feb 18, 2025
eef6c5e
Reformat docstring
M-J-Hochreiter Feb 19, 2025
875dc15
Copied input vector to not modify the users input
M-J-Hochreiter Feb 19, 2025
5250e0c
🎨 pre-commit fixes
pre-commit-ci[bot] Feb 19, 2025
769a921
Fixed linking problems for python tests
M-J-Hochreiter Feb 19, 2025
e2dbe30
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Feb 19, 2025
156aa96
made eps const, formatting, changed manual exp to polar
M-J-Hochreiter Feb 19, 2025
0bf7aa0
🎨 pre-commit fixes
pre-commit-ci[bot] Feb 19, 2025
cdaaea2
Added tests for invalid arguments
M-J-Hochreiter Feb 19, 2025
146846c
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Feb 19, 2025
5b20df1
🎨 pre-commit fixes
pre-commit-ci[bot] Feb 19, 2025
f331adc
fixed linting problems
M-J-Hochreiter Feb 19, 2025
b78391e
Merge branch 'main' of https://github.com/M-J-Hochreiter/mqt-core
M-J-Hochreiter Feb 19, 2025
f011c8d
Update test/algorithms/test_statepreparation.cpp
burgholzer Feb 21, 2025
3ab5843
Update test/algorithms/test_statepreparation.cpp
burgholzer Feb 21, 2025
fad2be8
Update test/algorithms/test_statepreparation.cpp
burgholzer Feb 21, 2025
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
Prev Previous commit
Next Next commit
Refactored StatePreparation files to match the other algorithms
M-J-Hochreiter committed Jan 14, 2025

Verified

This commit was signed with the committer’s verified signature.
ExampleWasTaken ExampleWasTaken
commit 84b5041f9372291f22054e338d272b8e4f73c750
43 changes: 9 additions & 34 deletions include/mqt-core/algorithms/StatePreparation.hpp
Original file line number Diff line number Diff line change
@@ -9,17 +9,11 @@

#pragma once

#include "CircuitOptimizer.hpp"
#include "QuantumComputation.hpp"
#include "ir/operations/StandardOperation.hpp"

#include <cmath>
#include <complex>
#include <utility>

namespace qc {
/**
* Class to prepare a generic Quantum State from a list of normalized complex
* Prepares a generic Quantum State from a list of normalized complex
amplitudes
* Adapted implementation of Qiskit State Preparation:
*
@@ -28,32 +22,13 @@ namespace qc {
* Shende, Bullock, Markov. Synthesis of Quantum Logic Circuits (2004)
[`https://ieeexplore.ieee.org/document/1629135`]
* */
class StatePreparation : public QuantumComputation {

public:
explicit StatePreparation(
const std::vector<std::complex<double>>& amplitudes);

private:
template <typename T> static bool isNormalized(std::vector<T> vec);
template <typename T> static double twoNorm(std::vector<T> vec);
static std::vector<std::vector<double>>
kroneckerProduct(std::vector<std::vector<double>> matrixA,
std::vector<std::vector<double>> matrixB);
static std::vector<std::vector<double>> createIdentity(size_t size);
static std::vector<double>
matrixVectorProd(const std::vector<std::vector<double>>& matrix,
std::vector<double> vector);
static qc::QuantumComputation
gatesToUncompute(std::vector<std::complex<double>> amplitudes,
size_t numQubits);
static std::tuple<std::vector<std::complex<double>>, std::vector<double>,
std::vector<double>>
rotationsToDisentangle(std::vector<std::complex<double>> amplitudes);
static std::tuple<std::complex<double>, double, double>
blochAngles(std::complex<double> const complexA,
std::complex<double> const complexB);
static qc::QuantumComputation
multiplex(qc::OpType targetGate, std::vector<double> angles, bool lastCnot);
};
/**
* @throws invalid_argument when amplitudes are not normalized or length not
* power of 2
* @param list of complex amplitudes to initialize to
* @return MQT Circuit that initializes a state
* */
[[nodiscard]] auto createStatePreparationCircuit(
const std::vector<std::complex<double>>& amplitudes) -> QuantumComputation;
} // namespace qc
120 changes: 74 additions & 46 deletions src/algorithms/StatePreparation.cpp
Original file line number Diff line number Diff line change
@@ -6,30 +6,60 @@
*
* Licensed under the MIT License
*/

#include "algorithms/StatePreparation.hpp"

#include "CircuitOptimizer.hpp"
#include "ir/operations/StandardOperation.hpp"

#include <cmath>
#include <complex>
#include <utility>

static const double EPS = 1e-10;

namespace qc {
using Matrix = std::vector<std::vector<double>>;

StatePreparation::StatePreparation(
const std::vector<std::complex<double>>& amplitudes) {}
auto createStatePreparationCircuit(
const std::vector<std::complex<double>>& amplitudes) -> QuantumComputation {

if (!isNormalized(amplitudes)) {
throw std::invalid_argument{
"Using State Preparation with Amplitudes that are not normalized"};
}

// get number of qubits needed
double const numQubits = std::log2(amplitudes.size());

if (numQubits == 0 || std::floor(numQubits) != numQubits) {
throw std::invalid_argument{
"Using State Preparation with vector size that is not a power of 2"};
}

QuantumComputation toZeroCircuit =
gatesToUncompute(amplitudes, static_cast<size_t>(numQubits));

// invert circuit
CircuitOptimizer::flattenOperations(toZeroCircuit);
toZeroCircuit.invert();

return toZeroCircuit;
}

template <typename T> bool StatePreparation::isNormalized(std::vector<T> vec) {
template <typename T>
[[noexcept]] auto isNormalized(std::vector<T> vec) -> bool {
return std::abs(1 - twoNorm(vec)) < EPS;
}

template <typename T> double StatePreparation::twoNorm(std::vector<T> vec) {
template <typename T>[[noexcept]] auto twoNorm(std::vector<T> vec) -> double {
double norm = 0;
for (auto elem : vec) {
norm += std::norm(elem);
}
return sqrt(norm);
}

Matrix StatePreparation::kroneckerProduct(Matrix matrixA, Matrix matrixB) {
[[noexcept]] auto kroneckerProduct(Matrix matrixA, Matrix matrixB) -> Matrix {
size_t const rowA = matrixA.size();
size_t const rowB = matrixB.size();
size_t const colA = matrixA[0].size();
@@ -55,7 +85,7 @@ Matrix StatePreparation::kroneckerProduct(Matrix matrixA, Matrix matrixB) {
return newMatrix;
}

Matrix StatePreparation::createIdentity(size_t size) {
[[noexcept]] auto createIdentity(size_t size) -> Matrix {
Matrix identity{
std::vector<std::vector<double>>(size, std::vector<double>(size, 0))};
for (size_t i = 0; i < size; ++i) {
@@ -64,9 +94,9 @@ Matrix StatePreparation::createIdentity(size_t size) {
return identity;
}

std::vector<double>
StatePreparation::matrixVectorProd(const Matrix& matrix,
std::vector<double> vector) {
[[noexcept]] auto
matrixVectorProd(const Matrix& matrix,
std::vector<double> vector) -> std::vector<double> {
std::vector<double> result;
for (const auto& matrixVec : matrix) {
double sum{0};
@@ -79,10 +109,9 @@ StatePreparation::matrixVectorProd(const Matrix& matrix,
}

// creates circuit that takes desired vector to zero
qc::QuantumComputation
StatePreparation::gatesToUncompute(std::vector<std::complex<double>> amplitudes,
size_t numQubits) {
qc::QuantumComputation disentangler{numQubits};
[[noexcept]] auto gatesToUncompute(std::vector<std::complex<double>> amplitudes,
size_t numQubits) -> QuantumComputation {
QuantumComputation disentangler{numQubits};
for (size_t i = 0; i < numQubits; ++i) {
// rotations to disentangle LSB
auto [remainingParams, thetas, phis] = rotationsToDisentangle(amplitudes);
@@ -96,8 +125,8 @@ StatePreparation::gatesToUncompute(std::vector<std::complex<double>> amplitudes,
}
if (phisNorm != 0) {
// call multiplex with RZGate
qc::QuantumComputation rzMultiplexer =
multiplex(qc::OpType{qc::RZ}, phis, addLastCnot);
QuantumComputation rzMultiplexer =
multiplex(OpType{RZ}, phis, addLastCnot);
// append rzMultiplexer to disentangler, but it should only attach on
// qubits i-numQubits, thus "i" is added to the local qubit indices
for (auto& op : rzMultiplexer) {
@@ -107,16 +136,16 @@ StatePreparation::gatesToUncompute(std::vector<std::complex<double>> amplitudes,
for (auto control : op->getControls()) {
// there were some errors when accessing the qubit directly and
// adding to it
op->setControls(qc::Controls{
qc::Control{control.qubit + static_cast<unsigned int>(i)}});
op->setControls(
Controls{Control{control.qubit + static_cast<unsigned int>(i)}});
}
}
disentangler.emplace_back<qc::Operation>(rzMultiplexer.asOperation());
disentangler.emplace_back<Operation>(rzMultiplexer.asOperation());
}
if (thetasNorm != 0) {
// call multiplex with RYGate
qc::QuantumComputation ryMultiplexer =
multiplex(qc::OpType{qc::RY}, thetas, addLastCnot);
QuantumComputation ryMultiplexer =
multiplex(OpType{RY}, thetas, addLastCnot);
// append reversed ry_multiplexer to disentangler, but it should only
// attach on qubits i-numQubits, thus "i" is added to the local qubit
// indices
@@ -128,11 +157,11 @@ StatePreparation::gatesToUncompute(std::vector<std::complex<double>> amplitudes,
for (auto control : op->getControls()) {
// there were some errors when accessing the qubit directly and
// adding to it
op->setControls(qc::Controls{
qc::Control{control.qubit + static_cast<unsigned int>(i)}});
op->setControls(
Controls{Control{control.qubit + static_cast<unsigned int>(i)}});
}
}
disentangler.emplace_back<qc::Operation>(ryMultiplexer.asOperation());
disentangler.emplace_back<Operation>(ryMultiplexer.asOperation());
}
}
// adjust global phase according to the last e^(it)
@@ -146,10 +175,10 @@ StatePreparation::gatesToUncompute(std::vector<std::complex<double>> amplitudes,

// works out Ry and Rz rotation angles used to disentangle LSB qubit
// rotations make up block diagonal matrix U
std::tuple<std::vector<std::complex<double>>, std::vector<double>,
std::vector<double>>
StatePreparation::rotationsToDisentangle(
std::vector<std::complex<double>> amplitudes) {
[[noexcept]] auto
rotationsToDisentangle(std::vector<std::complex<double>> amplitudes)
-> std::tuple<std::vector<std::complex<double>>, std::vector<double>,
std::vector<double>> {
std::vector<std::complex<double>> remainingVector;
std::vector<double> thetas;
std::vector<double> phis;
@@ -164,9 +193,9 @@ StatePreparation::rotationsToDisentangle(
return {remainingVector, thetas, phis};
}

std::tuple<std::complex<double>, double, double>
StatePreparation::blochAngles(std::complex<double> const complexA,
std::complex<double> const complexB) {
[[noexcept]] auto blochAngles(std::complex<double> const complexA,
std::complex<double> const complexB)
-> std::tuple<std::complex<double>, double, double> {
double theta{0};
double phi{0};
double finalT{0};
@@ -191,18 +220,17 @@ StatePreparation::blochAngles(std::complex<double> const complexA,
* @param lastCnot : add last cnot if true
* @return multiplexer circuit as QuantumComputation
*/
qc::QuantumComputation StatePreparation::multiplex(qc::OpType targetGate,
std::vector<double> angles,
bool lastCnot) {
[[noexcept]] auto multiplex(OpType targetGate, std::vector<double> angles,
bool lastCnot) -> QuantumComputation {
size_t const listLen = angles.size();
double const localNumQubits =
std::floor(std::log2(static_cast<double>(listLen))) + 1;
qc::QuantumComputation multiplexer{static_cast<size_t>(localNumQubits)};
QuantumComputation multiplexer{static_cast<size_t>(localNumQubits)};
// recursion base case
if (localNumQubits == 1) {
multiplexer.emplace_back<qc::StandardOperation>(
multiplexer.getNqubits(), qc::Controls{}, 0, targetGate,
std::vector{angles[0]});
multiplexer.emplace_back<StandardOperation>(multiplexer.getNqubits(),
Controls{}, 0, targetGate,
std::vector{angles[0]});
return multiplexer;
}

@@ -218,31 +246,31 @@ qc::QuantumComputation StatePreparation::multiplex(qc::OpType targetGate,
std::make_move_iterator(angles.begin()),
std::make_move_iterator(angles.begin() +
static_cast<int64_t>(listLen) / 2)};
qc::QuantumComputation multiplex1 = multiplex(targetGate, angles1, false);
QuantumComputation multiplex1 = multiplex(targetGate, angles1, false);

// append multiplex1 to multiplexer
multiplexer.emplace_back<qc::Operation>(multiplex1.asOperation());
multiplexer.emplace_back<Operation>(multiplex1.asOperation());
// flips the LSB qubit, control on MSB
multiplexer.cx(0, static_cast<qc::Qubit>(localNumQubits - 1));
multiplexer.cx(0, static_cast<Qubit>(localNumQubits - 1));

std::vector<double> const angles2{std::make_move_iterator(angles.begin()) +
static_cast<int64_t>(listLen) / 2,
std::make_move_iterator(angles.end())};
qc::QuantumComputation multiplex2 = multiplex(targetGate, angles2, false);
QuantumComputation multiplex2 = multiplex(targetGate, angles2, false);

// extra efficiency by reversing (!= inverting) second multiplex
if (listLen > 1) {
multiplex2.reverse();
multiplexer.emplace_back<qc::Operation>(multiplex2.asOperation());
multiplexer.emplace_back<Operation>(multiplex2.asOperation());
} else {
multiplexer.emplace_back<qc::Operation>(multiplex2.asOperation());
multiplexer.emplace_back<Operation>(multiplex2.asOperation());
}

if (lastCnot) {
multiplexer.cx(0, static_cast<qc::Qubit>(localNumQubits - 1));
multiplexer.cx(0, static_cast<Qubit>(localNumQubits - 1));
}

qc::CircuitOptimizer::flattenOperations(multiplexer);
CircuitOptimizer::flattenOperations(multiplexer);
return multiplexer;
}