From 20404bc246f4bdbb7f629a592219ada1770d9e03 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 15 Sep 2025 22:47:14 +0200 Subject: [PATCH 001/237] snapshot --- .../mlir/Dialect/MQTOpt/Transforms/Passes.h | 1 + .../MQTOpt/Transforms/GateDecomposition.cpp | 44 +++++ .../Transforms/GateDecompositionPattern.cpp | 118 ++++++++++++ mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 177 ++++++++++++++++++ 4 files changed, 340 insertions(+) create mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp create mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp create mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.h index 5bc05293c5..96c87bd4bf 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.h +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.h @@ -34,6 +34,7 @@ enum class PlacementStrategy : std::uint8_t { Random, Identity }; #include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" // IWYU pragma: export void populateGateEliminationPatterns(mlir::RewritePatternSet& patterns); +void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns); void populateMergeRotationGatesPatterns(mlir::RewritePatternSet& patterns); void populateSwapReconstructionAndElisionPatterns( mlir::RewritePatternSet& patterns); diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp new file mode 100644 index 0000000000..8ed6be639f --- /dev/null +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" + +#include +#include +#include +#include + +namespace mqt::ir::opt { + +#define GEN_PASS_DEF_GATEDECOMPOSITION +#include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" + +/** + * @brief This pass attempts to cancel consecutive self-inverse operations. + */ +struct GateDecomposition final : impl::GateDecompositionBase { + + void runOnOperation() override { + // Get the current operation being operated on. + auto op = getOperation(); + auto* ctx = &getContext(); + + // Define the set of patterns to use. + mlir::RewritePatternSet patterns(ctx); + populateGateDecompositionPatterns(patterns); + + // Apply patterns in an iterative and greedy manner. + if (mlir::failed(mlir::applyPatternsGreedily(op, std::move(patterns)))) { + signalPassFailure(); + } + } +}; + +} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp new file mode 100644 index 0000000000..92e1c3c73d --- /dev/null +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "Helpers.h" +#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" +#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mqt::ir::opt { + +/** + * @brief This pattern attempts to cancel consecutive self-inverse operations. + */ +struct GateDecompositionPattern final + : mlir::OpInterfaceRewritePattern { + + explicit GateDecompositionPattern(mlir::MLIRContext* context) + : OpInterfaceRewritePattern(context) {} + + dd::TwoQubitGateMatrix twoQubitIdentity = { + {{1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}}; + + mlir::LogicalResult + matchAndRewrite(UnitaryInterface op, + mlir::PatternRewriter& rewriter) const override { + auto series = getTwoQubitSeries(op); + if (series.size() <= 3) { + return mlir::failure(); + } + + dd::TwoQubitGateMatrix unitaryMatrix = dd::opToTwoQubitGateMatrix(qc::I); + for (auto&& gate : series) { + if (auto gateMatrix = helpers::getUnitaryMatrix(gate)) { + unitaryMatrix = helpers::multiply(unitaryMatrix, *gateMatrix); + } + } + + twoQubitDecompose(unitaryMatrix); + + return mlir::success(); + } + + [[nodiscard]] static llvm::SmallVector + getTwoQubitSeries(UnitaryInterface op) { + llvm::SmallVector qubits(2); + llvm::SmallVector result; + + if (helpers::isSingleQubitOperation(op)) { + qubits = {op->getResult(0), mlir::Value{}}; + } else if (helpers::isTwoQubitOperation(op)) { + qubits = op->getResults(); + } else { + return result; + } + while (true) { + for (auto&& user : op->getUsers()) { + auto userUnitary = llvm::cast(user); + if (helpers::isSingleQubitOperation(userUnitary)) { + auto&& operand = userUnitary->getOperand(0); + auto* it = llvm::find(qubits, operand); + if (it == qubits.end()) { + return result; + } + *it = userUnitary->getResult(0); + + result.push_back(userUnitary); + } else if (helpers::isTwoQubitOperation(userUnitary)) { + auto&& firstOperand = userUnitary->getOperand(0); + auto&& secondOperand = userUnitary->getOperand(1); + auto* firstQubitIt = llvm::find(qubits, firstOperand); + auto* secondQubitIt = llvm::find(qubits, secondOperand); + if (firstQubitIt == qubits.end() || secondQubitIt == qubits.end()) { + return result; + } + *firstQubitIt = userUnitary->getResult(0); + *secondQubitIt = userUnitary->getResult(1); + + result.push_back(userUnitary); + } else { + return result; + } + } + return result; + } + } + + void twoQubitDecompose(dd::TwoQubitGateMatrix unitaryMatrix) { + + } +}; + +/** + * @brief Populates the given pattern set with patterns for gate + * decomposition. + */ +void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns) { + patterns.add(patterns.getContext()); +} + +} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h new file mode 100644 index 0000000000..f4976b44e1 --- /dev/null +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include "dd/GateMatrixDefinitions.hpp" +#include "dd/Package.hpp" +#include "ir/Definitions.hpp" +#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" + +#include +#include +#include + +namespace mqt::ir::opt::helpers { + +std::optional mlirValueToFp(mlir::Value value); + +template +std::optional performMlirFloatBinaryOp(mlir::Value value, Func&& func) { + if (auto op = value.getDefiningOp()) { + auto lhs = mlirValueToFp(op.getLhs()); + auto rhs = mlirValueToFp(op.getRhs()); + if (lhs && rhs) { + return std::invoke(std::forward(func), *lhs, *rhs); + } + } + return std::nullopt; +} + +template +std::optional performMlirFloatUnaryOp(mlir::Value value, Func&& func) { + if (auto op = value.getDefiningOp()) { + if (auto operand = mlirValueToFp(op.getOperand())) { + return std::invoke(std::forward(func), *operand); + } + } + return std::nullopt; +} + +inline std::optional mlirValueToFp(mlir::Value value) { + if (auto op = value.getDefiningOp()) { + if (auto attr = llvm::dyn_cast(op.getValue())) { + return attr.getValueAsDouble(); + } + return std::nullopt; + } + if (auto result = performMlirFloatUnaryOp( + value, [](qc::fp a) { return -a; })) { + return result; + } + if (auto result = performMlirFloatUnaryOp( + value, [](qc::fp a) { return a; })) { + return result; + } + if (auto result = performMlirFloatUnaryOp( + value, [](qc::fp a) { return a; })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](qc::fp a, qc::fp b) { return std::max(a, b); })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](qc::fp a, qc::fp b) { return std::max(a, b); })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](qc::fp a, qc::fp b) { return std::min(a, b); })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](qc::fp a, qc::fp b) { return std::min(a, b); })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](qc::fp a, qc::fp b) { return std::fmod(a, b); })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](qc::fp a, qc::fp b) { return a + b; })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](qc::fp a, qc::fp b) { return a + b; })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](qc::fp a, qc::fp b) { return a + b; })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](qc::fp a, qc::fp b) { return a + b; })) { + return result; + } + return std::nullopt; +} + +[[nodiscard]] inline std::vector getParameters(UnitaryInterface op) { + std::vector parameters; + for (auto&& param : op.getParams()) { + if (auto value = helpers::mlirValueToFp(param)) { + parameters.push_back(*value); + } + } + return parameters; +} + +[[nodiscard]] inline qc::OpType getQcType(UnitaryInterface op) { + try { + const std::string type = op->getName().stripDialect().str(); + return qc::opTypeFromString(type); + } catch (const std::invalid_argument& /*exception*/) { + return qc::OpType::None; + } +} + +[[nodiscard]] inline bool isSingleQubitOperation(UnitaryInterface op) { + auto&& inQubits = op.getInQubits(); + auto&& outQubits = op.getOutQubits(); + bool isSingleQubitOp = + inQubits.size() == 1 && outQubits.size() == 1 && !op.isControlled(); + assert(isSingleQubitOp == qc::isSingleQubitGate(getQcType(op))); + return isSingleQubitOp; +} + +[[nodiscard]] inline bool isTwoQubitOperation(UnitaryInterface op) { + auto&& inQubits = op.getInQubits(); + auto&& inPosCtrlQubits = op.getPosCtrlInQubits(); + auto&& inNegCtrlQubits = op.getNegCtrlInQubits(); + auto inQubitSize = + inQubits.size() + inPosCtrlQubits.size() + inNegCtrlQubits.size(); + auto&& outQubits = op.getOutQubits(); + auto&& outPosCtrlQubits = op.getPosCtrlInQubits(); + auto&& outNegCtrlQubits = op.getNegCtrlInQubits(); + auto outQubitSize = + outQubits.size() + outPosCtrlQubits.size() + outNegCtrlQubits.size(); + bool isTwoQubitOp = inQubitSize == 2 && outQubitSize == 2; + assert(isTwoQubitOp == qc::isTwoQubitGate(getQcType(op))); + return isTwoQubitOp; +} + +[[nodiscard]] inline std::optional +getUnitaryMatrix(UnitaryInterface op) { + auto type = getQcType(op); + auto parameters = getParameters(op); + + if (isTwoQubitOperation(op)) { + return dd::opToTwoQubitGateMatrix(type, parameters); + } + return std::nullopt; +} + +[[nodiscard]] inline dd::GateMatrix multiply(std::complex factor, + dd::GateMatrix matrix) { + return {factor * matrix.at(0), factor * matrix.at(1), factor * matrix.at(2), + factor * matrix.at(3)}; +} + +[[nodiscard]] inline dd::TwoQubitGateMatrix +kroneckerProduct(dd::GateMatrix lhs, dd::GateMatrix rhs) { + return {multiply(lhs.at(0), rhs), multiply(lhs.at(1), rhs), + multiply(lhs.at(2), rhs), multiply(lhs.at(3), rhs)}; +} + +[[nodiscard]] inline dd::TwoQubitGateMatrix +multiply(dd::TwoQubitGateMatrix lhs, dd::TwoQubitGateMatrix rhs) { + return {}; +} +} // namespace mqt::ir::opt::helpers From 8fd23f69dd1443f28e0a1407e4f1c5f421663dfb Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 16 Sep 2025 13:01:25 +0200 Subject: [PATCH 002/237] copied rust code --- .../Transforms/GateDecompositionPattern.cpp | 611 ++++++++++++++++++ 1 file changed, 611 insertions(+) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 92e1c3c73d..3c9af492ea 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -12,6 +12,7 @@ #include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" #include "mlir/Dialect/MQTOpt/Transforms/Passes.h" +#include #include #include #include @@ -102,8 +103,618 @@ struct GateDecompositionPattern final } } + enum class Specialization { + General, + IdEquiv, + SWAPEquiv, + PartialSWAPEquiv, + PartialSWAPFlipEquiv, + ControlledEquiv, + MirrorControlledEquiv, + // These next 3 gates use the definition of fSim from eq (1) in: + // https://arxiv.org/pdf/2001.08343.pdf + fSimaabEquiv, + fSimabbEquiv, + fSimabmbEquiv, + }; + + struct TwoQubitWeylDecomposition { + qc::fp a; + qc::fp b; + qc::fp c; + qc::fp global_phase; + std::array, 4> K1l; + std::array, 4> K2l; + std::array, 4> K1r; + std::array, 4> K2r; + Specialization specialization; + //EulerBasis default_euler_basis; // TODO: simply use ZYZ? + std::optional requested_fidelity; + qc::fp calculated_fidelity; + dd::TwoQubitGateMatrix unitary_matrix; + + TwoQubitWeylDecomposition new_inner( + std::array, 4> unitary_matrix, + + std::optional fidelity, + std::optional _specialization + ) { + constexpr std::array, 4> IPZ = [[IM, C_ZERO], [C_ZERO, M_IM]]; + constexpr std::array, 4> IPY =[[C_ZERO, C_ONE], [C_M_ONE, C_ZERO]]; + constexpr std::array, 4> IPX = [[C_ZERO, IM], [IM, C_ZERO]]; + + + auto u = unitary_matrix; + let det_u = u.view().into_faer().determinant(); + let det_pow = det_u.powf(-0.25); + u.mapv_inplace(|x| x * det_pow); + let mut global_phase = det_u.arg() / 4.; + let u_p = magic_basis_transform(u.view(), MagicBasisTransform::OutOf); + let m2 = u_p.t().dot(&u_p); + let default_euler_basis = EulerBasis::ZYZ; + + // M2 is a symmetric complex matrix. We need to decompose it as M2 = P D P^T where + // P ∈ SO(4), D is diagonal with unit-magnitude elements. + // + // We can't use raw `eig` directly because it isn't guaranteed to give us real or orthogonal + // eigenvectors. Instead, since `M2` is complex-symmetric, + // M2 = A + iB + // for real-symmetric `A` and `B`, and as + // M2^+ @ M2 = A^2 + B^2 + i [A, B] = 1 + // we must have `A` and `B` commute, and consequently they are simultaneously diagonalizable. + // Mixing them together _should_ account for any degeneracy problems, but it's not + // guaranteed, so we repeat it a little bit. The fixed seed is to make failures + // deterministic; the value is not important. + let mut state = Pcg64Mcg::seed_from_u64(2023); + let mut found = false; + let mut d: Array1 = Array1::zeros(0); + let mut p: Array2 = Array2::zeros((0, 0)); + for i in 0..100 { + let rand_a: f64; + let rand_b: f64; + // For debugging the algorithm use the same RNG values from the + // previous Python implementation for the first random trial. + // In most cases this loop only executes a single iteration and + // using the same rng values rules out possible RNG differences + // as the root cause of a test failure + if i == 0 { + rand_a = 1.2602066112249388; + rand_b = 0.22317849046722027; + } else { + rand_a = state.sample(StandardNormal); + rand_b = state.sample(StandardNormal); + } + let m2_real = m2.mapv(|val| rand_a * val.re + rand_b * val.im); + let p_inner = m2_real + .view() + .into_faer() + .self_adjoint_eigen(Lower) + .map_err(|e| QiskitError::new_err(format!("{e:?}")))? + .U() + .into_ndarray() + .mapv(Complex64::from); + let d_inner = p_inner.t().dot(&m2).dot(&p_inner).diag().to_owned(); + let mut diag_d: Array2 = Array2::zeros((4, 4)); + diag_d + .diag_mut() + .iter_mut() + .enumerate() + .for_each(|(index, x)| *x = d_inner[index]); + + let compare = p_inner.dot(&diag_d).dot(&p_inner.t()); + found = abs_diff_eq!(compare.view(), m2, epsilon = 1.0e-13); + if found { + p = p_inner; + d = d_inner; + break; + } + } + if !found { + return Err(QiskitError::new_err(format!( + "TwoQubitWeylDecomposition: failed to diagonalize M2. Please report this at https://github.com/Qiskit/qiskit-terra/issues/4159. Input: {unitary_matrix:?}" + ))); + } + let mut d = -d.map(|x| x.arg() / 2.); + d[3] = -d[0] - d[1] - d[2]; + let mut cs: SmallVec<[f64; 3]> = (0..3) + .map(|i| ((d[i] + d[3]) / 2.0).rem_euclid(TWO_PI)) + .collect(); + let cstemp: SmallVec<[f64; 3]> = cs + .iter() + .map(|x| x.rem_euclid(PI2)) + .map(|x| x.min(PI2 - x)) + .collect(); + let mut order = arg_sort(&cstemp); + (order[0], order[1], order[2]) = (order[1], order[2], order[0]); + (cs[0], cs[1], cs[2]) = (cs[order[0]], cs[order[1]], cs[order[2]]); + (d[0], d[1], d[2]) = (d[order[0]], d[order[1]], d[order[2]]); + let mut p_orig = p.clone(); + for (i, item) in order.iter().enumerate().take(3) { + let slice_a = p.slice_mut(s![.., i]); + let slice_b = p_orig.slice_mut(s![.., *item]); + Zip::from(slice_a).and(slice_b).for_each(::std::mem::swap); + } + if p.view().into_faer().determinant().re < 0. { + p.slice_mut(s![.., -1]).mapv_inplace(|x| -x); + } + let mut temp: Array2 = Array2::zeros((4, 4)); + temp.diag_mut() + .iter_mut() + .enumerate() + .for_each(|(index, x)| *x = (IM * d[index]).exp()); + let k1 = magic_basis_transform(u_p.dot(&p).dot(&temp).view(), MagicBasisTransform::Into); + let k2 = magic_basis_transform(p.t(), MagicBasisTransform::Into); + + #[allow(non_snake_case)] + let (mut K1l, mut K1r, phase_l) = decompose_two_qubit_product_gate(k1.view())?; + #[allow(non_snake_case)] + let (K2l, mut K2r, phase_r) = decompose_two_qubit_product_gate(k2.view())?; + global_phase += phase_l + phase_r; + + // Flip into Weyl chamber + if cs[0] > PI2 { + cs[0] -= PI32; + K1l = K1l.dot(&ipy); + K1r = K1r.dot(&ipy); + global_phase += PI2; + } + if cs[1] > PI2 { + cs[1] -= PI32; + K1l = K1l.dot(&ipx); + K1r = K1r.dot(&ipx); + global_phase += PI2; + } + let mut conjs = 0; + if cs[0] > PI4 { + cs[0] = PI2 - cs[0]; + K1l = K1l.dot(&ipy); + K2r = ipy.dot(&K2r); + conjs += 1; + global_phase -= PI2; + } + if cs[1] > PI4 { + cs[1] = PI2 - cs[1]; + K1l = K1l.dot(&ipx); + K2r = ipx.dot(&K2r); + conjs += 1; + global_phase += PI2; + if conjs == 1 { + global_phase -= PI; + } + } + if cs[2] > PI2 { + cs[2] -= PI32; + K1l = K1l.dot(&ipz); + K1r = K1r.dot(&ipz); + global_phase += PI2; + if conjs == 1 { + global_phase -= PI; + } + } + if conjs == 1 { + cs[2] = PI2 - cs[2]; + K1l = K1l.dot(&ipz); + K2r = ipz.dot(&K2r); + global_phase += PI2; + } + if cs[2] > PI4 { + cs[2] -= PI2; + K1l = K1l.dot(&ipz); + K1r = K1r.dot(&ipz); + global_phase -= PI2; + } + let [a, b, c] = [cs[1], cs[0], cs[2]]; + let is_close = |ap: f64, bp: f64, cp: f64| -> bool { + let [da, db, dc] = [a - ap, b - bp, c - cp]; + let tr = 4. + * c64( + da.cos() * db.cos() * dc.cos(), + da.sin() * db.sin() * dc.sin(), + ); + match fidelity { + Some(fid) => tr.trace_to_fid() >= fid, + // Set to false here to default to general specialization in the absence of a + // fidelity and provided specialization. + None => false, + } + }; + + let closest_abc = closest_partial_swap(a, b, c); + let closest_ab_minus_c = closest_partial_swap(a, b, -c); + let mut flipped_from_original = false; + let specialization = match _specialization { + Some(specialization) => specialization, + None => { + if is_close(0., 0., 0.) { + Specialization::IdEquiv + } else if is_close(PI4, PI4, PI4) || is_close(PI4, PI4, -PI4) { + Specialization::SWAPEquiv + } else if is_close(closest_abc, closest_abc, closest_abc) { + Specialization::PartialSWAPEquiv + } else if is_close(closest_ab_minus_c, closest_ab_minus_c, -closest_ab_minus_c) { + Specialization::PartialSWAPFlipEquiv + } else if is_close(a, 0., 0.) { + Specialization::ControlledEquiv + } else if is_close(PI4, PI4, c) { + Specialization::MirrorControlledEquiv + } else if is_close((a + b) / 2., (a + b) / 2., c) { + Specialization::fSimaabEquiv + } else if is_close(a, (b + c) / 2., (b + c) / 2.) { + Specialization::fSimabbEquiv + } else if is_close(a, (b - c) / 2., (c - b) / 2.) { + Specialization::fSimabmbEquiv + } else { + Specialization::General + } + } + }; + let general = TwoQubitWeylDecomposition { + a, + b, + c, + global_phase, + K1l, + K1r, + K2l, + K2r, + specialization: Specialization::General, + default_euler_basis, + requested_fidelity: fidelity, + calculated_fidelity: -1.0, + unitary_matrix, + }; + let mut specialized: TwoQubitWeylDecomposition = match specialization { + // :math:`U \sim U_d(0,0,0) \sim Id` + // + // This gate binds 0 parameters, we make it canonical by setting + // :math:`K2_l = Id` , :math:`K2_r = Id`. + Specialization::IdEquiv => TwoQubitWeylDecomposition { + specialization, + a: 0., + b: 0., + c: 0., + K1l: general.K1l.dot(&general.K2l), + K1r: general.K1r.dot(&general.K2r), + K2l: Array2::eye(2), + K2r: Array2::eye(2), + ..general + }, + // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, -\pi/4) \sim \text{SWAP}` + // + // This gate binds 0 parameters, we make it canonical by setting + // :math:`K2_l = Id` , :math:`K2_r = Id`. + Specialization::SWAPEquiv => { + if c > 0. { + TwoQubitWeylDecomposition { + specialization, + a: PI4, + b: PI4, + c: PI4, + K1l: general.K1l.dot(&general.K2r), + K1r: general.K1r.dot(&general.K2l), + K2l: Array2::eye(2), + K2r: Array2::eye(2), + ..general + } + } else { + flipped_from_original = true; + TwoQubitWeylDecomposition { + specialization, + a: PI4, + b: PI4, + c: PI4, + global_phase: global_phase + PI2, + K1l: general.K1l.dot(&ipz).dot(&general.K2r), + K1r: general.K1r.dot(&ipz).dot(&general.K2l), + K2l: Array2::eye(2), + K2r: Array2::eye(2), + ..general + } + } + } + // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4) \sim \text{SWAP}^\alpha` + // + // This gate binds 3 parameters, we make it canonical by setting: + // + // :math:`K2_l = Id`. + Specialization::PartialSWAPEquiv => { + let closest = closest_partial_swap(a, b, c); + let mut k2l_dag = general.K2l.t().to_owned(); + k2l_dag.view_mut().mapv_inplace(|x| x.conj()); + TwoQubitWeylDecomposition { + specialization, + a: closest, + b: closest, + c: closest, + K1l: general.K1l.dot(&general.K2l), + K1r: general.K1r.dot(&general.K2l), + K2r: k2l_dag.dot(&general.K2r), + K2l: Array2::eye(2), + ..general + } + } + // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4) \sim \text{SWAP}^\alpha` + // + // (a non-equivalent root of SWAP from the TwoQubitWeylPartialSWAPEquiv + // similar to how :math:`x = (\pm \sqrt(x))^2`) + // + // This gate binds 3 parameters, we make it canonical by setting: + // + // :math:`K2_l = Id` + Specialization::PartialSWAPFlipEquiv => { + let closest = closest_partial_swap(a, b, -c); + let mut k2l_dag = general.K2l.t().to_owned(); + k2l_dag.mapv_inplace(|x| x.conj()); + TwoQubitWeylDecomposition { + specialization, + a: closest, + b: closest, + c: -closest, + K1l: general.K1l.dot(&general.K2l), + K1r: general.K1r.dot(&ipz).dot(&general.K2l).dot(&ipz), + K2r: ipz.dot(&k2l_dag).dot(&ipz).dot(&general.K2r), + K2l: Array2::eye(2), + ..general + } + } + // :math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}` + // + // This gate binds 4 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l) Rx(\lambda_l)` , + // :math:`K2_r = Ry(\theta_r) Rx(\lambda_r)` . + Specialization::ControlledEquiv => { + let euler_basis = EulerBasis::XYX; + let [k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l.view(), euler_basis); + let [k2rtheta, k2rphi, k2rlambda, k2rphase] = + angles_from_unitary(general.K2r.view(), euler_basis); + TwoQubitWeylDecomposition { + specialization, + a, + b: 0., + c: 0., + global_phase: global_phase + k2lphase + k2rphase, + K1l: general.K1l.dot(&rx_matrix(k2lphi)), + K1r: general.K1r.dot(&rx_matrix(k2rphi)), + K2l: ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), + K2r: ry_matrix(k2rtheta).dot(&rx_matrix(k2rlambda)), + default_euler_basis: euler_basis, + ..general + } + } + // :math:`U \sim U_d(\pi/4, \pi/4, \alpha) \sim \text{SWAP} \cdot \text{Ctrl-U}` + // + // This gate binds 4 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` , :math:`K2_r = Ry(\theta_r)\cdot Rz(\lambda_r)` + Specialization::MirrorControlledEquiv => { + let [k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l.view(), EulerBasis::ZYZ); + let [k2rtheta, k2rphi, k2rlambda, k2rphase] = + angles_from_unitary(general.K2r.view(), EulerBasis::ZYZ); + TwoQubitWeylDecomposition { + specialization, + a: PI4, + b: PI4, + c, + global_phase: global_phase + k2lphase + k2rphase, + K1l: general.K1l.dot(&rz_matrix(k2rphi)), + K1r: general.K1r.dot(&rz_matrix(k2lphi)), + K2l: ry_matrix(k2ltheta).dot(&rz_matrix(k2llambda)), + K2r: ry_matrix(k2rtheta).dot(&rz_matrix(k2rlambda)), + ..general + } + } + // :math:`U \sim U_d(\alpha, \alpha, \beta), \alpha \geq |\beta|` + // + // This gate binds 5 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)`. + Specialization::fSimaabEquiv => { + let [k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l.view(), EulerBasis::ZYZ); + TwoQubitWeylDecomposition { + specialization, + a: (a + b) / 2., + b: (a + b) / 2., + c, + global_phase: global_phase + k2lphase, + K1r: general.K1r.dot(&rz_matrix(k2lphi)), + K1l: general.K1l.dot(&rz_matrix(k2lphi)), + K2l: ry_matrix(k2ltheta).dot(&rz_matrix(k2llambda)), + K2r: rz_matrix(-k2lphi).dot(&general.K2r), + ..general + } + } + // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` + // + // This gate binds 5 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` + Specialization::fSimabbEquiv => { + let euler_basis = EulerBasis::XYX; + let [k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l.view(), euler_basis); + TwoQubitWeylDecomposition { + specialization, + a, + b: (b + c) / 2., + c: (b + c) / 2., + global_phase: global_phase + k2lphase, + K1r: general.K1r.dot(&rx_matrix(k2lphi)), + K1l: general.K1l.dot(&rx_matrix(k2lphi)), + K2l: ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), + K2r: rx_matrix(-k2lphi).dot(&general.K2r), + default_euler_basis: euler_basis, + ..general + } + } + // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` + // + // This gate binds 5 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` + Specialization::fSimabmbEquiv => { + let euler_basis = EulerBasis::XYX; + let [k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l.view(), euler_basis); + TwoQubitWeylDecomposition { + specialization, + a, + b: (b - c) / 2., + c: -((b - c) / 2.), + global_phase: global_phase + k2lphase, + K1l: general.K1l.dot(&rx_matrix(k2lphi)), + K1r: general.K1r.dot(&ipz).dot(&rx_matrix(k2lphi)).dot(&ipz), + K2l: ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), + K2r: ipz.dot(&rx_matrix(-k2lphi)).dot(&ipz).dot(&general.K2r), + default_euler_basis: euler_basis, + ..general + } + } + // U has no special symmetry. + // + // This gate binds all 6 possible parameters, so there is no need to make the single-qubit + // pre-/post-gates canonical. + Specialization::General => general, + }; + + let tr = if flipped_from_original { + let [da, db, dc] = [ + PI2 - a - specialized.a, + b - specialized.b, + -c - specialized.c, + ]; + 4. * c64( + da.cos() * db.cos() * dc.cos(), + da.sin() * db.sin() * dc.sin(), + ) + } else { + let [da, db, dc] = [a - specialized.a, b - specialized.b, c - specialized.c]; + 4. * c64( + da.cos() * db.cos() * dc.cos(), + da.sin() * db.sin() * dc.sin(), + ) + }; + specialized.calculated_fidelity = tr.trace_to_fid(); + if let Some(fid) = specialized.requested_fidelity { + if specialized.calculated_fidelity + 1.0e-13 < fid { + return Err(QiskitError::new_err(format!( + "Specialization: {:?} calculated fidelity: {} is worse than requested fidelity: {}", + specialized.specialization, + specialized.calculated_fidelity, + fid + ))); + } + } + specialized.global_phase += tr.arg(); + Ok(specialized) + }; + void twoQubitDecompose(dd::TwoQubitGateMatrix unitaryMatrix) { + qc::fp basis_fidelity = 1.0; + let target_decomposed = TwoQubitWeylDecomposition::new_inner( + unitary, Some(DEFAULT_FIDELITY), None) + ? ; + let traces = self.traces(&target_decomposed); + let best_nbasis = _num_basis_uses.unwrap_or_else( + || + {traces.into_iter() + .enumerate() + .map(| (idx, trace) | + (idx, trace.trace_to_fid() * basis_fidelity.powi(idx as i32))) + .min_by(| (_idx1, fid1), + (_idx2, fid2) | fid2.partial_cmp(fid1).unwrap()) + .unwrap() .0 as u8}); + let decomposition = match best_nbasis{ + 0 = > decomp0_inner(&target_decomposed), + 1 = > self.decomp1_inner(&target_decomposed), + 2 = > self.decomp2_supercontrolled_inner(&target_decomposed), + 3 = > self.decomp3_supercontrolled_inner(&target_decomposed), + _ = > unreachable !("Invalid basis to use"), + }; + let pulse_optimize = self.pulse_optimize.unwrap_or(true); + let sequence = if pulse_optimize { + self.pulse_optimal_chooser(best_nbasis, &decomposition, + &target_decomposed) + ? + } + else {None}; + if let + Some(seq) = sequence { return Ok(seq); } + let mut target_1q_basis_list = EulerBasisSet::new (); + target_1q_basis_list.add_basis(self.euler_basis); + let euler_decompositions + : SmallVec<[Option; 8]> = + decomposition.iter() + .map(| decomp | + {unitary_to_gate_sequence_inner(decomp.view(), + &target_1q_basis_list, 0, + None, true, None, )}) + .collect(); + let mut gates = Vec::with_capacity(TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY); + let mut global_phase = target_decomposed.global_phase; + global_phase -= best_nbasis as f64 * self.basis_decomposer.global_phase; + if best_nbasis + == 2 { global_phase += PI; } + for + i in 0..best_nbasis as usize { + if let + Some(euler_decomp) = &euler_decompositions[2 * i] { + for + gate in& euler_decomp.gates { + gates.push((gate.0.into(), gate.1.clone(), smallvec ![0])); + } + global_phase += euler_decomp.global_phase + } + if let + Some(euler_decomp) = &euler_decompositions[2 * i + 1] { + for + gate in& euler_decomp.gates { + gates.push((gate.0.into(), gate.1.clone(), smallvec ![1])); + } + global_phase += euler_decomp.global_phase + } + gates.push( + (self.gate.clone(), self.gate_params.clone(), smallvec ![ 0, 1 ])); + } + if let + Some(euler_decomp) = &euler_decompositions[2 * best_nbasis as usize] { + for + gate in& euler_decomp.gates { + gates.push((gate.0.into(), gate.1.clone(), smallvec ![0])); + } + global_phase += euler_decomp.global_phase + } + if let + Some(euler_decomp) = &euler_decompositions[2 * best_nbasis as usize + 1] { + for + gate in& euler_decomp.gates { + gates.push((gate.0.into(), gate.1.clone(), smallvec ![1])); + } + global_phase += euler_decomp.global_phase + } + Ok(TwoQubitGateSequence{ + gates, + global_phase, + }) + } + std::array, 4> traces(TwoQubitWeylDecomposition target) { + return { + 4. * std::complex( + target.a.cos() * target.b.cos() * target.c.cos(), + target.a.sin() * target.b.sin() * target.c.sin(), ), + 4. * + c64((PI4 - target.a).cos() * + (self.basis_decomposer.b - target.b).cos() * target.c.cos(), + (PI4 - target.a).sin() * + (self.basis_decomposer.b - target.b).sin() * + target.c.sin(), ), + c64(4. * target.c.cos(), 0.), + c64(4., 0.), + }; } }; From 5e806f668b930046ab88a099bd77093d51015198 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Sep 2025 11:02:50 +0000 Subject: [PATCH 003/237] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MQTOpt/Transforms/GateDecomposition.cpp | 3 +- .../Transforms/GateDecompositionPattern.cpp | 945 +++++++++--------- 2 files changed, 474 insertions(+), 474 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp index 8ed6be639f..a612aca957 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp @@ -23,7 +23,8 @@ namespace mqt::ir::opt { /** * @brief This pass attempts to cancel consecutive self-inverse operations. */ -struct GateDecomposition final : impl::GateDecompositionBase { +struct GateDecomposition final + : impl::GateDecompositionBase { void runOnOperation() override { // Get the current operation being operated on. diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 3c9af492ea..216edcf7b9 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -128,536 +128,535 @@ struct GateDecompositionPattern final std::array, 4> K1r; std::array, 4> K2r; Specialization specialization; - //EulerBasis default_euler_basis; // TODO: simply use ZYZ? + // EulerBasis default_euler_basis; // TODO: simply use ZYZ? std::optional requested_fidelity; qc::fp calculated_fidelity; dd::TwoQubitGateMatrix unitary_matrix; - TwoQubitWeylDecomposition new_inner( - std::array, 4> unitary_matrix, + TwoQubitWeylDecomposition + new_inner(std::array, 4> unitary_matrix, - std::optional fidelity, - std::optional _specialization - ) { - constexpr std::array, 4> IPZ = [[IM, C_ZERO], [C_ZERO, M_IM]]; - constexpr std::array, 4> IPY =[[C_ZERO, C_ONE], [C_M_ONE, C_ZERO]]; - constexpr std::array, 4> IPX = [[C_ZERO, IM], [IM, C_ZERO]]; + std::optional fidelity, + std::optional _specialization) { + constexpr std::array, 4> IPZ = + [[IM, C_ZERO], [C_ZERO, M_IM]]; + constexpr std::array, 4> IPY = + [[C_ZERO, C_ONE], [C_M_ONE, C_ZERO]]; + constexpr std::array, 4> IPX = + [[C_ZERO, IM], [IM, C_ZERO]]; + auto u = unitary_matrix; + let det_u = u.view().into_faer().determinant(); + let det_pow = det_u.powf(-0.25); + u.mapv_inplace(| x | x * det_pow); + let mut global_phase = det_u.arg() / 4.; + let u_p = magic_basis_transform(u.view(), MagicBasisTransform::OutOf); + let m2 = u_p.t().dot(&u_p); + let default_euler_basis = EulerBasis::ZYZ; - auto u = unitary_matrix; - let det_u = u.view().into_faer().determinant(); - let det_pow = det_u.powf(-0.25); - u.mapv_inplace(|x| x * det_pow); - let mut global_phase = det_u.arg() / 4.; - let u_p = magic_basis_transform(u.view(), MagicBasisTransform::OutOf); - let m2 = u_p.t().dot(&u_p); - let default_euler_basis = EulerBasis::ZYZ; - - // M2 is a symmetric complex matrix. We need to decompose it as M2 = P D P^T where - // P ∈ SO(4), D is diagonal with unit-magnitude elements. - // - // We can't use raw `eig` directly because it isn't guaranteed to give us real or orthogonal - // eigenvectors. Instead, since `M2` is complex-symmetric, - // M2 = A + iB - // for real-symmetric `A` and `B`, and as - // M2^+ @ M2 = A^2 + B^2 + i [A, B] = 1 - // we must have `A` and `B` commute, and consequently they are simultaneously diagonalizable. - // Mixing them together _should_ account for any degeneracy problems, but it's not - // guaranteed, so we repeat it a little bit. The fixed seed is to make failures - // deterministic; the value is not important. - let mut state = Pcg64Mcg::seed_from_u64(2023); - let mut found = false; - let mut d: Array1 = Array1::zeros(0); - let mut p: Array2 = Array2::zeros((0, 0)); - for i in 0..100 { - let rand_a: f64; - let rand_b: f64; + // M2 is a symmetric complex matrix. We need to decompose it as M2 = P D + // P^T where P ∈ SO(4), D is diagonal with unit-magnitude elements. + // + // We can't use raw `eig` directly because it isn't guaranteed to give us + // real or orthogonal eigenvectors. Instead, since `M2` is + // complex-symmetric, + // M2 = A + iB + // for real-symmetric `A` and `B`, and as + // M2^+ @ M2 = A^2 + B^2 + i [A, B] = 1 + // we must have `A` and `B` commute, and consequently they are + // simultaneously diagonalizable. Mixing them together _should_ account + // for any degeneracy problems, but it's not guaranteed, so we repeat it a + // little bit. The fixed seed is to make failures deterministic; the + // value is not important. + let mut state = Pcg64Mcg::seed_from_u64(2023); + let mut found = false; + let mut d : Array1 = Array1::zeros(0); + let mut p : Array2 = Array2::zeros((0, 0)); + for + i in 0..100 { + let rand_a : f64; + let rand_b : f64; // For debugging the algorithm use the same RNG values from the // previous Python implementation for the first random trial. // In most cases this loop only executes a single iteration and // using the same rng values rules out possible RNG differences // as the root cause of a test failure - if i == 0 { + if i + == 0 { rand_a = 1.2602066112249388; rand_b = 0.22317849046722027; - } else { - rand_a = state.sample(StandardNormal); - rand_b = state.sample(StandardNormal); + } + else { + rand_a = state.sample(StandardNormal); + rand_b = state.sample(StandardNormal); } - let m2_real = m2.mapv(|val| rand_a * val.re + rand_b * val.im); - let p_inner = m2_real - .view() - .into_faer() - .self_adjoint_eigen(Lower) - .map_err(|e| QiskitError::new_err(format!("{e:?}")))? - .U() - .into_ndarray() - .mapv(Complex64::from); + let m2_real = m2.mapv(| val | rand_a * val.re + rand_b * val.im); + let p_inner = + m2_real.view().into_faer().self_adjoint_eigen(Lower).map_err( + | e | QiskitError::new_err(format !("{e:?}"))) + ?.U().into_ndarray().mapv(Complex64::from); let d_inner = p_inner.t().dot(&m2).dot(&p_inner).diag().to_owned(); - let mut diag_d: Array2 = Array2::zeros((4, 4)); - diag_d - .diag_mut() - .iter_mut() - .enumerate() - .for_each(|(index, x)| *x = d_inner[index]); + let mut diag_d : Array2 = Array2::zeros((4, 4)); + diag_d.diag_mut().iter_mut().enumerate().for_each( + | (index, x) | * x = d_inner[index]); let compare = p_inner.dot(&diag_d).dot(&p_inner.t()); - found = abs_diff_eq!(compare.view(), m2, epsilon = 1.0e-13); + found = abs_diff_eq !(compare.view(), m2, epsilon = 1.0e-13); if found { - p = p_inner; - d = d_inner; - break; + p = p_inner; + d = d_inner; + break; } - } + } if !found { - return Err(QiskitError::new_err(format!( - "TwoQubitWeylDecomposition: failed to diagonalize M2. Please report this at https://github.com/Qiskit/qiskit-terra/issues/4159. Input: {unitary_matrix:?}" - ))); + return Err(QiskitError::new_err( + format !("TwoQubitWeylDecomposition: failed to diagonalize M2. " + "Please report this at " + "https://github.com/Qiskit/qiskit-terra/issues/4159. " + "Input: {unitary_matrix:?}"))); } - let mut d = -d.map(|x| x.arg() / 2.); + let mut d = -d.map(| x | x.arg() / 2.); d[3] = -d[0] - d[1] - d[2]; - let mut cs: SmallVec<[f64; 3]> = (0..3) - .map(|i| ((d[i] + d[3]) / 2.0).rem_euclid(TWO_PI)) - .collect(); - let cstemp: SmallVec<[f64; 3]> = cs - .iter() - .map(|x| x.rem_euclid(PI2)) - .map(|x| x.min(PI2 - x)) - .collect(); + let mut cs + : SmallVec<[f64; 3]> = + (0..3) + .map(| i | ((d[i] + d[3]) / 2.0).rem_euclid(TWO_PI)) + .collect(); + let cstemp : SmallVec<[f64; 3]> = cs.iter() + .map(| x | x.rem_euclid(PI2)) + .map(| x | x.min(PI2 - x)) + .collect(); let mut order = arg_sort(&cstemp); (order[0], order[1], order[2]) = (order[1], order[2], order[0]); (cs[0], cs[1], cs[2]) = (cs[order[0]], cs[order[1]], cs[order[2]]); (d[0], d[1], d[2]) = (d[order[0]], d[order[1]], d[order[2]]); let mut p_orig = p.clone(); - for (i, item) in order.iter().enumerate().take(3) { - let slice_a = p.slice_mut(s![.., i]); - let slice_b = p_orig.slice_mut(s![.., *item]); - Zip::from(slice_a).and(slice_b).for_each(::std::mem::swap); - } - if p.view().into_faer().determinant().re < 0. { - p.slice_mut(s![.., -1]).mapv_inplace(|x| -x); - } - let mut temp: Array2 = Array2::zeros((4, 4)); - temp.diag_mut() - .iter_mut() - .enumerate() - .for_each(|(index, x)| *x = (IM * d[index]).exp()); - let k1 = magic_basis_transform(u_p.dot(&p).dot(&temp).view(), MagicBasisTransform::Into); + for (i, item) + in order.iter().enumerate().take(3) { + let slice_a = p.slice_mut(s ![.., i ]); + let slice_b = p_orig.slice_mut(s ![.., *item ]); + Zip::from(slice_a).and (slice_b).for_each(::std::mem::swap); + } + if p + .view().into_faer().determinant().re < 0. { + p.slice_mut(s ![.., -1 ]).mapv_inplace(| x | -x); + } + let mut temp : Array2 = Array2::zeros((4, 4)); + temp.diag_mut().iter_mut().enumerate().for_each( + | (index, x) | * x = (IM * d[index]).exp()); + let k1 = magic_basis_transform(u_p.dot(&p).dot(&temp).view(), + MagicBasisTransform::Into); let k2 = magic_basis_transform(p.t(), MagicBasisTransform::Into); - #[allow(non_snake_case)] - let (mut K1l, mut K1r, phase_l) = decompose_two_qubit_product_gate(k1.view())?; - #[allow(non_snake_case)] - let (K2l, mut K2r, phase_r) = decompose_two_qubit_product_gate(k2.view())?; +#[allow(non_snake_case)] + let(mut K1l, mut K1r, phase_l) = + decompose_two_qubit_product_gate(k1.view()) ? ; +#[allow(non_snake_case)] + let(K2l, mut K2r, phase_r) = + decompose_two_qubit_product_gate(k2.view()) ? ; global_phase += phase_l + phase_r; // Flip into Weyl chamber - if cs[0] > PI2 { + if cs + [0] > PI2 { cs[0] -= PI32; K1l = K1l.dot(&ipy); K1r = K1r.dot(&ipy); global_phase += PI2; - } - if cs[1] > PI2 { + } + if cs + [1] > PI2 { cs[1] -= PI32; K1l = K1l.dot(&ipx); K1r = K1r.dot(&ipx); global_phase += PI2; - } + } let mut conjs = 0; - if cs[0] > PI4 { + if cs + [0] > PI4 { cs[0] = PI2 - cs[0]; K1l = K1l.dot(&ipy); K2r = ipy.dot(&K2r); conjs += 1; global_phase -= PI2; - } - if cs[1] > PI4 { + } + if cs + [1] > PI4 { cs[1] = PI2 - cs[1]; K1l = K1l.dot(&ipx); K2r = ipx.dot(&K2r); conjs += 1; global_phase += PI2; - if conjs == 1 { - global_phase -= PI; - } - } - if cs[2] > PI2 { + if conjs + == 1 { global_phase -= PI; } + } + if cs + [2] > PI2 { cs[2] -= PI32; K1l = K1l.dot(&ipz); K1r = K1r.dot(&ipz); global_phase += PI2; - if conjs == 1 { - global_phase -= PI; - } - } - if conjs == 1 { + if conjs + == 1 { global_phase -= PI; } + } + if conjs + == 1 { cs[2] = PI2 - cs[2]; K1l = K1l.dot(&ipz); K2r = ipz.dot(&K2r); global_phase += PI2; - } - if cs[2] > PI4 { + } + if cs + [2] > PI4 { cs[2] -= PI2; K1l = K1l.dot(&ipz); K1r = K1r.dot(&ipz); global_phase -= PI2; - } - let [a, b, c] = [cs[1], cs[0], cs[2]]; - let is_close = |ap: f64, bp: f64, cp: f64| -> bool { - let [da, db, dc] = [a - ap, b - bp, c - cp]; - let tr = 4. - * c64( - da.cos() * db.cos() * dc.cos(), - da.sin() * db.sin() * dc.sin(), - ); - match fidelity { - Some(fid) => tr.trace_to_fid() >= fid, - // Set to false here to default to general specialization in the absence of a - // fidelity and provided specialization. - None => false, - } + } + let[a, b, c] = [ cs[1], cs[0], cs[2] ]; + let is_close = | ap : f64, bp : f64, cp : f64 |->bool { + let[da, db, dc] = [ a - ap, b - bp, c - cp ]; + let tr = 4. * c64(da.cos() * db.cos() * dc.cos(), + da.sin() * db.sin() * dc.sin(), ); + match fidelity { + Some(fid) = > tr.trace_to_fid() >= fid, + // Set to false here to default to general specialization in the + // absence of a fidelity and provided specialization. + None = > false, + } }; let closest_abc = closest_partial_swap(a, b, c); let closest_ab_minus_c = closest_partial_swap(a, b, -c); let mut flipped_from_original = false; - let specialization = match _specialization { - Some(specialization) => specialization, - None => { - if is_close(0., 0., 0.) { - Specialization::IdEquiv - } else if is_close(PI4, PI4, PI4) || is_close(PI4, PI4, -PI4) { - Specialization::SWAPEquiv - } else if is_close(closest_abc, closest_abc, closest_abc) { - Specialization::PartialSWAPEquiv - } else if is_close(closest_ab_minus_c, closest_ab_minus_c, -closest_ab_minus_c) { - Specialization::PartialSWAPFlipEquiv - } else if is_close(a, 0., 0.) { - Specialization::ControlledEquiv - } else if is_close(PI4, PI4, c) { - Specialization::MirrorControlledEquiv - } else if is_close((a + b) / 2., (a + b) / 2., c) { - Specialization::fSimaabEquiv - } else if is_close(a, (b + c) / 2., (b + c) / 2.) { - Specialization::fSimabbEquiv - } else if is_close(a, (b - c) / 2., (c - b) / 2.) { - Specialization::fSimabmbEquiv - } else { - Specialization::General - } - } - }; - let general = TwoQubitWeylDecomposition { - a, - b, - c, - global_phase, - K1l, - K1r, - K2l, - K2r, - specialization: Specialization::General, - default_euler_basis, - requested_fidelity: fidelity, - calculated_fidelity: -1.0, - unitary_matrix, + let specialization = match _specialization{ + Some(specialization) = > specialization, + None =>{ + if is_close (0., 0., 0.){ + Specialization::IdEquiv} else if is_close (PI4, PI4, PI4) || + is_close(PI4, PI4, -PI4){ + Specialization::SWAPEquiv} else if is_close (closest_abc, + closest_abc, + closest_abc){ + Specialization:: + PartialSWAPEquiv} else if is_close (closest_ab_minus_c, + closest_ab_minus_c, + -closest_ab_minus_c){ + Specialization::PartialSWAPFlipEquiv} else if is_close (a, + 0., + 0.){ + Specialization::ControlledEquiv} else if is_close (PI4, PI4, + c){ + Specialization:: + MirrorControlledEquiv} else if is_close ((a + b) / 2., + (a + b) / 2., + c){ + Specialization::fSimaabEquiv} else if is_close (a, + (b + c) / + 2., + (b + c) / + 2.){ + Specialization::fSimabbEquiv} else if is_close (a, + (b - c) / + 2., + (c - b) / + 2.){ + Specialization::fSimabmbEquiv} else { + Specialization::General}}}; + let general = TwoQubitWeylDecomposition{ + a, + b, + c, + global_phase, + K1l, + K1r, + K2l, + K2r, + specialization : Specialization::General, + default_euler_basis, + requested_fidelity : fidelity, + calculated_fidelity : -1.0, + unitary_matrix, }; - let mut specialized: TwoQubitWeylDecomposition = match specialization { - // :math:`U \sim U_d(0,0,0) \sim Id` - // - // This gate binds 0 parameters, we make it canonical by setting - // :math:`K2_l = Id` , :math:`K2_r = Id`. - Specialization::IdEquiv => TwoQubitWeylDecomposition { - specialization, - a: 0., - b: 0., - c: 0., - K1l: general.K1l.dot(&general.K2l), - K1r: general.K1r.dot(&general.K2r), - K2l: Array2::eye(2), - K2r: Array2::eye(2), - ..general - }, - // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, -\pi/4) \sim \text{SWAP}` - // - // This gate binds 0 parameters, we make it canonical by setting - // :math:`K2_l = Id` , :math:`K2_r = Id`. - Specialization::SWAPEquiv => { - if c > 0. { - TwoQubitWeylDecomposition { - specialization, - a: PI4, - b: PI4, - c: PI4, - K1l: general.K1l.dot(&general.K2r), - K1r: general.K1r.dot(&general.K2l), - K2l: Array2::eye(2), - K2r: Array2::eye(2), - ..general - } - } else { - flipped_from_original = true; - TwoQubitWeylDecomposition { - specialization, - a: PI4, - b: PI4, - c: PI4, - global_phase: global_phase + PI2, - K1l: general.K1l.dot(&ipz).dot(&general.K2r), - K1r: general.K1r.dot(&ipz).dot(&general.K2l), - K2l: Array2::eye(2), - K2r: Array2::eye(2), - ..general - } - } - } - // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4) \sim \text{SWAP}^\alpha` - // - // This gate binds 3 parameters, we make it canonical by setting: - // - // :math:`K2_l = Id`. - Specialization::PartialSWAPEquiv => { - let closest = closest_partial_swap(a, b, c); - let mut k2l_dag = general.K2l.t().to_owned(); - k2l_dag.view_mut().mapv_inplace(|x| x.conj()); - TwoQubitWeylDecomposition { - specialization, - a: closest, - b: closest, - c: closest, - K1l: general.K1l.dot(&general.K2l), - K1r: general.K1r.dot(&general.K2l), - K2r: k2l_dag.dot(&general.K2r), - K2l: Array2::eye(2), - ..general - } - } - // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4) \sim \text{SWAP}^\alpha` - // - // (a non-equivalent root of SWAP from the TwoQubitWeylPartialSWAPEquiv - // similar to how :math:`x = (\pm \sqrt(x))^2`) - // - // This gate binds 3 parameters, we make it canonical by setting: - // - // :math:`K2_l = Id` - Specialization::PartialSWAPFlipEquiv => { - let closest = closest_partial_swap(a, b, -c); - let mut k2l_dag = general.K2l.t().to_owned(); - k2l_dag.mapv_inplace(|x| x.conj()); - TwoQubitWeylDecomposition { - specialization, - a: closest, - b: closest, - c: -closest, - K1l: general.K1l.dot(&general.K2l), - K1r: general.K1r.dot(&ipz).dot(&general.K2l).dot(&ipz), - K2r: ipz.dot(&k2l_dag).dot(&ipz).dot(&general.K2r), - K2l: Array2::eye(2), - ..general - } - } - // :math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}` - // - // This gate binds 4 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l) Rx(\lambda_l)` , - // :math:`K2_r = Ry(\theta_r) Rx(\lambda_r)` . - Specialization::ControlledEquiv => { - let euler_basis = EulerBasis::XYX; - let [k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l.view(), euler_basis); - let [k2rtheta, k2rphi, k2rlambda, k2rphase] = - angles_from_unitary(general.K2r.view(), euler_basis); - TwoQubitWeylDecomposition { - specialization, - a, - b: 0., - c: 0., - global_phase: global_phase + k2lphase + k2rphase, - K1l: general.K1l.dot(&rx_matrix(k2lphi)), - K1r: general.K1r.dot(&rx_matrix(k2rphi)), - K2l: ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), - K2r: ry_matrix(k2rtheta).dot(&rx_matrix(k2rlambda)), - default_euler_basis: euler_basis, - ..general - } - } - // :math:`U \sim U_d(\pi/4, \pi/4, \alpha) \sim \text{SWAP} \cdot \text{Ctrl-U}` - // - // This gate binds 4 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` , :math:`K2_r = Ry(\theta_r)\cdot Rz(\lambda_r)` - Specialization::MirrorControlledEquiv => { - let [k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l.view(), EulerBasis::ZYZ); - let [k2rtheta, k2rphi, k2rlambda, k2rphase] = - angles_from_unitary(general.K2r.view(), EulerBasis::ZYZ); - TwoQubitWeylDecomposition { - specialization, - a: PI4, - b: PI4, - c, - global_phase: global_phase + k2lphase + k2rphase, - K1l: general.K1l.dot(&rz_matrix(k2rphi)), - K1r: general.K1r.dot(&rz_matrix(k2lphi)), - K2l: ry_matrix(k2ltheta).dot(&rz_matrix(k2llambda)), - K2r: ry_matrix(k2rtheta).dot(&rz_matrix(k2rlambda)), - ..general - } - } - // :math:`U \sim U_d(\alpha, \alpha, \beta), \alpha \geq |\beta|` - // - // This gate binds 5 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)`. - Specialization::fSimaabEquiv => { - let [k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l.view(), EulerBasis::ZYZ); - TwoQubitWeylDecomposition { + let mut specialized + : TwoQubitWeylDecomposition = match specialization{ + // :math:`U \sim U_d(0,0,0) \sim Id` + // + // This gate binds 0 parameters, we make it canonical by + // setting + // :math:`K2_l = Id` , :math:`K2_r = Id`. + Specialization::IdEquiv = > TwoQubitWeylDecomposition{ specialization, - a: (a + b) / 2., - b: (a + b) / 2., - c, - global_phase: global_phase + k2lphase, - K1r: general.K1r.dot(&rz_matrix(k2lphi)), - K1l: general.K1l.dot(&rz_matrix(k2lphi)), - K2l: ry_matrix(k2ltheta).dot(&rz_matrix(k2llambda)), - K2r: rz_matrix(-k2lphi).dot(&general.K2r), + a : 0., + b : 0., + c : 0., + K1l : general.K1l.dot(&general.K2l), + K1r : general.K1r.dot(&general.K2r), + K2l : Array2::eye(2), + K2r : Array2::eye(2), ..general - } - } - // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` - // - // This gate binds 5 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` - Specialization::fSimabbEquiv => { - let euler_basis = EulerBasis::XYX; - let [k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l.view(), euler_basis); - TwoQubitWeylDecomposition { - specialization, - a, - b: (b + c) / 2., - c: (b + c) / 2., - global_phase: global_phase + k2lphase, - K1r: general.K1r.dot(&rx_matrix(k2lphi)), - K1l: general.K1l.dot(&rx_matrix(k2lphi)), - K2l: ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), - K2r: rx_matrix(-k2lphi).dot(&general.K2r), - default_euler_basis: euler_basis, - ..general - } - } - // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` - // - // This gate binds 5 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` - Specialization::fSimabmbEquiv => { - let euler_basis = EulerBasis::XYX; - let [k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l.view(), euler_basis); - TwoQubitWeylDecomposition { - specialization, - a, - b: (b - c) / 2., - c: -((b - c) / 2.), - global_phase: global_phase + k2lphase, - K1l: general.K1l.dot(&rx_matrix(k2lphi)), - K1r: general.K1r.dot(&ipz).dot(&rx_matrix(k2lphi)).dot(&ipz), - K2l: ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), - K2r: ipz.dot(&rx_matrix(-k2lphi)).dot(&ipz).dot(&general.K2r), - default_euler_basis: euler_basis, - ..general - } - } - // U has no special symmetry. - // - // This gate binds all 6 possible parameters, so there is no need to make the single-qubit - // pre-/post-gates canonical. - Specialization::General => general, - }; - - let tr = if flipped_from_original { - let [da, db, dc] = [ - PI2 - a - specialized.a, - b - specialized.b, - -c - specialized.c, - ]; - 4. * c64( - da.cos() * db.cos() * dc.cos(), - da.sin() * db.sin() * dc.sin(), - ) - } else { - let [da, db, dc] = [a - specialized.a, b - specialized.b, c - specialized.c]; - 4. * c64( - da.cos() * db.cos() * dc.cos(), - da.sin() * db.sin() * dc.sin(), - ) - }; - specialized.calculated_fidelity = tr.trace_to_fid(); - if let Some(fid) = specialized.requested_fidelity { - if specialized.calculated_fidelity + 1.0e-13 < fid { - return Err(QiskitError::new_err(format!( - "Specialization: {:?} calculated fidelity: {} is worse than requested fidelity: {}", - specialized.specialization, - specialized.calculated_fidelity, - fid - ))); - } + }, + // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, + // -\pi/4) \sim \text{SWAP}` + // + // This gate binds 0 parameters, we make it canonical by + // setting + // :math:`K2_l = Id` , :math:`K2_r = Id`. + Specialization::SWAPEquiv =>{ + if c > 0. {TwoQubitWeylDecomposition{ + specialization, + a : PI4, + b : PI4, + c : PI4, + K1l : general.K1l.dot(&general.K2r), + K1r : general.K1r.dot(&general.K2l), + K2l : Array2::eye(2), + K2r : Array2::eye(2), + ..general + }} else {flipped_from_original = true; + TwoQubitWeylDecomposition { + specialization, + a : PI4, + b : PI4, + c : PI4, + global_phase : global_phase + PI2, + K1l : general.K1l.dot(&ipz).dot(&general.K2r), + K1r : general.K1r.dot(&ipz).dot(&general.K2l), + K2l : Array2::eye(2), + K2r : Array2::eye(2), + ..general } - specialized.global_phase += tr.arg(); - Ok(specialized) - }; - - void twoQubitDecompose(dd::TwoQubitGateMatrix unitaryMatrix) { - qc::fp basis_fidelity = 1.0; - let target_decomposed = TwoQubitWeylDecomposition::new_inner( - unitary, Some(DEFAULT_FIDELITY), None) - ? ; - let traces = self.traces(&target_decomposed); - let best_nbasis = _num_basis_uses.unwrap_or_else( - || - {traces.into_iter() - .enumerate() - .map(| (idx, trace) | - (idx, trace.trace_to_fid() * basis_fidelity.powi(idx as i32))) - .min_by(| (_idx1, fid1), - (_idx2, fid2) | fid2.partial_cmp(fid1).unwrap()) - .unwrap() .0 as u8}); - let decomposition = match best_nbasis{ - 0 = > decomp0_inner(&target_decomposed), - 1 = > self.decomp1_inner(&target_decomposed), - 2 = > self.decomp2_supercontrolled_inner(&target_decomposed), - 3 = > self.decomp3_supercontrolled_inner(&target_decomposed), - _ = > unreachable !("Invalid basis to use"), - }; - let pulse_optimize = self.pulse_optimize.unwrap_or(true); - let sequence = if pulse_optimize { - self.pulse_optimal_chooser(best_nbasis, &decomposition, - &target_decomposed) - ? } - else {None}; - if let - Some(seq) = sequence { return Ok(seq); } - let mut target_1q_basis_list = EulerBasisSet::new (); - target_1q_basis_list.add_basis(self.euler_basis); - let euler_decompositions - : SmallVec<[Option; 8]> = - decomposition.iter() - .map(| decomp | - {unitary_to_gate_sequence_inner(decomp.view(), - &target_1q_basis_list, 0, - None, true, None, )}) - .collect(); - let mut gates = Vec::with_capacity(TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY); - let mut global_phase = target_decomposed.global_phase; - global_phase -= best_nbasis as f64 * self.basis_decomposer.global_phase; - if best_nbasis - == 2 { global_phase += PI; } + } + // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4) \sim + // \text{SWAP}^\alpha` + // + // This gate binds 3 parameters, we make it canonical by setting: + // + // :math:`K2_l = Id`. + Specialization::PartialSWAPEquiv => { + let closest = closest_partial_swap(a, b, c); + let mut k2l_dag = general.K2l.t().to_owned(); + k2l_dag.view_mut().mapv_inplace(| x | x.conj()); + TwoQubitWeylDecomposition { + specialization, a : closest, + b : closest, + c : closest, + K1l : general.K1l.dot(&general.K2l), + K1r : general.K1r.dot(&general.K2l), + K2r : k2l_dag.dot(&general.K2r), + K2l : Array2::eye(2), + ..general + } + } + // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4) \sim + // \text{SWAP}^\alpha` + // + // (a non-equivalent root of SWAP from the TwoQubitWeylPartialSWAPEquiv + // similar to how :math:`x = (\pm \sqrt(x))^2`) + // + // This gate binds 3 parameters, we make it canonical by setting: + // + // :math:`K2_l = Id` + Specialization::PartialSWAPFlipEquiv => { + let closest = closest_partial_swap(a, b, -c); + let mut k2l_dag = general.K2l.t().to_owned(); + k2l_dag.mapv_inplace(| x | x.conj()); + TwoQubitWeylDecomposition { + specialization, + a : closest, + b : closest, + c : -closest, + K1l : general.K1l.dot(&general.K2l), + K1r : general.K1r.dot(&ipz).dot(&general.K2l).dot(&ipz), + K2r : ipz.dot(&k2l_dag).dot(&ipz).dot(&general.K2r), + K2l : Array2::eye(2), + ..general + } + } + // :math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}` + // + // This gate binds 4 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l) Rx(\lambda_l)` , + // :math:`K2_r = Ry(\theta_r) Rx(\lambda_r)` . + Specialization::ControlledEquiv => { + let euler_basis = EulerBasis::XYX; + let[k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l.view(), euler_basis); + let[k2rtheta, k2rphi, k2rlambda, k2rphase] = + angles_from_unitary(general.K2r.view(), euler_basis); + TwoQubitWeylDecomposition { + specialization, a, b : 0., c : 0., + global_phase : global_phase + k2lphase + k2rphase, + K1l : general.K1l.dot(&rx_matrix(k2lphi)), + K1r : general.K1r.dot(&rx_matrix(k2rphi)), + K2l : ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), + K2r : ry_matrix(k2rtheta).dot(&rx_matrix(k2rlambda)), + default_euler_basis : euler_basis, ..general + } + } + // :math:`U \sim U_d(\pi/4, \pi/4, \alpha) \sim \text{SWAP} \cdot + // \text{Ctrl-U}` + // + // This gate binds 4 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` , :math:`K2_r = + // Ry(\theta_r)\cdot Rz(\lambda_r)` + Specialization::MirrorControlledEquiv => { + let[k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l.view(), EulerBasis::ZYZ); + let[k2rtheta, k2rphi, k2rlambda, k2rphase] = + angles_from_unitary(general.K2r.view(), EulerBasis::ZYZ); + TwoQubitWeylDecomposition { + specialization, + a : PI4, + b : PI4, + c, + global_phase : global_phase + k2lphase + k2rphase, + K1l : general.K1l.dot(&rz_matrix(k2rphi)), + K1r : general.K1r.dot(&rz_matrix(k2lphi)), + K2l : ry_matrix(k2ltheta).dot(&rz_matrix(k2llambda)), + K2r : ry_matrix(k2rtheta).dot(&rz_matrix(k2rlambda)), + ..general + } + } + // :math:`U \sim U_d(\alpha, \alpha, \beta), \alpha \geq |\beta|` + // + // This gate binds 5 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)`. + Specialization::fSimaabEquiv => { + let[k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l.view(), EulerBasis::ZYZ); + TwoQubitWeylDecomposition { + specialization, a : (a + b) / 2., b : (a + b) / 2., c, + global_phase : global_phase + k2lphase, + K1r : general.K1r.dot(&rz_matrix(k2lphi)), + K1l : general.K1l.dot(&rz_matrix(k2lphi)), + K2l : ry_matrix(k2ltheta).dot(&rz_matrix(k2llambda)), + K2r : rz_matrix(-k2lphi).dot(&general.K2r), ..general + } + } + // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` + // + // This gate binds 5 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` + Specialization::fSimabbEquiv => { + let euler_basis = EulerBasis::XYX; + let[k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l.view(), euler_basis); + TwoQubitWeylDecomposition { + specialization, a, b : (b + c) / 2., c : (b + c) / 2., + global_phase : global_phase + k2lphase, + K1r : general.K1r.dot(&rx_matrix(k2lphi)), + K1l : general.K1l.dot(&rx_matrix(k2lphi)), + K2l : ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), + K2r : rx_matrix(-k2lphi).dot(&general.K2r), + default_euler_basis : euler_basis, ..general + } + } + // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` + // + // This gate binds 5 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` + Specialization::fSimabmbEquiv => { + let euler_basis = EulerBasis::XYX; + let[k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l.view(), euler_basis); + TwoQubitWeylDecomposition { + specialization, a, b : (b - c) / 2., + c : -((b - c) / 2.), + global_phase : global_phase + k2lphase, + K1l : general.K1l.dot(&rx_matrix(k2lphi)), + K1r : general.K1r.dot(&ipz).dot(&rx_matrix(k2lphi)).dot(&ipz), + K2l : ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), + K2r : ipz.dot(&rx_matrix(-k2lphi)).dot(&ipz).dot(&general.K2r), + default_euler_basis : euler_basis, + ..general + } + } + // U has no special symmetry. + // + // This gate binds all 6 possible parameters, so there is no need to make the + // single-qubit pre-/post-gates canonical. + Specialization::General = > general, +}; + +let tr = if flipped_from_original { + let[da, db, dc] = [ + PI2 - a - specialized.a, + b - specialized.b, + -c - specialized.c, + ]; + 4. * c64(da.cos() * db.cos() * dc.cos(), da.sin() * db.sin() * dc.sin(), ) +} +else { + let[da, db, dc] = [ a - specialized.a, b - specialized.b, c - specialized.c ]; + 4. * c64(da.cos() * db.cos() * dc.cos(), da.sin() * db.sin() * dc.sin(), ) +}; +specialized.calculated_fidelity = tr.trace_to_fid(); +if let + Some(fid) = specialized.requested_fidelity { + if specialized + .calculated_fidelity + 1.0e-13 < fid { + return Err(QiskitError::new_err(format !( + "Specialization: {:?} calculated fidelity: {} is worse than " + "requested fidelity: {}", + specialized.specialization, specialized.calculated_fidelity, fid))); + } + } +specialized.global_phase += tr.arg(); +Ok(specialized) +}; + +void twoQubitDecompose(dd::TwoQubitGateMatrix unitaryMatrix) { + qc::fp basis_fidelity = 1.0; + let target_decomposed = TwoQubitWeylDecomposition::new_inner( + unitary, Some(DEFAULT_FIDELITY), None) + ? ; + let traces = self.traces(&target_decomposed); + let best_nbasis = _num_basis_uses.unwrap_or_else( + || + {traces.into_iter() + .enumerate() + .map(| (idx, trace) | + (idx, trace.trace_to_fid() * basis_fidelity.powi(idx as i32))) + .min_by(| (_idx1, fid1), + (_idx2, fid2) | fid2.partial_cmp(fid1).unwrap()) + .unwrap() .0 as u8}); + let decomposition = match best_nbasis{ + 0 = > decomp0_inner(&target_decomposed), + 1 = > self.decomp1_inner(&target_decomposed), + 2 = > self.decomp2_supercontrolled_inner(&target_decomposed), + 3 = > self.decomp3_supercontrolled_inner(&target_decomposed), + _ = > unreachable !("Invalid basis to use"), + }; + let pulse_optimize = self.pulse_optimize.unwrap_or(true); + let sequence = if pulse_optimize { + self.pulse_optimal_chooser(best_nbasis, &decomposition, &target_decomposed) + ? + } + else {None}; + if let + Some(seq) = sequence { return Ok(seq); } + let mut target_1q_basis_list = EulerBasisSet::new (); + target_1q_basis_list.add_basis(self.euler_basis); + let euler_decompositions + : SmallVec<[Option; 8]> = + decomposition.iter() + .map(| decomp | + {unitary_to_gate_sequence_inner(decomp.view(), + &target_1q_basis_list, 0, + None, true, None, )}) + .collect(); + let mut gates = Vec::with_capacity(TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY); + let mut global_phase = target_decomposed.global_phase; + global_phase -= best_nbasis as f64 * self.basis_decomposer.global_phase; + if best_nbasis + == 2 { global_phase += PI; } for i in 0..best_nbasis as usize { if let @@ -699,24 +698,24 @@ struct GateDecompositionPattern final gates, global_phase, }) - } +} - std::array, 4> traces(TwoQubitWeylDecomposition target) { - return { - 4. * std::complex( - target.a.cos() * target.b.cos() * target.c.cos(), - target.a.sin() * target.b.sin() * target.c.sin(), ), - 4. * - c64((PI4 - target.a).cos() * - (self.basis_decomposer.b - target.b).cos() * target.c.cos(), - (PI4 - target.a).sin() * - (self.basis_decomposer.b - target.b).sin() * - target.c.sin(), ), - c64(4. * target.c.cos(), 0.), - c64(4., 0.), - }; - } -}; +std::array, 4> traces(TwoQubitWeylDecomposition target) { + return { + 4. * std::complex( + target.a.cos() * target.b.cos() * target.c.cos(), + target.a.sin() * target.b.sin() * target.c.sin(), ), + 4. * c64((PI4 - target.a).cos() * + (self.basis_decomposer.b - target.b).cos() * target.c.cos(), + (PI4 - target.a).sin() * + (self.basis_decomposer.b - target.b).sin() * + target.c.sin(), ), + c64(4. * target.c.cos(), 0.), + c64(4., 0.), + }; +} +} +; /** * @brief Populates the given pattern set with patterns for gate From 70082d8d10f4fa8cdb75cb5d03104ff985b307f5 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 6 Oct 2025 22:23:59 +0200 Subject: [PATCH 004/237] snapshot --- .../Transforms/GateDecompositionPattern.cpp | 730 +++++++++++------- 1 file changed, 444 insertions(+), 286 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 216edcf7b9..57d7c52b78 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include #include @@ -118,6 +118,160 @@ struct GateDecompositionPattern final fSimabmbEquiv, }; + enum class MagicBasisTransform { + Into, + OutOf, + }; + + enum class EulerBasis { + U3 = 0, + U321 = 1, + U = 2, + PSX = 3, + U1X = 4, + RR = 5, + ZYZ = 6, + ZXZ = 7, + XZX = 8, + XYX = 9, + ZSXX = 10, + ZSX = 11, + }; + + using qfp = std::complex; + using diagonal4x4 = std::array; + using vector2d = std::vector; + using matrix2x2 = std::array; + using matrix4x4 = std::array; + + static constexpr matrix2x2 identityGate = {1, 0, 0, 1}; + + static qc::fp remEuclid(qc::fp a, qc::fp b) { + auto r = std::fmod(a, b); + return (r < 0.0) ? r + std::abs(b) : r; + } + + static matrix2x2 dot(const matrix2x2& lhs, const matrix2x2& rhs) { + return lhs; + } + static matrix4x4 dot(const matrix4x4& lhs, const matrix4x4& rhs) { + return lhs; + } + + static matrix2x2 transpose(const matrix2x2& x) { return x; } + static matrix4x4 transpose(const matrix4x4& x) { return x; } + + static qfp determinant(const matrix2x2& x) { return 0.0; }; + static qfp determinant(const matrix4x4& x) { return 0.0; }; + + static matrix2x2 multiply(qfp factor, matrix2x2 matrix) { + llvm::transform(matrix, matrix.begin(), + [&](auto&& x) { return factor * x; }); + return matrix; + } + + static matrix4x4 kroneckerProduct(const matrix2x2& lhs, + const matrix2x2& rhs) { + return from(multiply(lhs[0 * 2 + 0], rhs), multiply(lhs[0 * 2 + 1], rhs), + multiply(lhs[1 * 2 + 0], rhs), multiply(lhs[1 * 2 + 1], rhs)); + } + + static matrix4x4 from(const matrix2x2& first_quadrant, + const matrix2x2& second_quadrant, + const matrix2x2& third_quadrant, + const matrix2x2& fourth_quadrant) { + return { + first_quadrant[0 * 2 + 0], first_quadrant[0 * 2 + 1], + second_quadrant[0 * 2 + 0], second_quadrant[0 * 2 + 1], + first_quadrant[1 * 2 + 0], first_quadrant[1 * 2 + 1], + second_quadrant[1 * 2 + 0], second_quadrant[1 * 2 + 1], + third_quadrant[0 * 2 + 0], first_quadrant[0 * 2 + 1], + fourth_quadrant[0 * 2 + 0], fourth_quadrant[0 * 2 + 1], + third_quadrant[1 * 2 + 0], first_quadrant[1 * 2 + 1], + fourth_quadrant[1 * 2 + 0], fourth_quadrant[1 * 2 + 1], + }; + } + + // https://docs.rs/faer/latest/faer/mat/generic/struct.Mat.html#method.self_adjoint_eigen + static matrix4x4 self_adjoint_eigen_lower(const matrix4x4& x) { return x; } + + static std::tuple + decompose_two_qubit_product_gate(matrix4x4 special_unitary) { + // first quadrant + matrix2x2 r = {special_unitary[0 * 4 + 0], special_unitary[0 * 4 + 1], + special_unitary[1 * 4 + 0], special_unitary[1 * 4 + 1]}; + auto det_r = determinant(r); + if (std::abs(det_r) < 0.1) { + // third quadrant + r = {special_unitary[2 * 4 + 0], special_unitary[2 * 4 + 1], + special_unitary[3 * 4 + 0], special_unitary[3 * 4 + 1]}; + det_r = determinant(r); + } + if (std::abs(det_r) < 0.1) { + throw std::runtime_error{ + "decompose_two_qubit_product_gate: unable to decompose: det_r < 0.1"}; + } + llvm::transform(r, r.begin(), + [&](auto&& x) { return x / std::sqrt(det_r); }); + // transpose with complex conjugate of each element + matrix2x2 r_t_conj; + llvm::transform(transpose(r), r_t_conj.begin(), + [](auto&& x) { return std::conj(x); }); + + auto temp = kroneckerProduct(identityGate, r_t_conj); + temp = dot(special_unitary, temp); + + // [[a, b, c, d], + // [e, f, g, h], => [[a, c], + // [i, j, k, l], [i, k]] + // [m, n, o, p]] + matrix2x2 l = {temp[0 * 4 + 0], temp[0 * 4 + 2], temp[2 * 4 + 0], + temp[2 * 4 + 2]}; + auto det_l = determinant(l); + if (std::abs(det_l) < 0.9) { + throw std::runtime_error{ + "decompose_two_qubit_product_gate: unable to decompose: detL < 0.9"}; + } + llvm::transform(l, l.begin(), + [&](auto&& x) { return x / std::sqrt(det_l); }); + auto phase = std::arg(det_l) / 2.; + + return {l, r, phase}; + } + + static diagonal4x4 diagonal(const matrix4x4& matrix) { + return {matrix[0 * 4 + 0], matrix[1 * 4 + 1], matrix[2 * 4 + 2], + matrix[3 * 4 + 3]}; + } + + static matrix4x4 magic_basis_transform(const matrix4x4& unitary, + MagicBasisTransform direction) { + constexpr matrix4x4 B_NON_NORMALIZED = { + C_ONE, IM, C_ZERO, C_ZERO, C_ZERO, C_ZERO, IM, C_ONE, + C_ZERO, C_ZERO, IM, C_M_ONE, C_ONE, M_IM, C_ZERO, C_ZERO, + }; + + constexpr matrix4x4 B_NON_NORMALIZED_DAGGER = { + qfp(0.5, 0.), C_ZERO, C_ZERO, qfp(0.5, 0.), + qfp(0., -0.5), C_ZERO, C_ZERO, qfp(0., 0.5), + C_ZERO, qfp(0., -0.5), qfp(0., -0.5), C_ZERO, + C_ZERO, qfp(0.5, 0.), qfp(-0.5, 0.), C_ZERO, + }; + if (direction == MagicBasisTransform::OutOf) { + return dot(dot(B_NON_NORMALIZED_DAGGER, unitary), B_NON_NORMALIZED); + } + if (direction == MagicBasisTransform::Into) { + return dot(dot(B_NON_NORMALIZED, unitary), B_NON_NORMALIZED_DAGGER); + } + throw std::logic_error{"Unknown MagicBasisTransform direction!"}; + } + + static constexpr std::complex C_ZERO{0., 0.}; + static constexpr std::complex C_ONE{1., 0.}; + static constexpr std::complex C_M_ONE{-1., 0.}; + static constexpr std::complex IM{0., 1.}; + static constexpr std::complex M_IM{0., -1.}; + struct TwoQubitWeylDecomposition { qc::fp a; qc::fp b; @@ -133,26 +287,26 @@ struct GateDecompositionPattern final qc::fp calculated_fidelity; dd::TwoQubitGateMatrix unitary_matrix; - TwoQubitWeylDecomposition - new_inner(std::array, 4> unitary_matrix, + static TwoQubitWeylDecomposition + new_inner(matrix4x4 unitary_matrix, std::optional fidelity, std::optional _specialization) { - constexpr std::array, 4> IPZ = - [[IM, C_ZERO], [C_ZERO, M_IM]]; - constexpr std::array, 4> IPY = - [[C_ZERO, C_ONE], [C_M_ONE, C_ZERO]]; - constexpr std::array, 4> IPX = - [[C_ZERO, IM], [IM, C_ZERO]]; - - auto u = unitary_matrix; - let det_u = u.view().into_faer().determinant(); - let det_pow = det_u.powf(-0.25); - u.mapv_inplace(| x | x * det_pow); - let mut global_phase = det_u.arg() / 4.; - let u_p = magic_basis_transform(u.view(), MagicBasisTransform::OutOf); - let m2 = u_p.t().dot(&u_p); - let default_euler_basis = EulerBasis::ZYZ; + constexpr std::array, 4> IPZ = {IM, C_ZERO, C_ZERO, + M_IM}; + constexpr std::array, 4> IPY = {C_ZERO, C_ONE, + C_M_ONE, C_ZERO}; + constexpr std::array, 4> IPX = {C_ZERO, IM, IM, + C_ZERO}; + + auto& u = unitary_matrix; + auto det_u = determinant(u); + auto det_pow = std::pow(det_u, static_cast(-0.25)); + llvm::transform(u, u.begin(), [&](auto&& x) { return x * det_pow; }); + auto global_phase = std::arg(det_u) / 4.; + auto u_p = magic_basis_transform(u, MagicBasisTransform::OutOf); + auto m2 = dot(transpose(u_p), u_p); + auto default_euler_basis = EulerBasis::ZYZ; // M2 is a symmetric complex matrix. We need to decompose it as M2 = P D // P^T where P ∈ SO(4), D is diagonal with unit-magnitude elements. @@ -168,264 +322,266 @@ struct GateDecompositionPattern final // for any degeneracy problems, but it's not guaranteed, so we repeat it a // little bit. The fixed seed is to make failures deterministic; the // value is not important. - let mut state = Pcg64Mcg::seed_from_u64(2023); - let mut found = false; - let mut d : Array1 = Array1::zeros(0); - let mut p : Array2 = Array2::zeros((0, 0)); - for - i in 0..100 { - let rand_a : f64; - let rand_b : f64; - // For debugging the algorithm use the same RNG values from the - // previous Python implementation for the first random trial. - // In most cases this loop only executes a single iteration and - // using the same rng values rules out possible RNG differences - // as the root cause of a test failure - if i - == 0 { - rand_a = 1.2602066112249388; - rand_b = 0.22317849046722027; - } - else { - rand_a = state.sample(StandardNormal); - rand_b = state.sample(StandardNormal); - } - let m2_real = m2.mapv(| val | rand_a * val.re + rand_b * val.im); - let p_inner = - m2_real.view().into_faer().self_adjoint_eigen(Lower).map_err( - | e | QiskitError::new_err(format !("{e:?}"))) - ?.U().into_ndarray().mapv(Complex64::from); - let d_inner = p_inner.t().dot(&m2).dot(&p_inner).diag().to_owned(); - let mut diag_d : Array2 = Array2::zeros((4, 4)); - diag_d.diag_mut().iter_mut().enumerate().for_each( - | (index, x) | * x = d_inner[index]); - - let compare = p_inner.dot(&diag_d).dot(&p_inner.t()); - found = abs_diff_eq !(compare.view(), m2, epsilon = 1.0e-13); - if found { - p = p_inner; - d = d_inner; - break; - } - } - if !found { - return Err(QiskitError::new_err( - format !("TwoQubitWeylDecomposition: failed to diagonalize M2. " - "Please report this at " - "https://github.com/Qiskit/qiskit-terra/issues/4159. " - "Input: {unitary_matrix:?}"))); + auto state = std::mt19937{2023}; + std::normal_distribution dist; + auto found = false; + diagonal4x4 d; + matrix4x4 p; + for (int i = 0; i < 100; ++i) { + qc::fp rand_a; + qc::fp rand_b; + // For debugging the algorithm use the same RNG values from the + // previous Python implementation for the first random trial. + // In most cases this loop only executes a single iteration and + // using the same rng values rules out possible RNG differences + // as the root cause of a test failure + if (i == 0) { + rand_a = 1.2602066112249388; + rand_b = 0.22317849046722027; + } else { + rand_a = dist(state); + rand_b = dist(state); } - let mut d = -d.map(| x | x.arg() / 2.); - d[3] = -d[0] - d[1] - d[2]; - let mut cs - : SmallVec<[f64; 3]> = - (0..3) - .map(| i | ((d[i] + d[3]) / 2.0).rem_euclid(TWO_PI)) - .collect(); - let cstemp : SmallVec<[f64; 3]> = cs.iter() - .map(| x | x.rem_euclid(PI2)) - .map(| x | x.min(PI2 - x)) - .collect(); - let mut order = arg_sort(&cstemp); - (order[0], order[1], order[2]) = (order[1], order[2], order[0]); - (cs[0], cs[1], cs[2]) = (cs[order[0]], cs[order[1]], cs[order[2]]); - (d[0], d[1], d[2]) = (d[order[0]], d[order[1]], d[order[2]]); - let mut p_orig = p.clone(); - for (i, item) - in order.iter().enumerate().take(3) { - let slice_a = p.slice_mut(s ![.., i ]); - let slice_b = p_orig.slice_mut(s ![.., *item ]); - Zip::from(slice_a).and (slice_b).for_each(::std::mem::swap); - } - if p - .view().into_faer().determinant().re < 0. { - p.slice_mut(s ![.., -1 ]).mapv_inplace(| x | -x); - } - let mut temp : Array2 = Array2::zeros((4, 4)); - temp.diag_mut().iter_mut().enumerate().for_each( - | (index, x) | * x = (IM * d[index]).exp()); - let k1 = magic_basis_transform(u_p.dot(&p).dot(&temp).view(), - MagicBasisTransform::Into); - let k2 = magic_basis_transform(p.t(), MagicBasisTransform::Into); - -#[allow(non_snake_case)] - let(mut K1l, mut K1r, phase_l) = - decompose_two_qubit_product_gate(k1.view()) ? ; -#[allow(non_snake_case)] - let(K2l, mut K2r, phase_r) = - decompose_two_qubit_product_gate(k2.view()) ? ; - global_phase += phase_l + phase_r; - - // Flip into Weyl chamber - if cs - [0] > PI2 { - cs[0] -= PI32; - K1l = K1l.dot(&ipy); - K1r = K1r.dot(&ipy); - global_phase += PI2; - } - if cs - [1] > PI2 { - cs[1] -= PI32; - K1l = K1l.dot(&ipx); - K1r = K1r.dot(&ipx); - global_phase += PI2; - } - let mut conjs = 0; - if cs - [0] > PI4 { - cs[0] = PI2 - cs[0]; - K1l = K1l.dot(&ipy); - K2r = ipy.dot(&K2r); - conjs += 1; - global_phase -= PI2; - } - if cs - [1] > PI4 { - cs[1] = PI2 - cs[1]; - K1l = K1l.dot(&ipx); - K2r = ipx.dot(&K2r); - conjs += 1; - global_phase += PI2; - if conjs - == 1 { global_phase -= PI; } - } - if cs - [2] > PI2 { - cs[2] -= PI32; - K1l = K1l.dot(&ipz); - K1r = K1r.dot(&ipz); - global_phase += PI2; - if conjs - == 1 { global_phase -= PI; } - } - if conjs - == 1 { - cs[2] = PI2 - cs[2]; - K1l = K1l.dot(&ipz); - K2r = ipz.dot(&K2r); - global_phase += PI2; - } - if cs - [2] > PI4 { - cs[2] -= PI2; - K1l = K1l.dot(&ipz); - K1r = K1r.dot(&ipz); - global_phase -= PI2; - } - let[a, b, c] = [ cs[1], cs[0], cs[2] ]; - let is_close = | ap : f64, bp : f64, cp : f64 |->bool { - let[da, db, dc] = [ a - ap, b - bp, c - cp ]; - let tr = 4. * c64(da.cos() * db.cos() * dc.cos(), - da.sin() * db.sin() * dc.sin(), ); - match fidelity { - Some(fid) = > tr.trace_to_fid() >= fid, - // Set to false here to default to general specialization in the - // absence of a fidelity and provided specialization. - None = > false, - } - }; - - let closest_abc = closest_partial_swap(a, b, c); - let closest_ab_minus_c = closest_partial_swap(a, b, -c); - let mut flipped_from_original = false; - let specialization = match _specialization{ - Some(specialization) = > specialization, - None =>{ - if is_close (0., 0., 0.){ - Specialization::IdEquiv} else if is_close (PI4, PI4, PI4) || - is_close(PI4, PI4, -PI4){ - Specialization::SWAPEquiv} else if is_close (closest_abc, - closest_abc, - closest_abc){ - Specialization:: - PartialSWAPEquiv} else if is_close (closest_ab_minus_c, - closest_ab_minus_c, - -closest_ab_minus_c){ - Specialization::PartialSWAPFlipEquiv} else if is_close (a, - 0., - 0.){ - Specialization::ControlledEquiv} else if is_close (PI4, PI4, - c){ - Specialization:: - MirrorControlledEquiv} else if is_close ((a + b) / 2., - (a + b) / 2., - c){ - Specialization::fSimaabEquiv} else if is_close (a, - (b + c) / - 2., - (b + c) / - 2.){ - Specialization::fSimabbEquiv} else if is_close (a, - (b - c) / - 2., - (c - b) / - 2.){ - Specialization::fSimabmbEquiv} else { - Specialization::General}}}; - let general = TwoQubitWeylDecomposition{ - a, - b, - c, - global_phase, - K1l, - K1r, - K2l, - K2r, - specialization : Specialization::General, - default_euler_basis, - requested_fidelity : fidelity, - calculated_fidelity : -1.0, - unitary_matrix, - }; - let mut specialized - : TwoQubitWeylDecomposition = match specialization{ - // :math:`U \sim U_d(0,0,0) \sim Id` - // - // This gate binds 0 parameters, we make it canonical by - // setting - // :math:`K2_l = Id` , :math:`K2_r = Id`. - Specialization::IdEquiv = > TwoQubitWeylDecomposition{ - specialization, - a : 0., - b : 0., - c : 0., - K1l : general.K1l.dot(&general.K2l), - K1r : general.K1r.dot(&general.K2r), - K2l : Array2::eye(2), - K2r : Array2::eye(2), - ..general - }, - // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, - // -\pi/4) \sim \text{SWAP}` - // - // This gate binds 0 parameters, we make it canonical by - // setting - // :math:`K2_l = Id` , :math:`K2_r = Id`. - Specialization::SWAPEquiv =>{ - if c > 0. {TwoQubitWeylDecomposition{ - specialization, - a : PI4, - b : PI4, - c : PI4, - K1l : general.K1l.dot(&general.K2r), - K1r : general.K1r.dot(&general.K2l), - K2l : Array2::eye(2), - K2r : Array2::eye(2), - ..general - }} else {flipped_from_original = true; - TwoQubitWeylDecomposition { - specialization, - a : PI4, - b : PI4, - c : PI4, - global_phase : global_phase + PI2, - K1l : general.K1l.dot(&ipz).dot(&general.K2r), - K1r : general.K1r.dot(&ipz).dot(&general.K2l), - K2l : Array2::eye(2), - K2r : Array2::eye(2), - ..general + matrix4x4 m2_real; + llvm::transform(m2, m2_real.begin(), [&](const qfp& val) { + return rand_a * val.real() + rand_b * val.imag(); + }); + matrix4x4 p_inner = self_adjoint_eigen_lower(m2_real); + auto d_inner = diagonal(dot(dot(transpose(p_inner), m2), p_inner)); + matrix4x4 diag_d{}; // zero initialization + diag_d[0 * 4 + 0] = d_inner[0]; + diag_d[1 * 4 + 1] = d_inner[1]; + diag_d[2 * 4 + 2] = d_inner[2]; + diag_d[3 * 4 + 3] = d_inner[3]; + + auto compare = dot(dot(p_inner, diag_d), transpose(p_inner)); + found = llvm::all_of_zip(compare, m2, [](auto&& a, auto&& b) { + return std::abs(a - b) < 1.0e-13; + }); + if (found) { + p = p_inner; + d = d_inner; + break; + } + } + if (!found) { + throw std::runtime_error{ + "TwoQubitWeylDecomposition: failed to diagonalize M2."}; + } + std::array d_real; + llvm::transform(d, d_real.begin(), + [](auto&& x) { return -std::arg(x) / 2.0; }); + d_real[3] = -d_real[0] - d_real[1] - d_real[2]; + std::array cs; + for (int i = 0; i < cs.size(); ++i) { + assert(i < d_real.size()); + cs[i] = remEuclid((d_real[i] + d_real[3]) / 2.0, qc::PI_2); + } + decltype(cs) cstemp; + llvm::transform(cs, cstemp.begin(), [](auto&& x) { + auto tmp = remEuclid(x, qc::PI_2); + return std::min(tmp, qc::PI_2 - tmp); + }); + std::array order{0, 1, 2}; + llvm::stable_sort(order, + [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); + std::tie(order[0], order[1], order[2]) = {order[1], order[2], order[0]}; + std::tie(cs[0], cs[1], cs[2]) = {cs[order[0]], cs[order[1]], + cs[order[2]]}; + std::tie(d_real[0], d_real[1], d_real[2]) = { + d_real[order[0]], d_real[order[1]], d_real[order[2]]}; + + // swap columns of p according to order + constexpr auto P_ROW_LENGTH = 4; + auto p_orig = p; + for (int i = 0; i < order.size(); ++i) { + for (std::size_t row = 0; row < P_ROW_LENGTH; ++row) { + std::swap(p[row * 3 + i], p_orig[row * 3 + order[i]]); + } + } + + if (determinant(p).real() < 0.0) { + // negate last column + for (int i = 0; i < P_ROW_LENGTH; ++i) { + auto& x = p[i * P_ROW_LENGTH + P_ROW_LENGTH - 1]; + x = -x; + } + } + + matrix4x4 temp{}; + temp[0 * 4 + 0] = std::exp(IM * d_real[0]); + temp[1 * 4 + 1] = std::exp(IM * d_real[1]); + temp[2 * 4 + 2] = std::exp(IM * d_real[2]); + temp[3 * 4 + 3] = std::exp(IM * d_real[3]); + auto k1 = magic_basis_transform(dot(dot(u_p, p), temp), + MagicBasisTransform::Into); + auto k2 = magic_basis_transform(transpose(p), MagicBasisTransform::Into); + + auto [K1l, K1r, phase_l] = decompose_two_qubit_product_gate(k1); + auto [K2l, K2r, phase_r] = decompose_two_qubit_product_gate(k2); + global_phase += phase_l + phase_r; + + // Flip into Weyl chamber + if (cs[0] > qc::PI_2) { + cs[0] -= 3.0 * qc::PI_2; + K1l = dot(K1l, IPY); + K1r = dot(K1r, IPY); + global_phase += qc::PI_2; + } + if (cs[1] > qc::PI_2) { + cs[1] -= 3.0 * qc::PI_2; + K1l = dot(K1l, IPX); + K1r = dot(K1r, IPX); + global_phase += qc::PI_2; + } + auto conjs = 0; + if (cs[0] > qc::PI_4) { + cs[0] = qc::PI_2 - cs[0]; + K1l = dot(K1l, IPY); + K2r = dot(IPY, K2r); + conjs += 1; + global_phase -= qc::PI_2; + } + if (cs[1] > qc::PI_4) { + cs[1] = qc::PI_2 - cs[1]; + K1l = dot(K1l, IPX); + K2r = dot(IPX, K2r); + conjs += 1; + global_phase += qc::PI_2; + if (conjs == 1) { + global_phase -= qc::PI; } + } + if (cs[2] > qc::PI_2) { + cs[2] -= 3.0 * qc::PI_2; + K1l = dot(K1l, IPZ); + K1r = dot(K1r, IPZ); + global_phase += qc::PI_2; + if (conjs == 1) { + global_phase -= qc::PI; + } + } + if (conjs == 1) { + cs[2] = qc::PI_2 - cs[2]; + K1l = dot(K1l, IPZ); + K2r = dot(IPZ, K2r); + global_phase += qc::PI_2; + } + if (cs[2] > qc::PI_4) { + cs[2] -= qc::PI_2; + K1l = dot(K1l, IPZ); + K1r = dot(K1r, IPZ); + global_phase -= qc::PI_2; + } + auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); + let is_close = | ap : f64, bp : f64, cp : f64 |->bool { + let[da, db, dc] = [ a - ap, b - bp, c - cp ]; + let tr = 4. * c64(da.cos() * db.cos() * dc.cos(), + da.sin() * db.sin() * dc.sin(), ); + match fidelity { + Some(fid) = > tr.trace_to_fid() >= fid, + // Set to false here to default to general specialization in the + // absence of a fidelity and provided specialization. + None = > false, + } + }; + + let closest_abc = closest_partial_swap(a, b, c); + let closest_ab_minus_c = closest_partial_swap(a, b, -c); + auto flipped_from_original = false; + let specialization = match _specialization{ + Some(specialization) = > specialization, + None =>{ + if is_close (0., 0., 0.){ + Specialization::IdEquiv} else if is_close (qc::PI_4, qc::PI_4, + qc::PI_4) || + is_close(qc::PI_4, qc::PI_4, -qc::PI_4){ + Specialization::SWAPEquiv} else if is_close (closest_abc, + closest_abc, + closest_abc){ + Specialization:: + PartialSWAPEquiv} else if is_close (closest_ab_minus_c, + closest_ab_minus_c, + -closest_ab_minus_c){ + Specialization::PartialSWAPFlipEquiv} else if is_close (a, 0., + 0.){ + Specialization::ControlledEquiv} else if is_close (qc::PI_4, + qc::PI_4, + c){ + Specialization:: + MirrorControlledEquiv} else if is_close ((a + b) / 2., + (a + b) / 2., c){ + Specialization::fSimaabEquiv} else if is_close (a, + (b + c) / 2., + (b + c) / 2.){ + Specialization::fSimabbEquiv} else if is_close (a, + (b - c) / 2., + (c - b) / 2.){ + Specialization::fSimabmbEquiv} else { + Specialization::General}}}; + let general = TwoQubitWeylDecomposition{ + a, + b, + c, + global_phase, + K1l, + K1r, + K2l, + K2r, + specialization : Specialization::General, + default_euler_basis, + requested_fidelity : fidelity, + calculated_fidelity : -1.0, + unitary_matrix, + }; + auto specialized + : TwoQubitWeylDecomposition = match specialization{ + // :math:`U \sim U_d(0,0,0) \sim Id` + // + // This gate binds 0 parameters, we make it canonical by + // setting + // :math:`K2_l = Id` , :math:`K2_r = Id`. + Specialization::IdEquiv = > TwoQubitWeylDecomposition{ + specialization, + a : 0., + b : 0., + c : 0., + K1l : general.K1l.dot(&general.K2l), + K1r : general.K1r.dot(&general.K2r), + K2l : Array2::eye(2), + K2r : Array2::eye(2), + ..general + }, + // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, + // -\pi/4) \sim \text{SWAP}` + // + // This gate binds 0 parameters, we make it canonical by + // setting + // :math:`K2_l = Id` , :math:`K2_r = Id`. + Specialization::SWAPEquiv =>{ + if c > 0. {TwoQubitWeylDecomposition{ + specialization, + a : qc::PI_4, + b : qc::PI_4, + c : qc::PI_4, + K1l : general.K1l.dot(&general.K2r), + K1r : general.K1r.dot(&general.K2l), + K2l : Array2::eye(2), + K2r : Array2::eye(2), + ..general + }} else {flipped_from_original = true; + TwoQubitWeylDecomposition { + specialization, + a : qc::PI_4, + b : qc::PI_4, + c : qc::PI_4, + global_phase : global_phase + qc::PI_2, + K1l : general.K1l.dot(&ipz).dot(&general.K2r), + K1r : general.K1r.dot(&ipz).dot(&general.K2l), + K2l : Array2::eye(2), + K2r : Array2::eye(2), + ..general + } } } // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4) \sim @@ -436,7 +592,7 @@ struct GateDecompositionPattern final // :math:`K2_l = Id`. Specialization::PartialSWAPEquiv => { let closest = closest_partial_swap(a, b, c); - let mut k2l_dag = general.K2l.t().to_owned(); + auto k2l_dag = general.K2l.t().to_owned(); k2l_dag.view_mut().mapv_inplace(| x | x.conj()); TwoQubitWeylDecomposition { specialization, a : closest, @@ -460,7 +616,7 @@ struct GateDecompositionPattern final // :math:`K2_l = Id` Specialization::PartialSWAPFlipEquiv => { let closest = closest_partial_swap(a, b, -c); - let mut k2l_dag = general.K2l.t().to_owned(); + auto k2l_dag = general.K2l.t().to_owned(); k2l_dag.mapv_inplace(| x | x.conj()); TwoQubitWeylDecomposition { specialization, @@ -510,8 +666,8 @@ struct GateDecompositionPattern final angles_from_unitary(general.K2r.view(), EulerBasis::ZYZ); TwoQubitWeylDecomposition { specialization, - a : PI4, - b : PI4, + a : qc::PI_4, + b : qc::PI_4, c, global_phase : global_phase + k2lphase + k2rphase, K1l : general.K1l.dot(&rz_matrix(k2rphi)), @@ -583,11 +739,11 @@ struct GateDecompositionPattern final // This gate binds all 6 possible parameters, so there is no need to make the // single-qubit pre-/post-gates canonical. Specialization::General = > general, -}; +} let tr = if flipped_from_original { let[da, db, dc] = [ - PI2 - a - specialized.a, + qc::PI_2 - a - specialized.a, b - specialized.b, -c - specialized.c, ]; @@ -610,7 +766,9 @@ if let } specialized.global_phase += tr.arg(); Ok(specialized) -}; +} // namespace mqt::ir::opt +} +; void twoQubitDecompose(dd::TwoQubitGateMatrix unitaryMatrix) { qc::fp basis_fidelity = 1.0; @@ -642,7 +800,7 @@ void twoQubitDecompose(dd::TwoQubitGateMatrix unitaryMatrix) { else {None}; if let Some(seq) = sequence { return Ok(seq); } - let mut target_1q_basis_list = EulerBasisSet::new (); + auto target_1q_basis_list = EulerBasisSet::new (); target_1q_basis_list.add_basis(self.euler_basis); let euler_decompositions : SmallVec<[Option; 8]> = @@ -652,8 +810,8 @@ void twoQubitDecompose(dd::TwoQubitGateMatrix unitaryMatrix) { &target_1q_basis_list, 0, None, true, None, )}) .collect(); - let mut gates = Vec::with_capacity(TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY); - let mut global_phase = target_decomposed.global_phase; + auto gates = Vec::with_capacity(TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY); + auto global_phase = target_decomposed.global_phase; global_phase -= best_nbasis as f64 * self.basis_decomposer.global_phase; if best_nbasis == 2 { global_phase += PI; } @@ -705,9 +863,9 @@ std::array, 4> traces(TwoQubitWeylDecomposition target) { 4. * std::complex( target.a.cos() * target.b.cos() * target.c.cos(), target.a.sin() * target.b.sin() * target.c.sin(), ), - 4. * c64((PI4 - target.a).cos() * + 4. * c64((qc::PI_4 - target.a).cos() * (self.basis_decomposer.b - target.b).cos() * target.c.cos(), - (PI4 - target.a).sin() * + (qc::PI_4 - target.a).sin() * (self.basis_decomposer.b - target.b).sin() * target.c.sin(), ), c64(4. * target.c.cos(), 0.), From debe427ed86fd3369b819cceef0b274e8e30380e Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 7 Oct 2025 17:07:31 +0200 Subject: [PATCH 005/237] snapshot --- .../Transforms/GateDecompositionPattern.cpp | 760 ++++++++++-------- 1 file changed, 409 insertions(+), 351 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 57d7c52b78..19996b59af 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -266,6 +266,29 @@ struct GateDecompositionPattern final throw std::logic_error{"Unknown MagicBasisTransform direction!"}; } + static qc::fp trace_to_fid(const qfp& x) { + auto x_abs = std::abs(x); + return (4.0 + x_abs * x_abs) / 20.0; + } + + static qc::fp closest_partial_swap(qc::fp a, qc::fp b, qc::fp c) { + auto m = (a + b + c) / 3.; + auto [am, bm, cm] = std::array{a - m, b - m, c - m}; + auto [ab, bc, ca] = std::array{a - b, b - c, c - a}; + return m + am * bm * cm * (6. + ab * ab + bc * bc + ca * ca) / 18.; + } + + static matrix2x2 rx_matrix(qc::fp theta) { + auto half_theta = theta / 2.; + auto cos = qfp(std::cos(half_theta), 0.); + auto isin = qfp(0., -std::sin(half_theta)); + return {cos, isin, isin, cos}; + } + + static std::array params_xyx_inner(const matrix2x2& matrix) { + + } + static constexpr std::complex C_ZERO{0., 0.}; static constexpr std::complex C_ONE{1., 0.}; static constexpr std::complex C_M_ONE{-1., 0.}; @@ -285,7 +308,7 @@ struct GateDecompositionPattern final // EulerBasis default_euler_basis; // TODO: simply use ZYZ? std::optional requested_fidelity; qc::fp calculated_fidelity; - dd::TwoQubitGateMatrix unitary_matrix; + matrix4x4 unitary_matrix; static TwoQubitWeylDecomposition new_inner(matrix4x4 unitary_matrix, @@ -474,347 +497,382 @@ struct GateDecompositionPattern final global_phase -= qc::PI_2; } auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); - let is_close = | ap : f64, bp : f64, cp : f64 |->bool { - let[da, db, dc] = [ a - ap, b - bp, c - cp ]; - let tr = 4. * c64(da.cos() * db.cos() * dc.cos(), - da.sin() * db.sin() * dc.sin(), ); - match fidelity { - Some(fid) = > tr.trace_to_fid() >= fid, - // Set to false here to default to general specialization in the - // absence of a fidelity and provided specialization. - None = > false, + auto is_close = [&](qc::fp ap, qc::fp bp, qc::fp cp) -> bool { + auto da = a - ap; + auto db = b - bp; + auto dc = c - cp; + auto tr = 4. * qfp(std::cos(da) * std::cos(db) * std::cos(dc), + std::sin(da) * std::sin(db) * std::sin(dc)); + if (fidelity) { + return trace_to_fid(tr) >= *fidelity; } + return false; }; - let closest_abc = closest_partial_swap(a, b, c); - let closest_ab_minus_c = closest_partial_swap(a, b, -c); + auto closest_abc = closest_partial_swap(a, b, c); + auto closest_ab_minus_c = closest_partial_swap(a, b, -c); auto flipped_from_original = false; - let specialization = match _specialization{ - Some(specialization) = > specialization, - None =>{ - if is_close (0., 0., 0.){ - Specialization::IdEquiv} else if is_close (qc::PI_4, qc::PI_4, - qc::PI_4) || - is_close(qc::PI_4, qc::PI_4, -qc::PI_4){ - Specialization::SWAPEquiv} else if is_close (closest_abc, - closest_abc, - closest_abc){ - Specialization:: - PartialSWAPEquiv} else if is_close (closest_ab_minus_c, - closest_ab_minus_c, - -closest_ab_minus_c){ - Specialization::PartialSWAPFlipEquiv} else if is_close (a, 0., - 0.){ - Specialization::ControlledEquiv} else if is_close (qc::PI_4, - qc::PI_4, - c){ - Specialization:: - MirrorControlledEquiv} else if is_close ((a + b) / 2., - (a + b) / 2., c){ - Specialization::fSimaabEquiv} else if is_close (a, - (b + c) / 2., - (b + c) / 2.){ - Specialization::fSimabbEquiv} else if is_close (a, - (b - c) / 2., - (c - b) / 2.){ - Specialization::fSimabmbEquiv} else { - Specialization::General}}}; - let general = TwoQubitWeylDecomposition{ - a, - b, - c, - global_phase, - K1l, - K1r, - K2l, - K2r, - specialization : Specialization::General, - default_euler_basis, - requested_fidelity : fidelity, - calculated_fidelity : -1.0, - unitary_matrix, + + auto get_default_specialzation = [&]() { + if (is_close(0., 0., 0.)) { + return Specialization::IdEquiv; + } else if (is_close(qc::PI_4, qc::PI_4, qc::PI_4) || + is_close(qc::PI_4, qc::PI_4, -qc::PI_4)) { + return Specialization::SWAPEquiv; + } else if (is_close(closest_abc, closest_abc, closest_abc)) { + return Specialization::PartialSWAPEquiv; + } else if (is_close(closest_ab_minus_c, closest_ab_minus_c, + -closest_ab_minus_c)) { + return Specialization::PartialSWAPFlipEquiv; + } else if (is_close(a, 0., 0.)) { + return Specialization::ControlledEquiv; + } else if (is_close(qc::PI_4, qc::PI_4, c)) { + return Specialization::MirrorControlledEquiv; + } else if (is_close((a + b) / 2., (a + b) / 2., c)) { + return Specialization::fSimaabEquiv; + } else if (is_close(a, (b + c) / 2., (b + c) / 2.)) { + return Specialization::fSimabbEquiv; + } else if (is_close(a, (b - c) / 2., (c - b) / 2.)) { + return Specialization::fSimabmbEquiv; + } else { + return Specialization::General; + } }; - auto specialized - : TwoQubitWeylDecomposition = match specialization{ - // :math:`U \sim U_d(0,0,0) \sim Id` - // - // This gate binds 0 parameters, we make it canonical by - // setting - // :math:`K2_l = Id` , :math:`K2_r = Id`. - Specialization::IdEquiv = > TwoQubitWeylDecomposition{ - specialization, - a : 0., - b : 0., - c : 0., - K1l : general.K1l.dot(&general.K2l), - K1r : general.K1r.dot(&general.K2r), - K2l : Array2::eye(2), - K2r : Array2::eye(2), - ..general - }, - // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, - // -\pi/4) \sim \text{SWAP}` - // - // This gate binds 0 parameters, we make it canonical by - // setting - // :math:`K2_l = Id` , :math:`K2_r = Id`. - Specialization::SWAPEquiv =>{ - if c > 0. {TwoQubitWeylDecomposition{ - specialization, - a : qc::PI_4, - b : qc::PI_4, - c : qc::PI_4, - K1l : general.K1l.dot(&general.K2r), - K1r : general.K1r.dot(&general.K2l), - K2l : Array2::eye(2), - K2r : Array2::eye(2), - ..general - }} else {flipped_from_original = true; - TwoQubitWeylDecomposition { - specialization, - a : qc::PI_4, - b : qc::PI_4, - c : qc::PI_4, - global_phase : global_phase + qc::PI_2, - K1l : general.K1l.dot(&ipz).dot(&general.K2r), - K1r : general.K1r.dot(&ipz).dot(&general.K2l), - K2l : Array2::eye(2), - K2r : Array2::eye(2), - ..general - } - } - } - // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4) \sim - // \text{SWAP}^\alpha` - // - // This gate binds 3 parameters, we make it canonical by setting: - // - // :math:`K2_l = Id`. - Specialization::PartialSWAPEquiv => { - let closest = closest_partial_swap(a, b, c); - auto k2l_dag = general.K2l.t().to_owned(); - k2l_dag.view_mut().mapv_inplace(| x | x.conj()); - TwoQubitWeylDecomposition { - specialization, a : closest, - b : closest, - c : closest, - K1l : general.K1l.dot(&general.K2l), - K1r : general.K1r.dot(&general.K2l), - K2r : k2l_dag.dot(&general.K2r), - K2l : Array2::eye(2), + auto specialization = + _specialization.value_or(get_default_specialzation()); + TwoQubitWeylDecomposition general{ + a, + b, + c, + global_phase, + K1l, + K1r, + K2l, + K2r, + Specialization::General, + // default_euler_basis, + fidelity, + -1.0, + unitary_matrix, + }; + auto get_specialized_decomposition = [&]() { + // :math:`U \sim U_d(0,0,0) \sim Id` + // + // This gate binds 0 parameters, we make it canonical by + // setting + // :math:`K2_l = Id` , :math:`K2_r = Id`. + if (specialization == Specialization::IdEquiv) { + return TwoQubitWeylDecomposition{ + 0., + 0., + 0., + general.global_phase, + dot(general.K1l, general.K2l), + identityGate, + dot(general.K1r, general.K2r), + identityGate, + specialization, + general.requested_fidelity, + general.calculated_fidelity, + general.unitary_matrix, + }; + } + // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, + // -\pi/4) \sim \text{SWAP}` + // + // This gate binds 0 parameters, we make it canonical by + // setting + // :math:`K2_l = Id` , :math:`K2_r = Id`. + if (specialization == Specialization::SWAPEquiv) { + if (c > 0.) { + return TwoQubitWeylDecomposition{ + qc::PI_4, + qc::PI_4, + qc::PI_4, + general.global_phase, + dot(general.K1l, general.K2r), + identityGate, + dot(general.K1r, general.K2l), + identityGate, + specialization, + general.requested_fidelity, + general.calculated_fidelity, + general.unitary_matrix, + }; + } else { + flipped_from_original = true; + return TwoQubitWeylDecomposition{ + qc::PI_4, + qc::PI_4, + qc::PI_4, + global_phase + qc::PI_2, + dot(dot(general.K1l, IPZ), general.K2r), + identityGate, + dot(dot(general.K1r, IPZ), general.K2l), + identityGate, + specialization, + general.requested_fidelity, + general.calculated_fidelity, + general.unitary_matrix, + }; + } + } + // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4) \sim + // \text{SWAP}^\alpha` + // + // This gate binds 3 parameters, we make it canonical by setting: + // + // :math:`K2_l = Id`. + if (specialization == Specialization::PartialSWAPEquiv) { + auto closest = closest_partial_swap(a, b, c); + auto k2l_dag = transpose(general.K2l); + llvm::transform(k2l_dag, k2l_dag.begin(), + [](auto&& x) { return std::conj(x); }); + + return TwoQubitWeylDecomposition{ + closest, + closest, + closest, + general.global_phase, + dot(general.K1l, general.K2l), + identityGate, + dot(general.K1r, general.K2l), + dot(k2l_dag, general.K2r), + specialization, + general.requested_fidelity, + general.calculated_fidelity, + general.unitary_matrix, + }; + } + // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4) \sim + // \text{SWAP}^\alpha` + // + // (a non-equivalent root of SWAP from the TwoQubitWeylPartialSWAPEquiv + // similar to how :math:`x = (\pm \sqrt(x))^2`) + // + // This gate binds 3 parameters, we make it canonical by setting: + // + // :math:`K2_l = Id` + if (specialization == Specialization::PartialSWAPFlipEquiv) { + auto closest = closest_partial_swap(a, b, -c); + auto k2l_dag = transpose(general.K2l); + llvm::transform(k2l_dag, k2l_dag.begin(), + [](auto&& x) { return std::conj(x); }); + + return TwoQubitWeylDecomposition{ + closest, + closest, + -closest, + general.global_phase, + dot(general.K1l, general.K2l), + identityGate, + dot(dot(dot(general.K1r, IPZ), general.K2l), IPZ), + dot(dot(dot(IPZ, k2l_dag), IPZ), general.K2r), + specialization, + general.requested_fidelity, + general.calculated_fidelity, + general.unitary_matrix, + }; + } + // :math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}` + // + // This gate binds 4 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l) Rx(\lambda_l)` , + // :math:`K2_r = Ry(\theta_r) Rx(\lambda_r)` . + if (specialization == Specialization::ControlledEquiv) { + auto euler_basis = EulerBasis::XYX; + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l, euler_basis); + auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = + angles_from_unitary(general.K2r, euler_basis); + return TwoQubitWeylDecomposition{ + a, + 0., + 0., + global_phase + k2lphase + k2rphase, + dot(general.K1l, rx_matrix(k2lphi)), + ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), + dot(general.K1r, rx_matrix(k2rphi)), + ry_matrix(k2rtheta).dot(&rx_matrix(k2rlambda)), + // default_euler_basis : euler_basis, + specialization, + general.requested_fidelity, + general.calculated_fidelity, + general.unitary_matrix, + }; + } + // :math:`U \sim U_d(\pi/4, \pi/4, \alpha) \sim \text{SWAP} \cdot + // \text{Ctrl-U}` + // + // This gate binds 4 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` , :math:`K2_r = + // Ry(\theta_r)\cdot Rz(\lambda_r)` + Specialization::MirrorControlledEquiv => { + let[k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l.view(), EulerBasis::ZYZ); + let[k2rtheta, k2rphi, k2rlambda, k2rphase] = + angles_from_unitary(general.K2r.view(), EulerBasis::ZYZ); + TwoQubitWeylDecomposition { + specialization, + a : qc::PI_4, + b : qc::PI_4, + c, + global_phase : global_phase + k2lphase + k2rphase, + K1l : general.K1l.dot(&rz_matrix(k2rphi)), + K1r : general.K1r.dot(&rz_matrix(k2lphi)), + K2l : ry_matrix(k2ltheta).dot(&rz_matrix(k2llambda)), + K2r : ry_matrix(k2rtheta).dot(&rz_matrix(k2rlambda)), + ..general + } + } + // :math:`U \sim U_d(\alpha, \alpha, \beta), \alpha \geq |\beta|` + // + // This gate binds 5 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)`. + Specialization::fSimaabEquiv => { + let[k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l.view(), EulerBasis::ZYZ); + TwoQubitWeylDecomposition { + specialization, a : (a + b) / 2., b : (a + b) / 2., c, + global_phase : global_phase + k2lphase, + K1r : general.K1r.dot(&rz_matrix(k2lphi)), + K1l : general.K1l.dot(&rz_matrix(k2lphi)), + K2l : ry_matrix(k2ltheta).dot(&rz_matrix(k2llambda)), + K2r : rz_matrix(-k2lphi).dot(&general.K2r), ..general + } + } + // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` + // + // This gate binds 5 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` + Specialization::fSimabbEquiv => { + let euler_basis = EulerBasis::XYX; + let[k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l.view(), euler_basis); + TwoQubitWeylDecomposition { + specialization, a, b : (b + c) / 2., c : (b + c) / 2., + global_phase : global_phase + k2lphase, + K1r : general.K1r.dot(&rx_matrix(k2lphi)), + K1l : general.K1l.dot(&rx_matrix(k2lphi)), + K2l : ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), + K2r : rx_matrix(-k2lphi).dot(&general.K2r), + default_euler_basis : euler_basis, ..general + } + } + // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` + // + // This gate binds 5 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` + Specialization::fSimabmbEquiv => { + let euler_basis = EulerBasis::XYX; + let[k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l.view(), euler_basis); + TwoQubitWeylDecomposition { + specialization, a, b : (b - c) / 2., + c + : -((b - c) / 2.), + global_phase : global_phase + k2lphase, + K1l : general.K1l.dot(&rx_matrix(k2lphi)), + K1r : general.K1r.dot(&ipz).dot(&rx_matrix(k2lphi)).dot(&ipz), + K2l : ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), + K2r : ipz.dot(&rx_matrix(-k2lphi)) + .dot(&ipz) + .dot(&general.K2r), + default_euler_basis : euler_basis, ..general - } - } - // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4) \sim - // \text{SWAP}^\alpha` - // - // (a non-equivalent root of SWAP from the TwoQubitWeylPartialSWAPEquiv - // similar to how :math:`x = (\pm \sqrt(x))^2`) - // - // This gate binds 3 parameters, we make it canonical by setting: - // - // :math:`K2_l = Id` - Specialization::PartialSWAPFlipEquiv => { - let closest = closest_partial_swap(a, b, -c); - auto k2l_dag = general.K2l.t().to_owned(); - k2l_dag.mapv_inplace(| x | x.conj()); - TwoQubitWeylDecomposition { - specialization, - a : closest, - b : closest, - c : -closest, - K1l : general.K1l.dot(&general.K2l), - K1r : general.K1r.dot(&ipz).dot(&general.K2l).dot(&ipz), - K2r : ipz.dot(&k2l_dag).dot(&ipz).dot(&general.K2r), - K2l : Array2::eye(2), - ..general - } - } - // :math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}` - // - // This gate binds 4 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l) Rx(\lambda_l)` , - // :math:`K2_r = Ry(\theta_r) Rx(\lambda_r)` . - Specialization::ControlledEquiv => { - let euler_basis = EulerBasis::XYX; - let[k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l.view(), euler_basis); - let[k2rtheta, k2rphi, k2rlambda, k2rphase] = - angles_from_unitary(general.K2r.view(), euler_basis); - TwoQubitWeylDecomposition { - specialization, a, b : 0., c : 0., - global_phase : global_phase + k2lphase + k2rphase, - K1l : general.K1l.dot(&rx_matrix(k2lphi)), - K1r : general.K1r.dot(&rx_matrix(k2rphi)), - K2l : ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), - K2r : ry_matrix(k2rtheta).dot(&rx_matrix(k2rlambda)), - default_euler_basis : euler_basis, ..general - } - } - // :math:`U \sim U_d(\pi/4, \pi/4, \alpha) \sim \text{SWAP} \cdot - // \text{Ctrl-U}` - // - // This gate binds 4 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` , :math:`K2_r = - // Ry(\theta_r)\cdot Rz(\lambda_r)` - Specialization::MirrorControlledEquiv => { - let[k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l.view(), EulerBasis::ZYZ); - let[k2rtheta, k2rphi, k2rlambda, k2rphase] = - angles_from_unitary(general.K2r.view(), EulerBasis::ZYZ); - TwoQubitWeylDecomposition { - specialization, - a : qc::PI_4, - b : qc::PI_4, - c, - global_phase : global_phase + k2lphase + k2rphase, - K1l : general.K1l.dot(&rz_matrix(k2rphi)), - K1r : general.K1r.dot(&rz_matrix(k2lphi)), - K2l : ry_matrix(k2ltheta).dot(&rz_matrix(k2llambda)), - K2r : ry_matrix(k2rtheta).dot(&rz_matrix(k2rlambda)), - ..general - } - } - // :math:`U \sim U_d(\alpha, \alpha, \beta), \alpha \geq |\beta|` - // - // This gate binds 5 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)`. - Specialization::fSimaabEquiv => { - let[k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l.view(), EulerBasis::ZYZ); - TwoQubitWeylDecomposition { - specialization, a : (a + b) / 2., b : (a + b) / 2., c, - global_phase : global_phase + k2lphase, - K1r : general.K1r.dot(&rz_matrix(k2lphi)), - K1l : general.K1l.dot(&rz_matrix(k2lphi)), - K2l : ry_matrix(k2ltheta).dot(&rz_matrix(k2llambda)), - K2r : rz_matrix(-k2lphi).dot(&general.K2r), ..general - } - } - // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` - // - // This gate binds 5 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` - Specialization::fSimabbEquiv => { - let euler_basis = EulerBasis::XYX; - let[k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l.view(), euler_basis); - TwoQubitWeylDecomposition { - specialization, a, b : (b + c) / 2., c : (b + c) / 2., - global_phase : global_phase + k2lphase, - K1r : general.K1r.dot(&rx_matrix(k2lphi)), - K1l : general.K1l.dot(&rx_matrix(k2lphi)), - K2l : ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), - K2r : rx_matrix(-k2lphi).dot(&general.K2r), - default_euler_basis : euler_basis, ..general - } - } - // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` - // - // This gate binds 5 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` - Specialization::fSimabmbEquiv => { - let euler_basis = EulerBasis::XYX; - let[k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l.view(), euler_basis); - TwoQubitWeylDecomposition { - specialization, a, b : (b - c) / 2., - c : -((b - c) / 2.), - global_phase : global_phase + k2lphase, - K1l : general.K1l.dot(&rx_matrix(k2lphi)), - K1r : general.K1r.dot(&ipz).dot(&rx_matrix(k2lphi)).dot(&ipz), - K2l : ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), - K2r : ipz.dot(&rx_matrix(-k2lphi)).dot(&ipz).dot(&general.K2r), - default_euler_basis : euler_basis, - ..general - } - } - // U has no special symmetry. - // - // This gate binds all 6 possible parameters, so there is no need to make the - // single-qubit pre-/post-gates canonical. - Specialization::General = > general, -} - -let tr = if flipped_from_original { - let[da, db, dc] = [ - qc::PI_2 - a - specialized.a, - b - specialized.b, - -c - specialized.c, - ]; - 4. * c64(da.cos() * db.cos() * dc.cos(), da.sin() * db.sin() * dc.sin(), ) -} -else { - let[da, db, dc] = [ a - specialized.a, b - specialized.b, c - specialized.c ]; - 4. * c64(da.cos() * db.cos() * dc.cos(), da.sin() * db.sin() * dc.sin(), ) -}; -specialized.calculated_fidelity = tr.trace_to_fid(); -if let - Some(fid) = specialized.requested_fidelity { - if specialized - .calculated_fidelity + 1.0e-13 < fid { - return Err(QiskitError::new_err(format !( - "Specialization: {:?} calculated fidelity: {} is worse than " - "requested fidelity: {}", - specialized.specialization, specialized.calculated_fidelity, fid))); + } + } + // U has no special symmetry. + // + // This gate binds all 6 possible parameters, so there is no need to + // make the single-qubit pre-/post-gates canonical. + Specialization::General = > general, + }; + TwoQubitWeylDecomposition specialized = match specialization + + let tr = if flipped_from_original { + let[da, db, dc] = [ + qc::PI_2 - a - specialized.a, + b - specialized.b, + -c - specialized.c, + ]; + 4. * c64(da.cos() * db.cos() * dc.cos(), + da.sin() * db.sin() * dc.sin(), ) } - } -specialized.global_phase += tr.arg(); -Ok(specialized) -} // namespace mqt::ir::opt -} -; - -void twoQubitDecompose(dd::TwoQubitGateMatrix unitaryMatrix) { - qc::fp basis_fidelity = 1.0; - let target_decomposed = TwoQubitWeylDecomposition::new_inner( - unitary, Some(DEFAULT_FIDELITY), None) - ? ; - let traces = self.traces(&target_decomposed); - let best_nbasis = _num_basis_uses.unwrap_or_else( - || - {traces.into_iter() - .enumerate() - .map(| (idx, trace) | - (idx, trace.trace_to_fid() * basis_fidelity.powi(idx as i32))) - .min_by(| (_idx1, fid1), - (_idx2, fid2) | fid2.partial_cmp(fid1).unwrap()) - .unwrap() .0 as u8}); - let decomposition = match best_nbasis{ - 0 = > decomp0_inner(&target_decomposed), - 1 = > self.decomp1_inner(&target_decomposed), - 2 = > self.decomp2_supercontrolled_inner(&target_decomposed), - 3 = > self.decomp3_supercontrolled_inner(&target_decomposed), - _ = > unreachable !("Invalid basis to use"), + else { + let[da, db, dc] = + [ a - specialized.a, b - specialized.b, c - specialized.c ]; + 4. * c64(da.cos() * db.cos() * dc.cos(), + da.sin() * db.sin() * dc.sin(), ) + }; + specialized.calculated_fidelity = tr.trace_to_fid(); + if let + Some(fid) = specialized.requested_fidelity { + if specialized + .calculated_fidelity + 1.0e-13 < fid { + return Err(QiskitError::new_err(format !( + "Specialization: {:?} calculated fidelity: {} is worse than " + "requested fidelity: {}", + specialized.specialization, specialized.calculated_fidelity, + fid))); + } + } + specialized.global_phase += tr.arg(); + Ok(specialized) + } // namespace mqt::ir::opt }; - let pulse_optimize = self.pulse_optimize.unwrap_or(true); - let sequence = if pulse_optimize { - self.pulse_optimal_chooser(best_nbasis, &decomposition, &target_decomposed) - ? - } - else {None}; - if let - Some(seq) = sequence { return Ok(seq); } - auto target_1q_basis_list = EulerBasisSet::new (); - target_1q_basis_list.add_basis(self.euler_basis); - let euler_decompositions - : SmallVec<[Option; 8]> = - decomposition.iter() - .map(| decomp | - {unitary_to_gate_sequence_inner(decomp.view(), - &target_1q_basis_list, 0, - None, true, None, )}) - .collect(); - auto gates = Vec::with_capacity(TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY); - auto global_phase = target_decomposed.global_phase; - global_phase -= best_nbasis as f64 * self.basis_decomposer.global_phase; - if best_nbasis - == 2 { global_phase += PI; } + + void twoQubitDecompose(dd::TwoQubitGateMatrix unitaryMatrix) { + qc::fp basis_fidelity = 1.0; + let target_decomposed = TwoQubitWeylDecomposition::new_inner( + unitary, Some(DEFAULT_FIDELITY), None) + ? ; + let traces = self.traces(&target_decomposed); + let best_nbasis = _num_basis_uses.unwrap_or_else( + || + {traces.into_iter() + .enumerate() + .map(| (idx, trace) | + (idx, trace.trace_to_fid() * basis_fidelity.powi(idx as i32))) + .min_by(| (_idx1, fid1), + (_idx2, fid2) | fid2.partial_cmp(fid1).unwrap()) + .unwrap() .0 as u8}); + let decomposition = match best_nbasis{ + 0 = > decomp0_inner(&target_decomposed), + 1 = > self.decomp1_inner(&target_decomposed), + 2 = > self.decomp2_supercontrolled_inner(&target_decomposed), + 3 = > self.decomp3_supercontrolled_inner(&target_decomposed), + _ = > unreachable !("Invalid basis to use"), + }; + let pulse_optimize = self.pulse_optimize.unwrap_or(true); + let sequence = if pulse_optimize { + self.pulse_optimal_chooser(best_nbasis, &decomposition, + &target_decomposed) + ? + } + else {None}; + if let + Some(seq) = sequence { return Ok(seq); } + auto target_1q_basis_list = EulerBasisSet::new (); + target_1q_basis_list.add_basis(self.euler_basis); + let euler_decompositions + : SmallVec<[Option; 8]> = + decomposition.iter() + .map(| decomp | + {unitary_to_gate_sequence_inner(decomp.view(), + &target_1q_basis_list, 0, + None, true, None, )}) + .collect(); + auto gates = Vec::with_capacity(TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY); + auto global_phase = target_decomposed.global_phase; + global_phase -= best_nbasis as f64 * self.basis_decomposer.global_phase; + if best_nbasis + == 2 { global_phase += PI; } for i in 0..best_nbasis as usize { if let @@ -856,24 +914,24 @@ void twoQubitDecompose(dd::TwoQubitGateMatrix unitaryMatrix) { gates, global_phase, }) -} + } -std::array, 4> traces(TwoQubitWeylDecomposition target) { - return { - 4. * std::complex( - target.a.cos() * target.b.cos() * target.c.cos(), - target.a.sin() * target.b.sin() * target.c.sin(), ), - 4. * c64((qc::PI_4 - target.a).cos() * - (self.basis_decomposer.b - target.b).cos() * target.c.cos(), - (qc::PI_4 - target.a).sin() * - (self.basis_decomposer.b - target.b).sin() * - target.c.sin(), ), - c64(4. * target.c.cos(), 0.), - c64(4., 0.), - }; -} -} -; + std::array, 4> traces(TwoQubitWeylDecomposition target) { + return { + 4. * std::complex( + target.a.cos() * target.b.cos() * target.c.cos(), + target.a.sin() * target.b.sin() * target.c.sin(), ), + 4. * + c64((qc::PI_4 - target.a).cos() * + (self.basis_decomposer.b - target.b).cos() * target.c.cos(), + (qc::PI_4 - target.a).sin() * + (self.basis_decomposer.b - target.b).sin() * + target.c.sin(), ), + c64(4. * target.c.cos(), 0.), + c64(4., 0.), + }; + } +}; /** * @brief Populates the given pattern set with patterns for gate From b86844b6f2695e59fddc12d80084afd8907f42f1 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 7 Oct 2025 21:34:56 +0200 Subject: [PATCH 006/237] snapshot --- .../Transforms/GateDecompositionPattern.cpp | 411 +++++++++++------- 1 file changed, 252 insertions(+), 159 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 19996b59af..8f4b20412d 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -285,10 +285,28 @@ struct GateDecompositionPattern final return {cos, isin, isin, cos}; } - static std::array params_xyx_inner(const matrix2x2& matrix) { + static matrix2x2 ry_matrix(qc::fp theta) { + auto half_theta = theta / 2.; + auto cos = qfp(std::cos(half_theta), 0.); + auto sin = qfp(std::sin(half_theta), 0.); + return {cos, -sin, sin, cos}; + } + static matrix2x2 rz_matrix(qc::fp theta) { + auto ilam2 = qfp(0., 0.5 * theta); + return {std::exp(-ilam2), C_ZERO, C_ZERO, std::exp(ilam2)}; + } + + static std::array angles_from_unitary(const matrix2x2& matrix, + EulerBasis basis) { + if (basis == EulerBasis::XYX) { + return params_xyx_inner(matrix); + } + throw std::invalid_argument{"Unknown EulerBasis for angles_from_unitary"}; } + static std::array params_xyx_inner(const matrix2x2& matrix) {} + static constexpr std::complex C_ZERO{0., 0.}; static constexpr std::complex C_ONE{1., 0.}; static constexpr std::complex C_M_ONE{-1., 0.}; @@ -305,7 +323,7 @@ struct GateDecompositionPattern final std::array, 4> K1r; std::array, 4> K2r; Specialization specialization; - // EulerBasis default_euler_basis; // TODO: simply use ZYZ? + EulerBasis default_euler_basis; std::optional requested_fidelity; qc::fp calculated_fidelity; matrix4x4 unitary_matrix; @@ -396,7 +414,7 @@ struct GateDecompositionPattern final [](auto&& x) { return -std::arg(x) / 2.0; }); d_real[3] = -d_real[0] - d_real[1] - d_real[2]; std::array cs; - for (int i = 0; i < cs.size(); ++i) { + for (std::size_t i = 0; i < cs.size(); ++i) { assert(i < d_real.size()); cs[i] = remEuclid((d_real[i] + d_real[3]) / 2.0, qc::PI_2); } @@ -417,7 +435,7 @@ struct GateDecompositionPattern final // swap columns of p according to order constexpr auto P_ROW_LENGTH = 4; auto p_orig = p; - for (int i = 0; i < order.size(); ++i) { + for (std::size_t i = 0; i < order.size(); ++i) { for (std::size_t row = 0; row < P_ROW_LENGTH; ++row) { std::swap(p[row * 3 + i], p_orig[row * 3 + order[i]]); } @@ -550,7 +568,7 @@ struct GateDecompositionPattern final K2l, K2r, Specialization::General, - // default_euler_basis, + default_euler_basis, fidelity, -1.0, unitary_matrix, @@ -572,6 +590,7 @@ struct GateDecompositionPattern final dot(general.K1r, general.K2r), identityGate, specialization, + general.default_euler_basis, general.requested_fidelity, general.calculated_fidelity, general.unitary_matrix, @@ -595,6 +614,7 @@ struct GateDecompositionPattern final dot(general.K1r, general.K2l), identityGate, specialization, + general.default_euler_basis, general.requested_fidelity, general.calculated_fidelity, general.unitary_matrix, @@ -611,6 +631,7 @@ struct GateDecompositionPattern final dot(dot(general.K1r, IPZ), general.K2l), identityGate, specialization, + general.default_euler_basis, general.requested_fidelity, general.calculated_fidelity, general.unitary_matrix, @@ -639,6 +660,7 @@ struct GateDecompositionPattern final dot(general.K1r, general.K2l), dot(k2l_dag, general.K2r), specialization, + general.default_euler_basis, general.requested_fidelity, general.calculated_fidelity, general.unitary_matrix, @@ -669,6 +691,7 @@ struct GateDecompositionPattern final dot(dot(dot(general.K1r, IPZ), general.K2l), IPZ), dot(dot(dot(IPZ, k2l_dag), IPZ), general.K2r), specialization, + general.default_euler_basis, general.requested_fidelity, general.calculated_fidelity, general.unitary_matrix, @@ -692,11 +715,11 @@ struct GateDecompositionPattern final 0., global_phase + k2lphase + k2rphase, dot(general.K1l, rx_matrix(k2lphi)), - ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), + dot(ry_matrix(k2ltheta), rx_matrix(k2llambda)), dot(general.K1r, rx_matrix(k2rphi)), - ry_matrix(k2rtheta).dot(&rx_matrix(k2rlambda)), - // default_euler_basis : euler_basis, + dot(ry_matrix(k2rtheta), rx_matrix(k2rlambda)), specialization, + euler_basis, general.requested_fidelity, general.calculated_fidelity, general.unitary_matrix, @@ -709,170 +732,239 @@ struct GateDecompositionPattern final // // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` , :math:`K2_r = // Ry(\theta_r)\cdot Rz(\lambda_r)` - Specialization::MirrorControlledEquiv => { - let[k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l.view(), EulerBasis::ZYZ); - let[k2rtheta, k2rphi, k2rlambda, k2rphase] = - angles_from_unitary(general.K2r.view(), EulerBasis::ZYZ); - TwoQubitWeylDecomposition { - specialization, - a : qc::PI_4, - b : qc::PI_4, - c, - global_phase : global_phase + k2lphase + k2rphase, - K1l : general.K1l.dot(&rz_matrix(k2rphi)), - K1r : general.K1r.dot(&rz_matrix(k2lphi)), - K2l : ry_matrix(k2ltheta).dot(&rz_matrix(k2llambda)), - K2r : ry_matrix(k2rtheta).dot(&rz_matrix(k2rlambda)), - ..general - } + if (specialization == Specialization::MirrorControlledEquiv) { + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l, EulerBasis::ZYZ); + auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = + angles_from_unitary(general.K2r, EulerBasis::ZYZ); + TwoQubitWeylDecomposition{ + qc::PI_4, + qc::PI_4, + c, + global_phase + k2lphase + k2rphase, + dot(general.K1l, rz_matrix(k2rphi)), + dot(ry_matrix(k2ltheta), rz_matrix(k2llambda)), + dot(general.K1r, rz_matrix(k2lphi)), + dot(ry_matrix(k2rtheta), rz_matrix(k2rlambda)), + specialization, + general.default_euler_basis, + general.requested_fidelity, + general.calculated_fidelity, + general.unitary_matrix, + }; } // :math:`U \sim U_d(\alpha, \alpha, \beta), \alpha \geq |\beta|` // // This gate binds 5 parameters, we make it canonical by setting: // // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)`. - Specialization::fSimaabEquiv => { - let[k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l.view(), EulerBasis::ZYZ); - TwoQubitWeylDecomposition { - specialization, a : (a + b) / 2., b : (a + b) / 2., c, - global_phase : global_phase + k2lphase, - K1r : general.K1r.dot(&rz_matrix(k2lphi)), - K1l : general.K1l.dot(&rz_matrix(k2lphi)), - K2l : ry_matrix(k2ltheta).dot(&rz_matrix(k2llambda)), - K2r : rz_matrix(-k2lphi).dot(&general.K2r), ..general - } + if (specialization == Specialization::fSimaabEquiv) { + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l, EulerBasis::ZYZ); + return TwoQubitWeylDecomposition{ + (a + b) / 2., + (a + b) / 2., + c, + global_phase + k2lphase, + dot(general.K1l, rz_matrix(k2lphi)), + dot(ry_matrix(k2ltheta), rz_matrix(k2llambda)), + dot(general.K1r, rz_matrix(k2lphi)), + dot(rz_matrix(-k2lphi), general.K2r), + specialization, + general.default_euler_basis, + general.requested_fidelity, + general.calculated_fidelity, + general.unitary_matrix, + }; } // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` // // This gate binds 5 parameters, we make it canonical by setting: // // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` - Specialization::fSimabbEquiv => { - let euler_basis = EulerBasis::XYX; - let[k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l.view(), euler_basis); - TwoQubitWeylDecomposition { - specialization, a, b : (b + c) / 2., c : (b + c) / 2., - global_phase : global_phase + k2lphase, - K1r : general.K1r.dot(&rx_matrix(k2lphi)), - K1l : general.K1l.dot(&rx_matrix(k2lphi)), - K2l : ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), - K2r : rx_matrix(-k2lphi).dot(&general.K2r), - default_euler_basis : euler_basis, ..general - } + if (specialization == Specialization::fSimabbEquiv) { + auto euler_basis = EulerBasis::XYX; + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l, euler_basis); + return TwoQubitWeylDecomposition{ + a, + (b + c) / 2., + (b + c) / 2., + global_phase + k2lphase, + dot(general.K1l, rx_matrix(k2lphi)), + dot(ry_matrix(k2ltheta), rx_matrix(k2llambda)), + dot(general.K1r, rx_matrix(k2lphi)), + dot(rx_matrix(-k2lphi), general.K2r), + specialization, + euler_basis, + general.requested_fidelity, + general.calculated_fidelity, + general.unitary_matrix, + }; } // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` // // This gate binds 5 parameters, we make it canonical by setting: // // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` - Specialization::fSimabmbEquiv => { - let euler_basis = EulerBasis::XYX; - let[k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l.view(), euler_basis); - TwoQubitWeylDecomposition { - specialization, a, b : (b - c) / 2., - c - : -((b - c) / 2.), - global_phase : global_phase + k2lphase, - K1l : general.K1l.dot(&rx_matrix(k2lphi)), - K1r : general.K1r.dot(&ipz).dot(&rx_matrix(k2lphi)).dot(&ipz), - K2l : ry_matrix(k2ltheta).dot(&rx_matrix(k2llambda)), - K2r : ipz.dot(&rx_matrix(-k2lphi)) - .dot(&ipz) - .dot(&general.K2r), - default_euler_basis : euler_basis, - ..general - } + if (specialization == Specialization::fSimabmbEquiv) { + auto euler_basis = EulerBasis::XYX; + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + angles_from_unitary(general.K2l, euler_basis); + return TwoQubitWeylDecomposition{ + a, + (b - c) / 2., + -((b - c) / 2.), + global_phase + k2lphase, + dot(general.K1l, rx_matrix(k2lphi)), + dot(ry_matrix(k2ltheta), rx_matrix(k2llambda)), + dot(dot(dot(general.K1r, IPZ), rx_matrix(k2lphi)), IPZ), + dot(dot(dot(IPZ, rx_matrix(-k2lphi)), IPZ), general.K2r), + specialization, + euler_basis, + general.requested_fidelity, + general.calculated_fidelity, + general.unitary_matrix, + }; } // U has no special symmetry. // // This gate binds all 6 possible parameters, so there is no need to // make the single-qubit pre-/post-gates canonical. - Specialization::General = > general, + if (specialization == Specialization::General) { + return general; + } }; - TwoQubitWeylDecomposition specialized = match specialization - - let tr = if flipped_from_original { - let[da, db, dc] = [ - qc::PI_2 - a - specialized.a, - b - specialized.b, - -c - specialized.c, - ]; - 4. * c64(da.cos() * db.cos() * dc.cos(), - da.sin() * db.sin() * dc.sin(), ) - } - else { - let[da, db, dc] = - [ a - specialized.a, b - specialized.b, c - specialized.c ]; - 4. * c64(da.cos() * db.cos() * dc.cos(), - da.sin() * db.sin() * dc.sin(), ) + + TwoQubitWeylDecomposition specialized = get_specialized_decomposition(); + + auto get_tr = [&]() { + if (flipped_from_original) { + auto [da, db, dc] = std::array{ + qc::PI_2 - a - specialized.a, + b - specialized.b, + -c - specialized.c, + }; + return 4. * qfp(std::cos(da) * std::cos(db) * std::cos(dc), + std::sin(da) * std::sin(db) * std::sin(dc)); + } else { + auto [da, db, dc] = std::array{a - specialized.a, b - specialized.b, + c - specialized.c}; + return 4. * qfp(std::cos(da) * std::cos(db) * std::cos(dc), + std::sin(da) * std::sin(db) * std::sin(dc)); + } }; - specialized.calculated_fidelity = tr.trace_to_fid(); - if let - Some(fid) = specialized.requested_fidelity { - if specialized - .calculated_fidelity + 1.0e-13 < fid { - return Err(QiskitError::new_err(format !( - "Specialization: {:?} calculated fidelity: {} is worse than " - "requested fidelity: {}", - specialized.specialization, specialized.calculated_fidelity, - fid))); - } + auto tr = get_tr(); + specialized.calculated_fidelity = trace_to_fid(tr); + if (specialized.requested_fidelity) { + if (specialized.calculated_fidelity + 1.0e-13 < + *specialized.requested_fidelity) { + throw std::runtime_error{ + "Specialization: {:?} calculated fidelity: {} is worse than " + "requested fidelity: {}", + }; } - specialized.global_phase += tr.arg(); - Ok(specialized) + } + specialized.global_phase += std::arg(tr); + return specialized; } // namespace mqt::ir::opt }; - void twoQubitDecompose(dd::TwoQubitGateMatrix unitaryMatrix) { - qc::fp basis_fidelity = 1.0; - let target_decomposed = TwoQubitWeylDecomposition::new_inner( - unitary, Some(DEFAULT_FIDELITY), None) - ? ; - let traces = self.traces(&target_decomposed); - let best_nbasis = _num_basis_uses.unwrap_or_else( - || - {traces.into_iter() - .enumerate() - .map(| (idx, trace) | - (idx, trace.trace_to_fid() * basis_fidelity.powi(idx as i32))) - .min_by(| (_idx1, fid1), - (_idx2, fid2) | fid2.partial_cmp(fid1).unwrap()) - .unwrap() .0 as u8}); - let decomposition = match best_nbasis{ - 0 = > decomp0_inner(&target_decomposed), - 1 = > self.decomp1_inner(&target_decomposed), - 2 = > self.decomp2_supercontrolled_inner(&target_decomposed), - 3 = > self.decomp3_supercontrolled_inner(&target_decomposed), - _ = > unreachable !("Invalid basis to use"), - }; - let pulse_optimize = self.pulse_optimize.unwrap_or(true); - let sequence = if pulse_optimize { - self.pulse_optimal_chooser(best_nbasis, &decomposition, - &target_decomposed) - ? - } - else {None}; - if let - Some(seq) = sequence { return Ok(seq); } - auto target_1q_basis_list = EulerBasisSet::new (); - target_1q_basis_list.add_basis(self.euler_basis); - let euler_decompositions - : SmallVec<[Option; 8]> = - decomposition.iter() - .map(| decomp | - {unitary_to_gate_sequence_inner(decomp.view(), - &target_1q_basis_list, 0, - None, true, None, )}) - .collect(); - auto gates = Vec::with_capacity(TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY); - auto global_phase = target_decomposed.global_phase; - global_phase -= best_nbasis as f64 * self.basis_decomposer.global_phase; - if best_nbasis - == 2 { global_phase += PI; } + static constexpr auto DEFAULT_FIDELITY = 1.0 - 1.0e-9; + + struct TwoQubitBasisDecomposer { + std::string gate; + qc::fp basis_fidelity; + EulerBasis euler_basis; + std::optional pulse_optimize; + TwoQubitWeylDecomposition basis_decomposer; + bool super_controlled; + matrix2x2 u0l; + matrix2x2 u0r; + matrix2x2 u1l; + matrix2x2 u1ra; + matrix2x2 u1rb; + matrix2x2 u2la; + matrix2x2 u2lb; + matrix2x2 u2ra; + matrix2x2 u2rb; + matrix2x2 u3l; + matrix2x2 u3r; + matrix2x2 q0l; + matrix2x2 q0r; + matrix2x2 q1la; + matrix2x2 q1lb; + matrix2x2 q1ra; + matrix2x2 q1rb; + matrix2x2 q2l; + matrix2x2 q2r; + + void twoQubitDecompose(const matrix4x4& unitaryMatrix, + std::optional _basis_fidelity, + bool approximate, + std::optional _num_basis_uses) { + auto get_basis_fidelity = [&]() { + if (approximate) { + return _basis_fidelity.value_or(this->basis_fidelity); + } + return 1.0; + }; + qc::fp basis_fidelity = get_basis_fidelity(); + auto target_decomposed = TwoQubitWeylDecomposition::new_inner( + unitaryMatrix, DEFAULT_FIDELITY, std::nullopt); + auto traces = this->traces(target_decomposed); + auto get_default_nbasis = [&]() { + auto minValue = std::numeric_limits::max(); + auto minIndex = -1; + for(std::size_t i = 0; i < traces.size(); ++i) { + auto value = trace_to_fid(traces[i]) * std::pow(basis_fidelity, i); + if (value < minValue) { + minIndex = i; + minValue = value; + } + } + return minIndex; + }; + auto best_nbasis = _num_basis_uses.value_or(get_default_nbasis()); + auto choose_decomposition = [&]() { + if (best_nbasis == 0) { + return decomp0_inner(&target_decomposed); + } + if (best_nbasis == 1) { + return decomp1_inner(&target_decomposed); + } + if (best_nbasis == 2) { + return decomp2_supercontrolled_inner(&target_decomposed); + } + if (best_nbasis == 3) { + return decomp3_supercontrolled_inner(&target_decomposed); + } + throw std::logic_error{"Invalid basis to use"}; + }; + auto pulse_optimize = this->pulse_optimize.unwrap_or(true); + auto sequence = if (pulse_optimize) { + self.pulse_optimal_chooser(best_nbasis, &decomposition, + &target_decomposed) + ? + } + else {None}; + if let + Some(seq) = sequence { return Ok(seq); } + auto target_1q_basis_list = EulerBasisSet::new (); + target_1q_basis_list.add_basis(self.euler_basis); + let euler_decompositions + : SmallVec<[Option; 8]> = + decomposition.iter() + .map(| decomp | + {unitary_to_gate_sequence_inner( + decomp.view(), &target_1q_basis_list, 0, None, + true, None, )}) + .collect(); + auto gates = Vec::with_capacity(TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY); + auto global_phase = target_decomposed.global_phase; + global_phase -= best_nbasis as f64 * self.basis_decomposer.global_phase; + if best_nbasis + == 2 { global_phase += PI; } for i in 0..best_nbasis as usize { if let @@ -914,23 +1006,24 @@ struct GateDecompositionPattern final gates, global_phase, }) - } + } - std::array, 4> traces(TwoQubitWeylDecomposition target) { - return { - 4. * std::complex( - target.a.cos() * target.b.cos() * target.c.cos(), - target.a.sin() * target.b.sin() * target.c.sin(), ), - 4. * - c64((qc::PI_4 - target.a).cos() * - (self.basis_decomposer.b - target.b).cos() * target.c.cos(), - (qc::PI_4 - target.a).sin() * - (self.basis_decomposer.b - target.b).sin() * - target.c.sin(), ), - c64(4. * target.c.cos(), 0.), - c64(4., 0.), - }; - } + std::array traces(TwoQubitWeylDecomposition target) { + return { + 4. * qfp(std::cos(target.a) * std::cos(target.b) * std::cos(target.c), + std::sin(target.a) * std::sin(target.b) * + std::sin(target.c), ), + 4. * qfp(std::cos(qc::PI_4 - target.a) * + std::cos(self.basis_decomposer.b - target.b) * + std::cos(target.c), + std::sin(qc::PI_4 - target.a) * + std::sin(self.basis_decomposer.b - target.b) * + std::sin(target.c)), + qfp(4. * std::cos(target.c), 0.), + qfp(4., 0.), + }; + } + }; }; /** From e4dec477da145528d4c1c7ef66c4c98d31560cf4 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 9 Oct 2025 12:31:54 +0200 Subject: [PATCH 007/237] minor additions --- .../Transforms/GateDecompositionPattern.cpp | 401 +++++++++++++++++- 1 file changed, 387 insertions(+), 14 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 8f4b20412d..fb05f65864 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -564,8 +564,8 @@ struct GateDecompositionPattern final c, global_phase, K1l, - K1r, K2l, + K1r, K2r, Specialization::General, default_euler_basis, @@ -867,7 +867,7 @@ struct GateDecompositionPattern final } specialized.global_phase += std::arg(tr); return specialized; - } // namespace mqt::ir::opt + } }; static constexpr auto DEFAULT_FIDELITY = 1.0 - 1.0e-9; @@ -916,7 +916,7 @@ struct GateDecompositionPattern final auto get_default_nbasis = [&]() { auto minValue = std::numeric_limits::max(); auto minIndex = -1; - for(std::size_t i = 0; i < traces.size(); ++i) { + for (std::size_t i = 0; i < traces.size(); ++i) { auto value = trace_to_fid(traces[i]) * std::pow(basis_fidelity, i); if (value < minValue) { minIndex = i; @@ -928,20 +928,20 @@ struct GateDecompositionPattern final auto best_nbasis = _num_basis_uses.value_or(get_default_nbasis()); auto choose_decomposition = [&]() { if (best_nbasis == 0) { - return decomp0_inner(&target_decomposed); + return decomp0_inner(target_decomposed); } if (best_nbasis == 1) { - return decomp1_inner(&target_decomposed); + return decomp1_inner(target_decomposed); } if (best_nbasis == 2) { - return decomp2_supercontrolled_inner(&target_decomposed); + return decomp2_supercontrolled_inner(target_decomposed); } if (best_nbasis == 3) { - return decomp3_supercontrolled_inner(&target_decomposed); + return decomp3_supercontrolled_inner(target_decomposed); } throw std::logic_error{"Invalid basis to use"}; }; - auto pulse_optimize = this->pulse_optimize.unwrap_or(true); + auto pulse_optimize = this->pulse_optimize.value_or(true); auto sequence = if (pulse_optimize) { self.pulse_optimal_chooser(best_nbasis, &decomposition, &target_decomposed) @@ -1008,21 +1008,394 @@ struct GateDecompositionPattern final }) } - std::array traces(TwoQubitWeylDecomposition target) { + private: + [[nodiscard]] std::vector + decomp0_inner(const TwoQubitWeylDecomposition& target) const { + return { + dot(target.K1r, target.K2r), + dot(target.K1l, target.K2l), + }; + } + + [[nodiscard]] std::vector + decomp1_inner(const TwoQubitWeylDecomposition& target) const { + auto transpose_conjugate = [](auto&& matrix) { + auto result = transpose(matrix); + llvm::transform(result, result.begin(), + [](auto&& x) { return std::conj(x); }); + return result; + }; + + // FIXME: fix for z!=0 and c!=0 using closest reflection (not always in + // the Weyl chamber) + return { + dot(transpose_conjugate(basis_decomposer.K2r), target.K2r), + dot(transpose_conjugate(basis_decomposer.K2l), target.K2l), + dot(target.K1r, transpose_conjugate(basis_decomposer.K1r)), + dot(target.K1l, transpose_conjugate(basis_decomposer.K1l)), + }; + } + + [[nodiscard]] std::vector decomp2_supercontrolled_inner( + const TwoQubitWeylDecomposition& target) const { + return { + dot(q2r, target.K2r), + dot(q2l, target.K2l), + dot(dot(q1ra, rz_matrix(2. * target.b)), q1rb), + dot(dot(q1la, rz_matrix(-2. * target.a)), q1lb), + dot(target.K1r, q0r), + dot(target.K1l, q0l), + }; + } + + [[nodiscard]] std::vector decomp3_supercontrolled_inner( + const TwoQubitWeylDecomposition& target) const { return { - 4. * qfp(std::cos(target.a) * std::cos(target.b) * std::cos(target.c), - std::sin(target.a) * std::sin(target.b) * - std::sin(target.c), ), + dot(u3r, target.K2r), + dot(u3l, target.K2l), + dot(dot(u2ra, rz_matrix(2. * target.b)), u2rb), + dot(dot(u2la, rz_matrix(-2. * target.a)), u2lb), + dot(dot(u1ra, rz_matrix(-2. * target.c)), u1rb), + u1l, + dot(target.K1r, u0r), + dot(target.K1l, u0l), + }; + } + + std::optional> + pulse_optimal_chooser(std::uint8_t best_nbasis, + std::vector decomposition, + const TwoQubitWeylDecomposition& target_decomposed) { + if (pulse_optimize.has_value() && + (best_nbasis == 0 || best_nbasis == 1 || best_nbasis > 3)) { + return std::nullopt; + } + if (euler_basis != EulerBasis::ZSX && euler_basis != EulerBasis::ZSXX) { + if (pulse_optimize.has_value()) { + throw std::runtime_error{ + "'pulse_optimize' currently only works with ZSX basis"}; + } + return std::nullopt; + } + + if (gate != "cx") { + if (pulse_optimize.has_value()) { + throw std::runtime_error{ + "pulse_optimizer currently only works with CNOT entangling gate"}; + } + return std::nullopt; + } + auto result = if (best_nbasis == 3) { + self.get_sx_vz_3cx_efficient_euler(decomposition, target_decomposed) + } + else if (best_nbasis == 2) { + self.get_sx_vz_2cx_efficient_euler(decomposition, target_decomposed) + } + else {None}; + if (pulse_optimize.has_value() && result.is_none()) { + throw std::runtime_error{ + "Failed to compute requested pulse optimal decomposition"}; + } + return result; + } + /// + /// Decomposition of SU(4) gate for device with SX, virtual RZ, and CNOT + /// gates assuming two CNOT gates are needed. + /// + /// This first decomposes each unitary from the KAK decomposition into ZXZ + /// on the source qubit of the CNOTs and XZX on the targets in order to + /// commute operators to beginning and end of decomposition. The beginning + /// and ending single qubit gates are then collapsed and re-decomposed with + /// the single qubit decomposer. This last step could be avoided if + /// performance is a concern. + std::optional> get_sx_vz_2cx_efficient_euler( + const std::vector& decomposition, + const TwoQubitWeylDecomposition& target_decomposed) { + std::vector gates; + auto global_phase = target_decomposed.global_phase; + global_phase -= 2. * basis_decomposer.global_phase; + + auto get_euler_angles = [&](std::size_t startIndex, EulerBasis basis) { + std::vector> result; + for (std::size_t i = startIndex; i < decomposition.size(); i += 2) { + auto euler_angles = angles_from_unitary(decomposition[i], basis); + global_phase += euler_angles[3]; + result.push_back({euler_angles[2], euler_angles[0], euler_angles[1]}); + } + return result; + }; + + auto euler_q0 = get_euler_angles(0, EulerBasis::ZXZ); + auto euler_q1 = get_euler_angles(1, EulerBasis::XZX); + + auto euler_matrix_q0 = + dot(rx_matrix(euler_q0[0][1]), rz_matrix(euler_q0[0][0])); + euler_matrix_q0 = + dot(rz_matrix(euler_q0[0][2] + euler_q0[1][0] + qc::PI_2), + euler_matrix_q0); + append_1q_sequence(gates, global_phase, euler_matrix_q0, 0); + let mut euler_matrix_q1 = + rz_matrix(euler_q1[0][1]).dot(&rx_matrix(euler_q1[0][0])); + euler_matrix_q1 = + rx_matrix(euler_q1[0][2] + euler_q1[1][0]).dot(&euler_matrix_q1); + self.append_1q_sequence(&mut gates, &mut global_phase, + euler_matrix_q1.view(), 1); + gates.push((Some(StandardGate::CX), smallvec ![], smallvec ![ 0, 1 ])); + gates.push((Some(StandardGate::SX), smallvec ![], smallvec ![0])); + gates.push((Some(StandardGate::RZ), smallvec ![euler_q0[1][1] - PI], + smallvec ![0], )); + gates.push((Some(StandardGate::SX), smallvec ![], smallvec ![0])); + gates.push((Some(StandardGate::RZ), smallvec ![euler_q1[1][1]], + smallvec ![1], )); + global_phase += PI2; + gates.push((Some(StandardGate::CX), smallvec ![], smallvec ![ 0, 1 ])); + let mut euler_matrix_q0 = + rx_matrix(euler_q0[2][1]) + .dot(&rz_matrix(euler_q0[1][2] + euler_q0[2][0] + PI2)); + euler_matrix_q0 = rz_matrix(euler_q0[2][2]).dot(&euler_matrix_q0); + self.append_1q_sequence(&mut gates, &mut global_phase, + euler_matrix_q0.view(), 0); + let mut euler_matrix_q1 = + rz_matrix(euler_q1[2][1]) + .dot(&rx_matrix(euler_q1[1][2] + euler_q1[2][0])); + euler_matrix_q1 = rx_matrix(euler_q1[2][2]).dot(&euler_matrix_q1); + self.append_1q_sequence(&mut gates, &mut global_phase, + euler_matrix_q1.view(), 1); + Some(TwoQubitGateSequence{ + gates, + global_phase, + }) + } + + /// Decomposition of SU(4) gate for device with SX, virtual RZ, and CNOT + /// gates assuming three CNOT gates are needed. + /// + /// This first decomposes each unitary from the KAK decomposition into ZXZ + /// on the source qubit of the CNOTs and XZX on the targets in order commute + /// operators to beginning and end of decomposition. Inserting Hadamards + /// reverses the direction of the CNOTs and transforms a variable Rx -> + /// variable virtual Rz. The beginning and ending single qubit gates are + /// then collapsed and re-decomposed with the single qubit decomposer. This + /// last step could be avoided if performance is a concern. + fn get_sx_vz_3cx_efficient_euler( + &self, decomposition : &SmallVec<[Array2; 8]>, + target_decomposed : &TwoQubitWeylDecomposition, ) + -> Option { + let mut gates = Vec::new (); + let mut global_phase = target_decomposed.global_phase; + global_phase -= 3. * self.basis_decomposer.global_phase; + global_phase = global_phase.rem_euclid(TWO_PI); + let atol = 1e-10; // absolute tolerance for floats + // Decompose source unitaries to zxz + let euler_q0 + : Vec<[f64; 3]> = + decomposition.iter() + .step_by(2) + .map(| decomp | + { + let euler_angles = angles_from_unitary( + decomp.view(), EulerBasis::ZXZ); + global_phase += euler_angles[3]; + [ euler_angles[2], euler_angles[0], euler_angles[1] ] + }) + .collect(); + // Decompose target unitaries to xzx + let euler_q1 + : Vec<[f64; 3]> = + decomposition.iter() + .skip(1) + .step_by(2) + .map(| decomp | + { + let euler_angles = angles_from_unitary( + decomp.view(), EulerBasis::XZX); + global_phase += euler_angles[3]; + [ euler_angles[2], euler_angles[0], euler_angles[1] ] + }) + .collect(); + + let x12 = euler_q0[1][2] + euler_q0[2][0]; + let x12_is_non_zero = !abs_diff_eq !(x12, 0., epsilon = atol); + let mut x12_is_old_mult = None; + let mut x12_phase = 0.; + let x12_is_pi_mult = abs_diff_eq !(x12.sin(), 0., epsilon = atol); + if x12_is_pi_mult { + x12_is_old_mult = Some(abs_diff_eq !(x12.cos(), -1., epsilon = atol)); + x12_phase = PI * x12.cos(); + } + let x02_add = x12 - euler_q0[1][0]; + let x12_is_half_pi = abs_diff_eq !(x12, PI2, epsilon = atol); + + let mut euler_matrix_q0 = + rx_matrix(euler_q0[0][1]).dot(&rz_matrix(euler_q0[0][0])); + if x12_is_non_zero + &&x12_is_pi_mult { + euler_matrix_q0 = + rz_matrix(euler_q0[0][2] - x02_add).dot(&euler_matrix_q0); + } + else { + euler_matrix_q0 = + rz_matrix(euler_q0[0][2] + euler_q0[1][0]).dot(&euler_matrix_q0); + } + euler_matrix_q0 = aview2(&H_GATE).dot(&euler_matrix_q0); + self.append_1q_sequence(&mut gates, &mut global_phase, + euler_matrix_q0.view(), 0); + + let rx_0 = rx_matrix(euler_q1[0][0]); + let rz = rz_matrix(euler_q1[0][1]); + let rx_1 = rx_matrix(euler_q1[0][2] + euler_q1[1][0]); + let mut euler_matrix_q1 = rz.dot(&rx_0); + euler_matrix_q1 = rx_1.dot(&euler_matrix_q1); + euler_matrix_q1 = aview2(&H_GATE).dot(&euler_matrix_q1); + self.append_1q_sequence(&mut gates, &mut global_phase, + euler_matrix_q1.view(), 1); + + gates.push((Some(StandardGate::CX), smallvec ![], smallvec ![ 1, 0 ])); + + if x12_is_pi_mult { + // even or odd multiple + if x12_is_non_zero { + global_phase += x12_phase; + } + if x12_is_non_zero + &&x12_is_old_mult.unwrap() { + gates.push((Some(StandardGate::RZ), smallvec ![-euler_q0[1][1]], + smallvec ![0], )); + } + else { + gates.push((Some(StandardGate::RZ), smallvec ![euler_q0[1][1]], + smallvec ![0], )); + global_phase += PI; + } + } + if x12_is_half_pi { + gates.push((Some(StandardGate::SX), smallvec ![], smallvec ![0])); + global_phase -= PI4; + } else if x12_is_non_zero + &&!x12_is_pi_mult { + if self + .pulse_optimize.is_none() { + self.append_1q_sequence(&mut gates, &mut global_phase, + rx_matrix(x12).view(), 0); + } + else { + return None; + } + } + if abs_diff_eq + !(euler_q1[1][1], PI2, epsilon = atol) { + gates.push((Some(StandardGate::SX), smallvec ![], smallvec ![1])); + global_phase -= PI4 + } + else if self + .pulse_optimize.is_none() { + self.append_1q_sequence(&mut gates, &mut global_phase, + rx_matrix(euler_q1[1][1]).view(), 1, ); + } + else { + return None; + } + gates.push((Some(StandardGate::RZ), + smallvec ![euler_q1[1][2] + euler_q1[2][0]], + smallvec ![1], )); + gates.push((Some(StandardGate::CX), smallvec ![], smallvec ![ 1, 0 ])); + gates.push((Some(StandardGate::RZ), smallvec ![euler_q0[2][1]], + smallvec ![0], )); + if abs_diff_eq + !(euler_q1[2][1], PI2, epsilon = atol) { + gates.push((Some(StandardGate::SX), smallvec ![], smallvec ![1])); + global_phase -= PI4; + } + else if self + .pulse_optimize.is_none() { + self.append_1q_sequence(&mut gates, &mut global_phase, + rx_matrix(euler_q1[2][1]).view(), 1, ); + } + else { + return None; + } + gates.push((Some(StandardGate::CX), smallvec ![], smallvec ![ 1, 0 ])); + let mut euler_matrix = + rz_matrix(euler_q0[2][2] + euler_q0[3][0]).dot(&aview2(&H_GATE)); + euler_matrix = rx_matrix(euler_q0[3][1]).dot(&euler_matrix); + euler_matrix = rz_matrix(euler_q0[3][2]).dot(&euler_matrix); + self.append_1q_sequence(&mut gates, &mut global_phase, + euler_matrix.view(), 0); + + let mut euler_matrix = + rx_matrix(euler_q1[2][2] + euler_q1[3][0]).dot(&aview2(&H_GATE)); + euler_matrix = rz_matrix(euler_q1[3][1]).dot(&euler_matrix); + euler_matrix = rx_matrix(euler_q1[3][2]).dot(&euler_matrix); + self.append_1q_sequence(&mut gates, &mut global_phase, + euler_matrix.view(), 1); + + let out_unitary = compute_unitary(&gates, global_phase); + // TODO: fix the sign problem to avoid correction here + if abs_diff_eq + !(target_decomposed.unitary_matrix[[ 0, 0 ]], -out_unitary[[ 0, 0 ]], + epsilon = atol) { + global_phase += PI; + } + Some(TwoQubitGateSequence{ + gates, + global_phase, + }) + } + + void append_1q_sequence(std::vector& gates, qc::fp& global_phase, + matrix4x4 unitary, std::uint8_t qubit) { + std::vector target_1q_basis_list; + target_1q_basis_list.push_back(euler_basis); + auto sequence = unitary_to_gate_sequence_inner( + unitary, &target_1q_basis_list, qubit as usize, None, true, None, ); + if let + Some(sequence) = sequence { + *global_phase += sequence.global_phase; + for + gate in sequence.gates { + gates.push((Some(gate.0), gate.1, smallvec ![qubit])); + } + } + } + + [[nodiscard]] std::array + traces(TwoQubitWeylDecomposition target) const { + return { + 4. * + qfp(std::cos(target.a) * std::cos(target.b) * std::cos(target.c), + std::sin(target.a) * std::sin(target.b) * std::sin(target.c)), 4. * qfp(std::cos(qc::PI_4 - target.a) * - std::cos(self.basis_decomposer.b - target.b) * + std::cos(basis_decomposer.b - target.b) * std::cos(target.c), std::sin(qc::PI_4 - target.a) * - std::sin(self.basis_decomposer.b - target.b) * + std::sin(basis_decomposer.b - target.b) * std::sin(target.c)), qfp(4. * std::cos(target.c), 0.), qfp(4., 0.), }; } + + std::optional> unitary_to_gate_sequence_inner( + matrix4x4 unitary_mat, const std::vector& target_basis_list, + std::size_t qubit, + std::vector> error_map, + bool simplify, std::optional atol) { + target_basis_list.get_bases() + .map(| target_basis | + { + let[theta, phi, lam, phase] = + angles_from_unitary(unitary_mat, target_basis); + generate_circuit(&target_basis, theta, phi, lam, phase, + simplify, atol) + .unwrap() + }) + .min_by( + | a, b | { + let error_a = compare_error_fn(a, &error_map, qubit); + let error_b = compare_error_fn(b, &error_map, qubit); + error_a.partial_cmp(&error_b).unwrap_or(Ordering::Equal) + }) + } }; }; From 9ce8ac3af5e4772e489940fece32cbbd01b588b1 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 9 Oct 2025 23:13:51 +0200 Subject: [PATCH 008/237] add pass to Passes.td --- mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td index b16059771f..6c714a3179 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td @@ -43,6 +43,12 @@ def GateElimination : Pass<"gate-elimination", "mlir::ModuleOp"> { }]; } +def GateDecomposition : Pass<"gate-decomposition", "mlir::ModuleOp"> { + let summary = "This pass performs various gate decompositions to translate quantum gates being used."; + let description = [{ + }]; +} + def MergeRotationGates : Pass<"merge-rotation-gates", "mlir::ModuleOp"> { let summary = "This pass searches for consecutive applications of rotation gates that can be merged."; let description = [{ From f45107b2528ce4a7c708643b6f4de0b4be26ae94 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 9 Oct 2025 23:14:07 +0200 Subject: [PATCH 009/237] snapshot --- .../Transforms/GateDecompositionPattern.cpp | 703 ++++++++++-------- 1 file changed, 413 insertions(+), 290 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index fb05f65864..090ca3353a 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -144,7 +144,10 @@ struct GateDecompositionPattern final using matrix2x2 = std::array; using matrix4x4 = std::array; + static constexpr auto sqrt2 = static_cast(1.4142135623730950488L); static constexpr matrix2x2 identityGate = {1, 0, 0, 1}; + static constexpr matrix2x2 hGate = {1.0 / sqrt2, 1.0 / sqrt2, 1.0 / sqrt2, + -1.0 / sqrt2}; static qc::fp remEuclid(qc::fp a, qc::fp b) { auto r = std::fmod(a, b); @@ -873,7 +876,8 @@ struct GateDecompositionPattern final static constexpr auto DEFAULT_FIDELITY = 1.0 - 1.0e-9; struct TwoQubitBasisDecomposer { - std::string gate; + qc::OpType gate; + std::optional gate_params; qc::fp basis_fidelity; EulerBasis euler_basis; std::optional pulse_optimize; @@ -899,10 +903,22 @@ struct GateDecompositionPattern final matrix2x2 q2l; matrix2x2 q2r; - void twoQubitDecompose(const matrix4x4& unitaryMatrix, - std::optional _basis_fidelity, - bool approximate, - std::optional _num_basis_uses) { + struct QubitGateSequence { + struct Gate { + qc::OpType type{qc::I}; + std::optional parameter; + std::vector qubit_id = {0}; + }; + std::vector gates; + qc::fp globalPhase; + }; + using OneQubitGateSequence = QubitGateSequence; + using TwoQubitGateSequence = QubitGateSequence; + + std::optional + twoQubitDecompose(const matrix4x4& unitaryMatrix, + std::optional _basis_fidelity, bool approximate, + std::optional _num_basis_uses) { auto get_basis_fidelity = [&]() { if (approximate) { return _basis_fidelity.value_or(this->basis_fidelity); @@ -941,71 +957,58 @@ struct GateDecompositionPattern final } throw std::logic_error{"Invalid basis to use"}; }; - auto pulse_optimize = this->pulse_optimize.value_or(true); - auto sequence = if (pulse_optimize) { - self.pulse_optimal_chooser(best_nbasis, &decomposition, - &target_decomposed) - ? + auto decomposition = choose_decomposition(); + if (pulse_optimize.value_or(true)) { + return pulse_optimal_chooser(best_nbasis, decomposition, + target_decomposed); } - else {None}; - if let - Some(seq) = sequence { return Ok(seq); } - auto target_1q_basis_list = EulerBasisSet::new (); - target_1q_basis_list.add_basis(self.euler_basis); - let euler_decompositions - : SmallVec<[Option; 8]> = - decomposition.iter() - .map(| decomp | - {unitary_to_gate_sequence_inner( - decomp.view(), &target_1q_basis_list, 0, None, - true, None, )}) - .collect(); - auto gates = Vec::with_capacity(TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY); - auto global_phase = target_decomposed.global_phase; - global_phase -= best_nbasis as f64 * self.basis_decomposer.global_phase; - if best_nbasis - == 2 { global_phase += PI; } - for - i in 0..best_nbasis as usize { - if let - Some(euler_decomp) = &euler_decompositions[2 * i] { - for - gate in& euler_decomp.gates { - gates.push((gate.0.into(), gate.1.clone(), smallvec ![0])); - } - global_phase += euler_decomp.global_phase - } - if let - Some(euler_decomp) = &euler_decompositions[2 * i + 1] { - for - gate in& euler_decomp.gates { - gates.push((gate.0.into(), gate.1.clone(), smallvec ![1])); - } - global_phase += euler_decomp.global_phase - } - gates.push( - (self.gate.clone(), self.gate_params.clone(), smallvec ![ 0, 1 ])); + std::vector target_1q_basis_list; + target_1q_basis_list.push_back(euler_basis); + llvm::SmallVector, 8> + euler_decompositions; + for (auto&& decomp : decomposition) { + auto euler_decomp = unitary_to_gate_sequence_inner( + decomp, target_1q_basis_list, 0, {}, true, std::nullopt); + euler_decompositions.push_back(euler_decomp); } - if let - Some(euler_decomp) = &euler_decompositions[2 * best_nbasis as usize] { - for - gate in& euler_decomp.gates { - gates.push((gate.0.into(), gate.1.clone(), smallvec ![0])); - } - global_phase += euler_decomp.global_phase + // Worst case length is 5x 1q gates for each 1q decomposition + 1x 2q gate + // We might overallocate a bit if the euler basis is different but + // the worst case is just 16 extra elements with just a String and 2 + // smallvecs each. This is only transient though as the circuit sequences + // aren't long lived and are just used to create a QuantumCircuit or + // DAGCircuit when we return to Python space. + constexpr auto TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY = 21; + TwoQubitGateSequence gates{.globalPhase = target_decomposed.global_phase}; + gates.gates.reserve(TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY); + gates.globalPhase -= best_nbasis * basis_decomposer.global_phase; + if (best_nbasis == 2) { + gates.globalPhase += qc::PI; } - if let - Some(euler_decomp) = &euler_decompositions[2 * best_nbasis as usize + 1] { - for - gate in& euler_decomp.gates { - gates.push((gate.0.into(), gate.1.clone(), smallvec ![1])); + + auto add_euler_decomposition = [&](std::size_t index, + std::size_t qubit_id) { + if (auto&& euler_decomp = euler_decompositions[index]) { + for (auto&& gate : euler_decomp->gates) { + gates.gates.push_back({.type = gate.type, + .parameter = gate.parameter, + .qubit_id = {qubit_id}}); + gates.globalPhase += euler_decomp->globalPhase; } - global_phase += euler_decomp.global_phase + } + }; + + for (std::size_t i = 0; i < best_nbasis; ++i) { + add_euler_decomposition(2 * i, 0); + add_euler_decomposition(2 * i + 1, 1); + + gates.gates.push_back( + {.type = gate, .parameter = gate_params, .qubit_id = {0, 1}}); } - Ok(TwoQubitGateSequence{ - gates, - global_phase, - }) + + add_euler_decomposition(2 * best_nbasis, 0); + add_euler_decomposition(2 * best_nbasis + 1, 1); + + return gates; } private: @@ -1062,7 +1065,7 @@ struct GateDecompositionPattern final }; } - std::optional> + std::optional pulse_optimal_chooser(std::uint8_t best_nbasis, std::vector decomposition, const TwoQubitWeylDecomposition& target_decomposed) { @@ -1085,19 +1088,64 @@ struct GateDecompositionPattern final } return std::nullopt; } - auto result = if (best_nbasis == 3) { - self.get_sx_vz_3cx_efficient_euler(decomposition, target_decomposed) - } - else if (best_nbasis == 2) { - self.get_sx_vz_2cx_efficient_euler(decomposition, target_decomposed) + std::optional result; + if (best_nbasis == 3) { + result = + get_sx_vz_3cx_efficient_euler(decomposition, target_decomposed); + } else if (best_nbasis == 2) { + result = + get_sx_vz_2cx_efficient_euler(decomposition, target_decomposed); } - else {None}; - if (pulse_optimize.has_value() && result.is_none()) { + if (pulse_optimize.has_value() && !result.has_value()) { throw std::runtime_error{ "Failed to compute requested pulse optimal decomposition"}; } return result; } + + matrix2x2 getSingleQubitMatrix(const QubitGateSequence::Gate& gate) { + if (gate.type == qc::SX) { + return {qfp{0.5, 0.5}, qfp{0.5, -0.5}, qfp{0.5, -0.5}, qfp{0.5, 0.5}}; + } + if (gate.type == qc::RZ) { + return rz_matrix(gate.parameter.value()); + } + if (gate.type == qc::X) { + return {0, 1, 1, 0}; + } + throw std::invalid_argument{ + "unsupported gate type for single qubit matrix"}; + } + + matrix4x4 getTwoQubitMatrix(const QubitGateSequence::Gate& gate) { + if (gate.qubit_id.empty()) { + return kroneckerProduct(identityGate, identityGate); + } else if (gate.qubit_id.size() == 1) { + if (gate.qubit_id[0] == 0) { + return kroneckerProduct(identityGate, getSingleQubitMatrix(gate)); + } else if (gate.qubit_id[0] == 1) { + return kroneckerProduct(getSingleQubitMatrix(gate), identityGate); + } else { + throw std::logic_error{"Invalid qubit ID in compute_unitary"}; + } + } else if (gate.qubit_id.size() == 2) { + if (gate.type == qc::X) { + // controlled X (CX) + if (gate.qubit_id == std::vector{0, 1}) { + return {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0}; + } + if (gate.qubit_id == std::vector{1, 0}) { + return {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}; + } + } + throw std::invalid_argument{ + "unsupported gate type for two qubit matrix"}; + } else { + throw std::logic_error{ + "Invalid number of qubit IDs in compute_unitary"}; + } + } + /// /// Decomposition of SU(4) gate for device with SX, virtual RZ, and CNOT /// gates assuming two CNOT gates are needed. @@ -1108,18 +1156,17 @@ struct GateDecompositionPattern final /// and ending single qubit gates are then collapsed and re-decomposed with /// the single qubit decomposer. This last step could be avoided if /// performance is a concern. - std::optional> get_sx_vz_2cx_efficient_euler( + TwoQubitGateSequence get_sx_vz_2cx_efficient_euler( const std::vector& decomposition, const TwoQubitWeylDecomposition& target_decomposed) { - std::vector gates; - auto global_phase = target_decomposed.global_phase; - global_phase -= 2. * basis_decomposer.global_phase; + TwoQubitGateSequence gates{.globalPhase = target_decomposed.global_phase}; + gates.globalPhase -= 2. * basis_decomposer.global_phase; auto get_euler_angles = [&](std::size_t startIndex, EulerBasis basis) { std::vector> result; for (std::size_t i = startIndex; i < decomposition.size(); i += 2) { auto euler_angles = angles_from_unitary(decomposition[i], basis); - global_phase += euler_angles[3]; + gates.globalPhase += euler_angles[3]; result.push_back({euler_angles[2], euler_angles[0], euler_angles[1]}); } return result; @@ -1133,38 +1180,34 @@ struct GateDecompositionPattern final euler_matrix_q0 = dot(rz_matrix(euler_q0[0][2] + euler_q0[1][0] + qc::PI_2), euler_matrix_q0); - append_1q_sequence(gates, global_phase, euler_matrix_q0, 0); - let mut euler_matrix_q1 = - rz_matrix(euler_q1[0][1]).dot(&rx_matrix(euler_q1[0][0])); + append_1q_sequence(gates, euler_matrix_q0, 0); + auto euler_matrix_q1 = + dot(rz_matrix(euler_q1[0][1]), rx_matrix(euler_q1[0][0])); euler_matrix_q1 = - rx_matrix(euler_q1[0][2] + euler_q1[1][0]).dot(&euler_matrix_q1); - self.append_1q_sequence(&mut gates, &mut global_phase, - euler_matrix_q1.view(), 1); - gates.push((Some(StandardGate::CX), smallvec ![], smallvec ![ 0, 1 ])); - gates.push((Some(StandardGate::SX), smallvec ![], smallvec ![0])); - gates.push((Some(StandardGate::RZ), smallvec ![euler_q0[1][1] - PI], - smallvec ![0], )); - gates.push((Some(StandardGate::SX), smallvec ![], smallvec ![0])); - gates.push((Some(StandardGate::RZ), smallvec ![euler_q1[1][1]], - smallvec ![1], )); - global_phase += PI2; - gates.push((Some(StandardGate::CX), smallvec ![], smallvec ![ 0, 1 ])); - let mut euler_matrix_q0 = - rx_matrix(euler_q0[2][1]) - .dot(&rz_matrix(euler_q0[1][2] + euler_q0[2][0] + PI2)); - euler_matrix_q0 = rz_matrix(euler_q0[2][2]).dot(&euler_matrix_q0); - self.append_1q_sequence(&mut gates, &mut global_phase, - euler_matrix_q0.view(), 0); - let mut euler_matrix_q1 = - rz_matrix(euler_q1[2][1]) - .dot(&rx_matrix(euler_q1[1][2] + euler_q1[2][0])); - euler_matrix_q1 = rx_matrix(euler_q1[2][2]).dot(&euler_matrix_q1); - self.append_1q_sequence(&mut gates, &mut global_phase, - euler_matrix_q1.view(), 1); - Some(TwoQubitGateSequence{ - gates, - global_phase, - }) + dot(rx_matrix(euler_q1[0][2] + euler_q1[1][0]), euler_matrix_q1); + append_1q_sequence(gates, euler_matrix_q1, 1); + gates.gates.push_back( + {.type = qc::X, .qubit_id = {0, 1}}); // TODO: mark CX somehow? + gates.gates.push_back({.type = qc::SX, .qubit_id = {0}}); + gates.gates.push_back({.type = qc::RZ, + .parameter = euler_q0[1][1] - qc::PI, + .qubit_id = {0}}); + gates.gates.push_back({.type = qc::SX, .qubit_id = {0}}); + gates.gates.push_back( + {.type = qc::RZ, .parameter = euler_q1[1][1], .qubit_id = {1}}); + gates.globalPhase += qc::PI_2; + gates.gates.push_back( + {.type = qc::X, .qubit_id = {0, 1}}); // TODO: mark CX somehow? + euler_matrix_q0 = + dot(rx_matrix(euler_q0[2][1]), + rz_matrix(euler_q0[1][2] + euler_q0[2][0] + qc::PI_2)); + euler_matrix_q0 = dot(rz_matrix(euler_q0[2][2]), euler_matrix_q0); + append_1q_sequence(gates, euler_matrix_q0, 0); + euler_matrix_q1 = dot(rz_matrix(euler_q1[2][1]), + rx_matrix(euler_q1[1][2] + euler_q1[2][0])); + euler_matrix_q1 = dot(rx_matrix(euler_q1[2][2]), euler_matrix_q1); + append_1q_sequence(gates, euler_matrix_q1, 1); + return gates; } /// Decomposition of SU(4) gate for device with SX, virtual RZ, and CNOT @@ -1177,185 +1220,158 @@ struct GateDecompositionPattern final /// variable virtual Rz. The beginning and ending single qubit gates are /// then collapsed and re-decomposed with the single qubit decomposer. This /// last step could be avoided if performance is a concern. - fn get_sx_vz_3cx_efficient_euler( - &self, decomposition : &SmallVec<[Array2; 8]>, - target_decomposed : &TwoQubitWeylDecomposition, ) - -> Option { - let mut gates = Vec::new (); - let mut global_phase = target_decomposed.global_phase; - global_phase -= 3. * self.basis_decomposer.global_phase; - global_phase = global_phase.rem_euclid(TWO_PI); - let atol = 1e-10; // absolute tolerance for floats - // Decompose source unitaries to zxz - let euler_q0 - : Vec<[f64; 3]> = - decomposition.iter() - .step_by(2) - .map(| decomp | - { - let euler_angles = angles_from_unitary( - decomp.view(), EulerBasis::ZXZ); - global_phase += euler_angles[3]; - [ euler_angles[2], euler_angles[0], euler_angles[1] ] - }) - .collect(); + std::optional get_sx_vz_3cx_efficient_euler( + const std::vector& decomposition, + const TwoQubitWeylDecomposition& target_decomposed) { + TwoQubitGateSequence gates{.globalPhase = target_decomposed.global_phase}; + gates.globalPhase -= 3. * basis_decomposer.global_phase; + gates.globalPhase = remEuclid(gates.globalPhase, qc::TAU); + auto atol = 1e-10; // absolute tolerance for floats + // Decompose source unitaries to zxz + + auto get_euler_angles = [&](std::size_t startIndex, EulerBasis basis) { + std::vector> result; + for (std::size_t i = startIndex; i < decomposition.size(); i += 2) { + auto euler_angles = angles_from_unitary(decomposition[i], basis); + gates.globalPhase += euler_angles[3]; + result.push_back({euler_angles[2], euler_angles[0], euler_angles[1]}); + } + return result; + }; + + auto euler_q0 = get_euler_angles(0, EulerBasis::ZXZ); // Decompose target unitaries to xzx - let euler_q1 - : Vec<[f64; 3]> = - decomposition.iter() - .skip(1) - .step_by(2) - .map(| decomp | - { - let euler_angles = angles_from_unitary( - decomp.view(), EulerBasis::XZX); - global_phase += euler_angles[3]; - [ euler_angles[2], euler_angles[0], euler_angles[1] ] - }) - .collect(); - - let x12 = euler_q0[1][2] + euler_q0[2][0]; - let x12_is_non_zero = !abs_diff_eq !(x12, 0., epsilon = atol); - let mut x12_is_old_mult = None; - let mut x12_phase = 0.; - let x12_is_pi_mult = abs_diff_eq !(x12.sin(), 0., epsilon = atol); - if x12_is_pi_mult { - x12_is_old_mult = Some(abs_diff_eq !(x12.cos(), -1., epsilon = atol)); - x12_phase = PI * x12.cos(); + auto euler_q1 = get_euler_angles(1, EulerBasis::XZX); + + auto x12 = euler_q0[1][2] + euler_q0[2][0]; + auto x12_is_non_zero = std::abs(x12) < atol; + auto x12_is_old_mult = std::abs(std::cos(x12) - 1.0) < atol; + auto x12_phase = 0.; + auto x12_is_pi_mult = std::abs(std::sin(x12)) < atol; + if (x12_is_pi_mult) { + x12_phase = qc::PI * std::cos(x12); } - let x02_add = x12 - euler_q0[1][0]; - let x12_is_half_pi = abs_diff_eq !(x12, PI2, epsilon = atol); - - let mut euler_matrix_q0 = - rx_matrix(euler_q0[0][1]).dot(&rz_matrix(euler_q0[0][0])); - if x12_is_non_zero - &&x12_is_pi_mult { - euler_matrix_q0 = - rz_matrix(euler_q0[0][2] - x02_add).dot(&euler_matrix_q0); - } - else { + auto x02_add = x12 - euler_q0[1][0]; + auto x12_is_half_pi = std::abs(x12 - qc::PI_2) < atol; + + auto euler_matrix_q0 = + dot(rx_matrix(euler_q0[0][1]), rz_matrix(euler_q0[0][0])); + if (x12_is_non_zero && x12_is_pi_mult) { + euler_matrix_q0 = + dot(rz_matrix(euler_q0[0][2] - x02_add), euler_matrix_q0); + } else { euler_matrix_q0 = - rz_matrix(euler_q0[0][2] + euler_q0[1][0]).dot(&euler_matrix_q0); + dot(rz_matrix(euler_q0[0][2] + euler_q0[1][0]), euler_matrix_q0); } - euler_matrix_q0 = aview2(&H_GATE).dot(&euler_matrix_q0); - self.append_1q_sequence(&mut gates, &mut global_phase, - euler_matrix_q0.view(), 0); - - let rx_0 = rx_matrix(euler_q1[0][0]); - let rz = rz_matrix(euler_q1[0][1]); - let rx_1 = rx_matrix(euler_q1[0][2] + euler_q1[1][0]); - let mut euler_matrix_q1 = rz.dot(&rx_0); - euler_matrix_q1 = rx_1.dot(&euler_matrix_q1); - euler_matrix_q1 = aview2(&H_GATE).dot(&euler_matrix_q1); - self.append_1q_sequence(&mut gates, &mut global_phase, - euler_matrix_q1.view(), 1); - - gates.push((Some(StandardGate::CX), smallvec ![], smallvec ![ 1, 0 ])); - - if x12_is_pi_mult { + euler_matrix_q0 = dot(hGate, euler_matrix_q0); + append_1q_sequence(gates, euler_matrix_q0, 0); + + auto rx_0 = rx_matrix(euler_q1[0][0]); + auto rz = rz_matrix(euler_q1[0][1]); + auto rx_1 = rx_matrix(euler_q1[0][2] + euler_q1[1][0]); + auto euler_matrix_q1 = dot(rz, rx_0); + euler_matrix_q1 = dot(rx_1, euler_matrix_q1); + euler_matrix_q1 = dot(hGate, euler_matrix_q1); + append_1q_sequence(gates, euler_matrix_q1, 1); + + gates.gates.push_back({.type = qc::X, .qubit_id = {1, 0}}); + + if (x12_is_pi_mult) { // even or odd multiple - if x12_is_non_zero { - global_phase += x12_phase; + if (x12_is_non_zero) { + gates.globalPhase += x12_phase; } - if x12_is_non_zero - &&x12_is_old_mult.unwrap() { - gates.push((Some(StandardGate::RZ), smallvec ![-euler_q0[1][1]], - smallvec ![0], )); - } - else { - gates.push((Some(StandardGate::RZ), smallvec ![euler_q0[1][1]], - smallvec ![0], )); - global_phase += PI; + if (x12_is_non_zero && x12_is_old_mult) { + gates.gates.push_back({.type = qc::RZ, + .parameter = {-euler_q0[1][1]}, + .qubit_id = {0}}); + } else { + gates.gates.push_back( + {.type = qc::RZ, .parameter = {euler_q0[1][1]}, .qubit_id = {0}}); + gates.globalPhase += qc::PI; } } - if x12_is_half_pi { - gates.push((Some(StandardGate::SX), smallvec ![], smallvec ![0])); - global_phase -= PI4; - } else if x12_is_non_zero - &&!x12_is_pi_mult { - if self - .pulse_optimize.is_none() { - self.append_1q_sequence(&mut gates, &mut global_phase, - rx_matrix(x12).view(), 0); - } - else { - return None; - } - } - if abs_diff_eq - !(euler_q1[1][1], PI2, epsilon = atol) { - gates.push((Some(StandardGate::SX), smallvec ![], smallvec ![1])); - global_phase -= PI4 - } - else if self - .pulse_optimize.is_none() { - self.append_1q_sequence(&mut gates, &mut global_phase, - rx_matrix(euler_q1[1][1]).view(), 1, ); + if (x12_is_half_pi) { + gates.gates.push_back({.type = qc::SX, .qubit_id = {0}}); + gates.globalPhase -= qc::PI_4; + } else if (x12_is_non_zero && !x12_is_pi_mult) { + if (!pulse_optimize.has_value()) { + append_1q_sequence(gates, rx_matrix(x12), 0); + } else { + return std::nullopt; } - else { - return None; } - gates.push((Some(StandardGate::RZ), - smallvec ![euler_q1[1][2] + euler_q1[2][0]], - smallvec ![1], )); - gates.push((Some(StandardGate::CX), smallvec ![], smallvec ![ 1, 0 ])); - gates.push((Some(StandardGate::RZ), smallvec ![euler_q0[2][1]], - smallvec ![0], )); - if abs_diff_eq - !(euler_q1[2][1], PI2, epsilon = atol) { - gates.push((Some(StandardGate::SX), smallvec ![], smallvec ![1])); - global_phase -= PI4; - } - else if self - .pulse_optimize.is_none() { - self.append_1q_sequence(&mut gates, &mut global_phase, - rx_matrix(euler_q1[2][1]).view(), 1, ); - } - else { - return None; + if (std::abs(euler_q1[1][1] - qc::PI_2) < atol) { + gates.gates.push_back({.type = qc::SX, .qubit_id = {1}}); + gates.globalPhase -= qc::PI_4; + } else if (!pulse_optimize.has_value()) { + append_1q_sequence(gates, rx_matrix(euler_q1[1][1]), 1); + } else { + return std::nullopt; + } + gates.gates.push_back({.type = qc::RZ, + .parameter = {euler_q1[1][2] + euler_q1[2][0]}, + .qubit_id = {1}}); + gates.gates.push_back({.type = qc::X, .qubit_id = {1, 0}}); + gates.gates.push_back( + {.type = qc::RZ, .parameter = {euler_q1[2][1]}, .qubit_id = {0}}); + if (std::abs(euler_q1[2][1] - qc::PI_2) < atol) { + gates.gates.push_back({.type = qc::SX, .qubit_id = {1}}); + gates.globalPhase -= qc::PI_4; + } else if (!pulse_optimize.has_value()) { + append_1q_sequence(gates, rx_matrix(euler_q1[2][1]), 1); + } else { + return std::nullopt; } - gates.push((Some(StandardGate::CX), smallvec ![], smallvec ![ 1, 0 ])); - let mut euler_matrix = - rz_matrix(euler_q0[2][2] + euler_q0[3][0]).dot(&aview2(&H_GATE)); - euler_matrix = rx_matrix(euler_q0[3][1]).dot(&euler_matrix); - euler_matrix = rz_matrix(euler_q0[3][2]).dot(&euler_matrix); - self.append_1q_sequence(&mut gates, &mut global_phase, - euler_matrix.view(), 0); - - let mut euler_matrix = - rx_matrix(euler_q1[2][2] + euler_q1[3][0]).dot(&aview2(&H_GATE)); - euler_matrix = rz_matrix(euler_q1[3][1]).dot(&euler_matrix); - euler_matrix = rx_matrix(euler_q1[3][2]).dot(&euler_matrix); - self.append_1q_sequence(&mut gates, &mut global_phase, - euler_matrix.view(), 1); - - let out_unitary = compute_unitary(&gates, global_phase); + gates.gates.push_back({.type = qc::X, .qubit_id = {1, 0}}); + auto eulerMatrix = dot(rz_matrix(euler_q0[2][2] + euler_q0[3][0]), hGate); + eulerMatrix = dot(rx_matrix(euler_q0[3][1]), eulerMatrix); + eulerMatrix = dot(rz_matrix(euler_q0[3][2]), eulerMatrix); + append_1q_sequence(gates, eulerMatrix, 0); + + eulerMatrix = dot(rx_matrix(euler_q1[2][2] + euler_q1[3][0]), hGate); + eulerMatrix = dot(rz_matrix(euler_q1[3][1]), eulerMatrix); + eulerMatrix = dot(rx_matrix(euler_q1[3][2]), eulerMatrix); + append_1q_sequence(gates, eulerMatrix, 1); + + auto out_unitary = compute_unitary(gates, gates.globalPhase); // TODO: fix the sign problem to avoid correction here - if abs_diff_eq - !(target_decomposed.unitary_matrix[[ 0, 0 ]], -out_unitary[[ 0, 0 ]], - epsilon = atol) { - global_phase += PI; - } - Some(TwoQubitGateSequence{ - gates, - global_phase, - }) + if (std::abs(target_decomposed.unitary_matrix.at(0 * 4 + 0) - + (-out_unitary.at(0 * 4 + 0))) < atol) { + gates.globalPhase += qc::PI; + } + return gates; } - void append_1q_sequence(std::vector& gates, qc::fp& global_phase, - matrix4x4 unitary, std::uint8_t qubit) { + matrix4x4 compute_unitary(const TwoQubitGateSequence& sequence, + qc::fp global_phase) { + auto phase = std::exp(std::complex{0, global_phase}); + matrix4x4 matrix; + matrix[0 * 4 + 0] = phase; + matrix[1 * 4 + 1] = phase; + matrix[2 * 4 + 2] = phase; + matrix[3 * 4 + 3] = phase; + + for (auto&& gate : sequence.gates) { + matrix4x4 gate_matrix = getTwoQubitMatrix(gate); + + matrix = dot(gate_matrix, matrix); + } + return matrix; + } + + void append_1q_sequence(TwoQubitGateSequence& twoQubitSequence, + matrix2x2 unitary, std::uint8_t qubit) { std::vector target_1q_basis_list; target_1q_basis_list.push_back(euler_basis); auto sequence = unitary_to_gate_sequence_inner( - unitary, &target_1q_basis_list, qubit as usize, None, true, None, ); - if let - Some(sequence) = sequence { - *global_phase += sequence.global_phase; - for - gate in sequence.gates { - gates.push((Some(gate.0), gate.1, smallvec ![qubit])); - } - } + unitary, target_1q_basis_list, qubit, {}, true, std::nullopt); + twoQubitSequence.globalPhase += sequence.globalPhase; + for (auto&& gate : sequence.gates) { + twoQubitSequence.gates.push_back({.type = gate.type, + .parameter = gate.parameter, + .qubit_id = {qubit}}); + } } [[nodiscard]] std::array @@ -1375,28 +1391,135 @@ struct GateDecompositionPattern final }; } - std::optional> unitary_to_gate_sequence_inner( - matrix4x4 unitary_mat, const std::vector& target_basis_list, + OneQubitGateSequence generate_circuit(EulerBasis target_basis, + const matrix2x2& unitaryMatrix, + bool simplify, + std::optional atol) { + auto [theta, phi, lamda, phase] = + angles_from_unitary(unitaryMatrix, target_basis); + + switch (target_basis) { + case EulerBasis::ZYZ: + return calculateRotationGates(theta, phi, lamda, phase, qc::RZ, qc::RY, + simplify, atol); + case EulerBasis::ZXZ: + return calculateRotationGates(theta, phi, lamda, phase, qc::RZ, qc::RX, + simplify, atol); + case EulerBasis::XZX: + return calculateRotationGates(theta, phi, lamda, phase, qc::RX, qc::RZ, + simplify, atol); + case EulerBasis::XYX: + return calculateRotationGates(theta, phi, lamda, phase, qc::RX, qc::RY, + simplify, atol); + default: + // TODO: allow other bases + throw std::invalid_argument{"Unsupported base for circuit generation!"}; + } + } + + OneQubitGateSequence unitary_to_gate_sequence_inner( + matrix2x2 unitary_mat, const std::vector& target_basis_list, std::size_t qubit, - std::vector> error_map, + const std::vector>& + error_map, // TODO: remove error_map+qubit for platform independence bool simplify, std::optional atol) { - target_basis_list.get_bases() - .map(| target_basis | - { - let[theta, phi, lam, phase] = - angles_from_unitary(unitary_mat, target_basis); - generate_circuit(&target_basis, theta, phi, lam, phase, - simplify, atol) - .unwrap() - }) - .min_by( - | a, b | { - let error_a = compare_error_fn(a, &error_map, qubit); - let error_b = compare_error_fn(b, &error_map, qubit); - error_a.partial_cmp(&error_b).unwrap_or(Ordering::Equal) - }) + auto calculateError = [](const OneQubitGateSequence& sequence) { + return sequence.gates.size(); + }; + + auto minError = std::numeric_limits::max(); + OneQubitGateSequence bestCircuit; + for (std::size_t i = 0; i < target_basis_list.size(); ++i) { + auto& target_basis = target_basis_list[i]; + auto circuit = + generate_circuit(target_basis, unitary_mat, simplify, atol); + auto error = calculateError(circuit); + if (error < minError) { + bestCircuit = circuit; + minError = error; + } + return bestCircuit; + } } - }; + + // TODO: copied+adapted from single-qubit decomposition + /** + * @note Adapted from circuit_kak() in the IBM Qiskit framework. + * (C) Copyright IBM 2022 + * + * This code is licensed under the Apache License, Version 2.0. You + * may obtain a copy of this license in the LICENSE.txt file in the root + * directory of this source tree or at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice + * indicating that they have been altered from the originals. + */ + [[nodiscard]] static OneQubitGateSequence + calculateRotationGates(qc::fp theta, qc::fp phi, qc::fp lambda, + qc::fp phase, qc::OpType kGate, qc::OpType aGate, + bool simplify, std::optional atol) { + qc::fp angleZeroEpsilon = atol.value_or(1e-12); + if (simplify) { + angleZeroEpsilon = -1.0; + } + + auto remEuclid = [](qc::fp a, qc::fp b) { + auto r = std::fmod(a, b); + return (r < 0.0) ? r + std::abs(b) : r; + }; + // Wrap angle into interval [-π,π). If within atol of the endpoint, clamp + // to -π + auto mod2pi = [&](qc::fp angle) -> qc::fp { + // remEuclid() isn't exactly the same as Python's % operator, but + // because the RHS here is a constant and positive it is effectively + // equivalent for this case + auto wrapped = remEuclid(angle + qc::PI, 2. * qc::PI) - qc::PI; + if (std::abs(wrapped - qc::PI) < angleZeroEpsilon) { + return -qc::PI; + } + return wrapped; + }; + + qc::fp globalPhase = phase - ((phi + lambda) / 2.); + + std::vector gates; + if (std::abs(theta) < angleZeroEpsilon) { + lambda += phi; + lambda = mod2pi(lambda); + if (std::abs(lambda) > angleZeroEpsilon) { + gates.push_back({kGate, lambda}); + globalPhase += lambda / 2.0; + } + return {gates, globalPhase}; + } + + if (std::abs(theta - qc::PI) < angleZeroEpsilon) { + globalPhase += phi; + lambda -= phi; + phi = 0.0; + } + if (std::abs(mod2pi(lambda + qc::PI)) < angleZeroEpsilon || + std::abs(mod2pi(phi + qc::PI)) < angleZeroEpsilon) { + lambda += qc::PI; + theta = -theta; + phi += qc::PI; + } + lambda = mod2pi(lambda); + if (std::abs(lambda) > angleZeroEpsilon) { + globalPhase += lambda / 2.0; + gates.push_back({kGate, lambda}); + } + gates.push_back({aGate, theta}); + phi = mod2pi(phi); + if (std::abs(phi) > angleZeroEpsilon) { + globalPhase += phi / 2.0; + gates.push_back({kGate, phi}); + } + return {gates, globalPhase}; + } + }; // namespace mqt::ir::opt }; /** From 81e6230c54f650a50bf8319b30f8e78122777e40 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 13 Oct 2025 21:41:51 +0200 Subject: [PATCH 010/237] compiling snapshot --- .../Dialect/MQTOpt/Transforms/CMakeLists.txt | 2 +- .../Transforms/GateDecompositionPattern.cpp | 366 +++++++++++++++--- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 27 +- 3 files changed, 332 insertions(+), 63 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt b/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt index e7f1f55dac..6badce698e 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt +++ b/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt @@ -7,7 +7,7 @@ # Licensed under the MIT License get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) -set(LIBRARIES ${dialect_libs} MQT::CoreIR) +set(LIBRARIES ${dialect_libs} MQT::CoreIR MQT::CoreDD) add_compile_options(-fexceptions) file(GLOB_RECURSE TRANSFORMS_SOURCES *.cpp) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 090ca3353a..7f2ebb5418 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -43,18 +43,29 @@ struct GateDecompositionPattern final matchAndRewrite(UnitaryInterface op, mlir::PatternRewriter& rewriter) const override { auto series = getTwoQubitSeries(op); + llvm::errs() << "SERIES SIZE: " << series.size() << '\n'; + if (series.size() <= 3) { return mlir::failure(); } - dd::TwoQubitGateMatrix unitaryMatrix = dd::opToTwoQubitGateMatrix(qc::I); + matrix4x4 unitaryMatrix = + helpers::flatten(dd::opToTwoQubitGateMatrix(qc::I)); for (auto&& gate : series) { if (auto gateMatrix = helpers::getUnitaryMatrix(gate)) { - unitaryMatrix = helpers::multiply(unitaryMatrix, *gateMatrix); + unitaryMatrix = + helpers::multiply(unitaryMatrix, helpers::flatten(*gateMatrix)); } } - twoQubitDecompose(unitaryMatrix); + TwoQubitBasisDecomposer decomposer; + auto sequence = decomposer.twoQubitDecompose(unitaryMatrix, DEFAULT_FIDELITY, true, + std::nullopt); + if (!sequence) { + return mlir::failure(); + } + + applySeries(rewriter, series, *sequence); return mlir::success(); } @@ -71,14 +82,22 @@ struct GateDecompositionPattern final } else { return result; } + result.push_back(op); + while (true) { - for (auto&& user : op->getUsers()) { - auto userUnitary = llvm::cast(user); + for (auto&& user : llvm::concat(qubits[0].getUsers(), + qubits[1].getUsers())) { + auto userUnitary = mlir::dyn_cast(user); + if (!userUnitary) { + return result; + } + if (helpers::isSingleQubitOperation(userUnitary)) { auto&& operand = userUnitary->getOperand(0); auto* it = llvm::find(qubits, operand); if (it == qubits.end()) { - return result; + throw std::logic_error{"Operand of single-qubit op and user of " + "qubit is not in qubits"}; } *it = userUnitary->getResult(0); @@ -99,7 +118,48 @@ struct GateDecompositionPattern final return result; } } - return result; + } + return result; + } + + /** + * @brief Creates a new rotation gate with no controls. + * + * @tparam OpType The type of the operation to be created. + * @param op The first instance of the rotation gate. + * @param rewriter The pattern rewriter. + * @return A new rotation gate. + */ + template + static OpType createOneParameterGate(mlir::PatternRewriter& rewriter, + mlir::Location location, + qc::fp parameter, mlir::ValueRange inQubits) { + auto parameterValue = rewriter.create( + location, rewriter.getF64Type(), rewriter.getF64FloatAttr(parameter)); + + return rewriter.create( + location, inQubits.getType(), mlir::TypeRange{}, mlir::TypeRange{}, + mlir::DenseF64ArrayAttr{}, mlir::DenseBoolArrayAttr{}, + mlir::ValueRange{parameterValue}, inQubits, mlir::ValueRange{}, + mlir::ValueRange{}); + } + + + struct QubitGateSequence { + struct Gate { + qc::OpType type{qc::I}; + std::vector parameter; + std::vector qubit_id = {0}; + }; + std::vector gates; + qc::fp globalPhase; + }; + using OneQubitGateSequence = QubitGateSequence; + using TwoQubitGateSequence = QubitGateSequence; + + static void applySeries(mlir::PatternRewriter& rewriter, const llvm::SmallVector& series, const TwoQubitGateSequence& sequence) { + if (sequence.globalPhase != 0.0) { + createOneParameterGate(rewriter, series[0]->getLoc(), sequence.globalPhase, {}); } } @@ -154,6 +214,21 @@ struct GateDecompositionPattern final return (r < 0.0) ? r + std::abs(b) : r; } + // Wrap angle into interval [-π,π). If within atol of the endpoint, clamp + // to -π + static qc::fp + mod2pi(qc::fp angle, + qc::fp angleZeroEpsilon = std::numeric_limits::epsilon()) { + // remEuclid() isn't exactly the same as Python's % operator, but + // because the RHS here is a constant and positive it is effectively + // equivalent for this case + auto wrapped = remEuclid(angle + qc::PI, 2. * qc::PI) - qc::PI; + if (std::abs(wrapped - qc::PI) < angleZeroEpsilon) { + return -qc::PI; + } + return wrapped; + } + static matrix2x2 dot(const matrix2x2& lhs, const matrix2x2& rhs) { return lhs; } @@ -164,6 +239,13 @@ struct GateDecompositionPattern final static matrix2x2 transpose(const matrix2x2& x) { return x; } static matrix4x4 transpose(const matrix4x4& x) { return x; } + static matrix2x2 transpose_conjugate(const matrix2x2& matrix) { + auto result = transpose(matrix); + llvm::transform(result, result.begin(), + [](auto&& x) { return std::conj(x); }); + return result; + }; + static qfp determinant(const matrix2x2& x) { return 0.0; }; static qfp determinant(const matrix4x4& x) { return 0.0; }; @@ -308,7 +390,45 @@ struct GateDecompositionPattern final throw std::invalid_argument{"Unknown EulerBasis for angles_from_unitary"}; } - static std::array params_xyx_inner(const matrix2x2& matrix) {} + static std::array params_zyz_inner(const matrix2x2& matrix) { + auto getIndex = [](auto x, auto y) { return (y * 2) + x; }; + auto determinant = [getIndex](auto&& matrix) { + return (matrix.at(getIndex(0, 0)) * matrix.at(getIndex(1, 1))) - + (matrix.at(getIndex(1, 0)) * matrix.at(getIndex(0, 1))); + }; + + auto detArg = std::arg(determinant(matrix)); + auto phase = 0.5 * detArg; + auto theta = 2. * std::atan2(std::abs(matrix.at(getIndex(1, 0))), + std::abs(matrix.at(getIndex(0, 0)))); + auto ang1 = std::arg(matrix.at(getIndex(1, 1))); + auto ang2 = std::arg(matrix.at(getIndex(1, 0))); + auto phi = ang1 + ang2 - detArg; + auto lam = ang1 - ang2; + return {theta, phi, lam, phase}; + } + + static std::array params_xyx_inner(const matrix2x2& matrix) { + auto mat_zyz = std::array{ + 0.5 * (matrix.at(0 * 2 + 0) + matrix.at(0 * 2 + 1 * 2 * 2) + + matrix.at(1 * 2 + 0) + matrix.at(1 * 2 + 1)), + 0.5 * (matrix.at(0 * 2 + 0) - matrix.at(0 * 2 + 1 * 2 * 2) + + matrix.at(1 * 2 + 0) - matrix.at(1 * 2 + 1)), + 0.5 * (matrix.at(0 * 2 + 0) + matrix.at(0 * 2 + 1 * 2 * 2) - + matrix.at(1 * 2 + 0) - matrix.at(1 * 2 + 1)), + 0.5 * (matrix.at(0 * 2 + 0) - matrix.at(0 * 2 + 1 * 2 * 2) - + matrix.at(1 * 2 + 0) + matrix.at(1 * 2 + 1)), + }; + auto [theta, phi, lam, phase] = params_zyz_inner(mat_zyz); + auto new_phi = mod2pi(phi + qc::PI, 0.); + auto new_lam = mod2pi(lam + qc::PI, 0.); + return { + theta, + new_phi, + new_lam, + phase + (new_phi + new_lam - phi - lam) / 2., + }; + } static constexpr std::complex C_ZERO{0., 0.}; static constexpr std::complex C_ONE{1., 0.}; @@ -316,6 +436,13 @@ struct GateDecompositionPattern final static constexpr std::complex IM{0., 1.}; static constexpr std::complex M_IM{0., -1.}; + static constexpr std::array, 4> IPZ = {IM, C_ZERO, + C_ZERO, M_IM}; + static constexpr std::array, 4> IPY = {C_ZERO, C_ONE, + C_M_ONE, C_ZERO}; + static constexpr std::array, 4> IPX = {C_ZERO, IM, IM, + C_ZERO}; + struct TwoQubitWeylDecomposition { qc::fp a; qc::fp b; @@ -336,13 +463,6 @@ struct GateDecompositionPattern final std::optional fidelity, std::optional _specialization) { - constexpr std::array, 4> IPZ = {IM, C_ZERO, C_ZERO, - M_IM}; - constexpr std::array, 4> IPY = {C_ZERO, C_ONE, - C_M_ONE, C_ZERO}; - constexpr std::array, 4> IPX = {C_ZERO, IM, IM, - C_ZERO}; - auto& u = unitary_matrix; auto det_u = determinant(u); auto det_pow = std::pow(det_u, static_cast(-0.25)); @@ -837,6 +957,7 @@ struct GateDecompositionPattern final if (specialization == Specialization::General) { return general; } + throw std::logic_error{"Unknown specialization"}; }; TwoQubitWeylDecomposition specialized = get_specialized_decomposition(); @@ -877,7 +998,7 @@ struct GateDecompositionPattern final struct TwoQubitBasisDecomposer { qc::OpType gate; - std::optional gate_params; + std::vector gate_params; qc::fp basis_fidelity; EulerBasis euler_basis; std::optional pulse_optimize; @@ -903,17 +1024,166 @@ struct GateDecompositionPattern final matrix2x2 q2l; matrix2x2 q2r; - struct QubitGateSequence { - struct Gate { - qc::OpType type{qc::I}; - std::optional parameter; - std::vector qubit_id = {0}; + TwoQubitBasisDecomposer + new_inner(qc::OpType gate = qc::X, // CX + const std::vector& gate_params = {}, + matrix4x4 gate_matrix = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, + 1, 0}, // CX matrix + qc::fp basis_fidelity = 1.0, + EulerBasis euler_basis = EulerBasis::ZYZ, + std::optional pulse_optimize = std::nullopt) { + auto relative_eq = [](auto&& lhs, auto&& rhs, auto&& epsilon, + auto&& max_relative) { + // Handle same infinities + if (lhs == rhs) { + return true; + } + + // Handle remaining infinities + if (std::isinf(lhs) || std::isinf(rhs)) { + return false; + } + + auto abs_diff = std::abs(lhs - rhs); + + // For when the numbers are really close together + if (abs_diff <= epsilon) { + return true; + } + + auto abs_lhs = std::abs(lhs); + auto abs_rhs = std::abs(rhs); + if (abs_rhs > abs_lhs) { + return abs_diff <= abs_rhs * max_relative; + } + return abs_diff <= abs_lhs * max_relative; }; - std::vector gates; - qc::fp globalPhase; - }; - using OneQubitGateSequence = QubitGateSequence; - using TwoQubitGateSequence = QubitGateSequence; + constexpr auto FRAC_1_SQRT_2 = 0.707106781186547524400844362104849039; + constexpr auto K12R_ARR = std::array{ + qfp(0., FRAC_1_SQRT_2), + qfp(FRAC_1_SQRT_2, 0.), + qfp(-FRAC_1_SQRT_2, 0.), + qfp(0., -FRAC_1_SQRT_2), + }; + constexpr auto K12L_ARR = std::array{ + qfp(0.5, 0.5), + qfp(0.5, 0.5), + qfp(-0.5, 0.5), + qfp(0.5, -0.5), + }; + + auto basis_decomposer = TwoQubitWeylDecomposition::new_inner( + gate_matrix, DEFAULT_FIDELITY, std::nullopt); + auto super_controlled = + relative_eq(basis_decomposer.a, qc::PI_4, + std::numeric_limits::epsilon(), 1e-09) && + relative_eq(basis_decomposer.c, 0.0, + std::numeric_limits::epsilon(), 1e-09); + + // Create some useful matrices U1, U2, U3 are equivalent to the basis, + // expand as Ui = Ki1.Ubasis.Ki2 + auto b = basis_decomposer.b; + auto temp = qfp(0.5, -0.5); + auto k11l = std::array{ + temp * (M_IM * std::exp(qfp(0., -b))), temp * std::exp(qfp(0., -b)), + temp * (M_IM * std::exp(qfp(0., b))), temp * -(std::exp(qfp(0., b)))}; + auto k11r = std::array{FRAC_1_SQRT_2 * std::exp((IM * qfp(0., -b))), + FRAC_1_SQRT_2 * -std::exp(qfp(0., -b)), + FRAC_1_SQRT_2 * std::exp(qfp(0., b)), + FRAC_1_SQRT_2 * (M_IM * std::exp(qfp(0., b)))}; + auto k32l_k21l = std::array{FRAC_1_SQRT_2 * std::cos(qfp(1., (2. * b))), + FRAC_1_SQRT_2 * (IM * std::sin((2. * b))), + FRAC_1_SQRT_2 * (IM * std::sin(2. * b)), + FRAC_1_SQRT_2 * qfp(1., -std::cos(2. * b))}; + temp = qfp(0.5, 0.5); + auto k21r = std::array{ + temp * (M_IM * std::exp(qfp(0., -2. * b))), + temp * std::exp(qfp(0., -2. * b)), + temp * (IM * std::exp(qfp(0., 2. * b))), + temp * std::exp(qfp(0., 2. * b)), + }; + constexpr auto K22L_ARR = std::array{ + qfp(FRAC_1_SQRT_2, 0.), + qfp(-FRAC_1_SQRT_2, 0.), + qfp(FRAC_1_SQRT_2, 0.), + qfp(FRAC_1_SQRT_2, 0.), + }; + constexpr auto K22R_ARR = std::array{C_ZERO, C_ONE, C_M_ONE, C_ZERO}; + auto k31l = std::array{ + FRAC_1_SQRT_2 * std::exp(qfp(0., -b)), + FRAC_1_SQRT_2 * std::exp(qfp(0., -b)), + FRAC_1_SQRT_2 * -std::exp(qfp(0., b)), + FRAC_1_SQRT_2 * std::exp(qfp(0., b)), + }; + auto k31r = std::array{ + IM * std::exp(qfp(0., b)), + C_ZERO, + C_ZERO, + M_IM * std::exp(qfp(0., -b)), + }; + temp = qfp(0.5, 0.5); + auto k32r = std::array{ + temp * std::exp(qfp(0., b)), + temp * -std::exp(qfp(0., -b)), + temp * (M_IM * std::exp(qfp(0., b))), + temp * (M_IM * std::exp(qfp(0., -b))), + }; + auto k1ld = transpose_conjugate(basis_decomposer.K1l); + auto k1rd = transpose_conjugate(basis_decomposer.K1r); + auto k2ld = transpose_conjugate(basis_decomposer.K2l); + auto k2rd = transpose_conjugate(basis_decomposer.K2r); + // Pre-build the fixed parts of the matrices used in 3-part decomposition + auto u0l = dot(k31l, k1ld); + auto u0r = dot(k31r, k1rd); + auto u1l = dot(dot(k2ld, k32l_k21l), k1ld); + auto u1ra = dot(k2rd, k32r); + auto u1rb = dot(k21r, k1rd); + auto u2la = dot(k2ld, K22L_ARR); + auto u2lb = dot(k11l, k1ld); + auto u2ra = dot(k2rd, K22R_ARR); + auto u2rb = dot(k11r, k1rd); + auto u3l = dot(k2ld, K12L_ARR); + auto u3r = dot(k2rd, K12R_ARR); + // Pre-build the fixed parts of the matrices used in the 2-part + // decomposition + auto q0l = dot(transpose_conjugate(K12L_ARR), k1ld); + auto q0r = dot(dot(transpose_conjugate(K12R_ARR), IPZ), k1rd); + auto q1la = dot(k2ld, transpose_conjugate(k11l)); + auto q1lb = dot(k11l, k1ld); + auto q1ra = dot(dot(k2rd, IPZ), transpose_conjugate(k11r)); + auto q1rb = dot(k11r, k1rd); + auto q2l = dot(k2ld, K12L_ARR); + auto q2r = dot(k2rd, K12R_ARR); + + return TwoQubitBasisDecomposer{ + gate, + gate_params, + basis_fidelity, + euler_basis, + pulse_optimize, + basis_decomposer, + super_controlled, + u0l, + u0r, + u1l, + u1ra, + u1rb, + u2la, + u2lb, + u2ra, + u2rb, + u3l, + u3r, + q0l, + q0r, + q1la, + q1lb, + q1ra, + q1rb, + q2l, + q2r, + }; + } std::optional twoQubitDecompose(const matrix4x4& unitaryMatrix, @@ -971,6 +1241,7 @@ struct GateDecompositionPattern final decomp, target_1q_basis_list, 0, {}, true, std::nullopt); euler_decompositions.push_back(euler_decomp); } + TwoQubitGateSequence gates{.globalPhase = target_decomposed.global_phase}; // Worst case length is 5x 1q gates for each 1q decomposition + 1x 2q gate // We might overallocate a bit if the euler basis is different but // the worst case is just 16 extra elements with just a String and 2 @@ -978,7 +1249,6 @@ struct GateDecompositionPattern final // aren't long lived and are just used to create a QuantumCircuit or // DAGCircuit when we return to Python space. constexpr auto TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY = 21; - TwoQubitGateSequence gates{.globalPhase = target_decomposed.global_phase}; gates.gates.reserve(TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY); gates.globalPhase -= best_nbasis * basis_decomposer.global_phase; if (best_nbasis == 2) { @@ -1022,13 +1292,6 @@ struct GateDecompositionPattern final [[nodiscard]] std::vector decomp1_inner(const TwoQubitWeylDecomposition& target) const { - auto transpose_conjugate = [](auto&& matrix) { - auto result = transpose(matrix); - llvm::transform(result, result.begin(), - [](auto&& x) { return std::conj(x); }); - return result; - }; - // FIXME: fix for z!=0 and c!=0 using closest reflection (not always in // the Weyl chamber) return { @@ -1081,7 +1344,7 @@ struct GateDecompositionPattern final return std::nullopt; } - if (gate != "cx") { + if (gate != qc::X) { // CX if (pulse_optimize.has_value()) { throw std::runtime_error{ "pulse_optimizer currently only works with CNOT entangling gate"}; @@ -1108,7 +1371,7 @@ struct GateDecompositionPattern final return {qfp{0.5, 0.5}, qfp{0.5, -0.5}, qfp{0.5, -0.5}, qfp{0.5, 0.5}}; } if (gate.type == qc::RZ) { - return rz_matrix(gate.parameter.value()); + return rz_matrix(gate.parameter.at(0)); } if (gate.type == qc::X) { return {0, 1, 1, 0}; @@ -1190,11 +1453,11 @@ struct GateDecompositionPattern final {.type = qc::X, .qubit_id = {0, 1}}); // TODO: mark CX somehow? gates.gates.push_back({.type = qc::SX, .qubit_id = {0}}); gates.gates.push_back({.type = qc::RZ, - .parameter = euler_q0[1][1] - qc::PI, + .parameter = {euler_q0[1][1] - qc::PI}, .qubit_id = {0}}); gates.gates.push_back({.type = qc::SX, .qubit_id = {0}}); gates.gates.push_back( - {.type = qc::RZ, .parameter = euler_q1[1][1], .qubit_id = {1}}); + {.type = qc::RZ, .parameter = {euler_q1[1][1]}, .qubit_id = {1}}); gates.globalPhase += qc::PI_2; gates.gates.push_back( {.type = qc::X, .qubit_id = {0, 1}}); // TODO: mark CX somehow? @@ -1438,8 +1701,8 @@ struct GateDecompositionPattern final bestCircuit = circuit; minError = error; } - return bestCircuit; } + return bestCircuit; } // TODO: copied+adapted from single-qubit decomposition @@ -1465,23 +1728,6 @@ struct GateDecompositionPattern final angleZeroEpsilon = -1.0; } - auto remEuclid = [](qc::fp a, qc::fp b) { - auto r = std::fmod(a, b); - return (r < 0.0) ? r + std::abs(b) : r; - }; - // Wrap angle into interval [-π,π). If within atol of the endpoint, clamp - // to -π - auto mod2pi = [&](qc::fp angle) -> qc::fp { - // remEuclid() isn't exactly the same as Python's % operator, but - // because the RHS here is a constant and positive it is effectively - // equivalent for this case - auto wrapped = remEuclid(angle + qc::PI, 2. * qc::PI) - qc::PI; - if (std::abs(wrapped - qc::PI) < angleZeroEpsilon) { - return -qc::PI; - } - return wrapped; - }; - qc::fp globalPhase = phase - ((phi + lambda) / 2.); std::vector gates; @@ -1489,7 +1735,7 @@ struct GateDecompositionPattern final lambda += phi; lambda = mod2pi(lambda); if (std::abs(lambda) > angleZeroEpsilon) { - gates.push_back({kGate, lambda}); + gates.push_back({kGate, {lambda}}); globalPhase += lambda / 2.0; } return {gates, globalPhase}; @@ -1509,13 +1755,13 @@ struct GateDecompositionPattern final lambda = mod2pi(lambda); if (std::abs(lambda) > angleZeroEpsilon) { globalPhase += lambda / 2.0; - gates.push_back({kGate, lambda}); + gates.push_back({kGate, {lambda}}); } - gates.push_back({aGate, theta}); + gates.push_back({aGate, {theta}}); phi = mod2pi(phi); if (std::abs(phi) > angleZeroEpsilon) { globalPhase += phi / 2.0; - gates.push_back({kGate, phi}); + gates.push_back({kGate, {phi}}); } return {gates, globalPhase}; } diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index f4976b44e1..66936284c4 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -21,6 +21,14 @@ namespace mqt::ir::opt::helpers { +inline auto flatten(const dd::TwoQubitGateMatrix& matrix) { + std::array, 16> result; + for (std::size_t i = 0; i < result.size(); ++i) { + result[i] = matrix[i / 4][i % 4]; + } + return result; +} + std::optional mlirValueToFp(mlir::Value value); template @@ -127,7 +135,6 @@ inline std::optional mlirValueToFp(mlir::Value value) { auto&& outQubits = op.getOutQubits(); bool isSingleQubitOp = inQubits.size() == 1 && outQubits.size() == 1 && !op.isControlled(); - assert(isSingleQubitOp == qc::isSingleQubitGate(getQcType(op))); return isSingleQubitOp; } @@ -143,7 +150,6 @@ inline std::optional mlirValueToFp(mlir::Value value) { auto outQubitSize = outQubits.size() + outPosCtrlQubits.size() + outNegCtrlQubits.size(); bool isTwoQubitOp = inQubitSize == 2 && outQubitSize == 2; - assert(isTwoQubitOp == qc::isTwoQubitGate(getQcType(op))); return isTwoQubitOp; } @@ -154,6 +160,9 @@ getUnitaryMatrix(UnitaryInterface op) { if (isTwoQubitOperation(op)) { return dd::opToTwoQubitGateMatrix(type, parameters); + } else if (isSingleQubitOperation(op)) { + auto matrix = dd::opToSingleQubitGateMatrix(type, parameters); + // TODO } return std::nullopt; } @@ -164,6 +173,20 @@ getUnitaryMatrix(UnitaryInterface op) { factor * matrix.at(3)}; } +[[nodiscard]] inline auto +multiply(const std::array, 16>& lhs, + const std::array, 16>& rhs) { + std::array, 16> result; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + for (int k = 0; k < 4; k++) { + result[i * 4 + j] += lhs[i * 4 + k] * rhs[k * 4 + j]; + } + } + } + return result; +} + [[nodiscard]] inline dd::TwoQubitGateMatrix kroneckerProduct(dd::GateMatrix lhs, dd::GateMatrix rhs) { return {multiply(lhs.at(0), rhs), multiply(lhs.at(1), rhs), From 588db2023805998b4a348d9ba41a4d9a5017ded3 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 15 Oct 2025 20:30:32 +0200 Subject: [PATCH 011/237] tmp --- .../MQTOpt/Transforms/GateDecomposition.cpp | 6 +- .../Transforms/GateDecompositionPattern.cpp | 391 ++++++++++++------ mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 102 +++-- 3 files changed, 353 insertions(+), 146 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp index a612aca957..a5c039568d 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp @@ -35,8 +35,12 @@ struct GateDecomposition final mlir::RewritePatternSet patterns(ctx); populateGateDecompositionPatterns(patterns); + // Configure greedy driver + mlir::GreedyRewriteConfig config; + config.setUseTopDownTraversal(true); + // Apply patterns in an iterative and greedy manner. - if (mlir::failed(mlir::applyPatternsGreedily(op, std::move(patterns)))) { + if (mlir::failed(mlir::applyPatternsGreedily(op, std::move(patterns), config))) { signalPassFailure(); } } diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 7f2ebb5418..1afe8d2e6d 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -36,9 +36,6 @@ struct GateDecompositionPattern final explicit GateDecompositionPattern(mlir::MLIRContext* context) : OpInterfaceRewritePattern(context) {} - dd::TwoQubitGateMatrix twoQubitIdentity = { - {{1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}}; - mlir::LogicalResult matchAndRewrite(UnitaryInterface op, mlir::PatternRewriter& rewriter) const override { @@ -49,18 +46,17 @@ struct GateDecompositionPattern final return mlir::failure(); } - matrix4x4 unitaryMatrix = - helpers::flatten(dd::opToTwoQubitGateMatrix(qc::I)); + matrix4x4 unitaryMatrix = kroneckerProduct(identityGate, identityGate); for (auto&& gate : series) { - if (auto gateMatrix = helpers::getUnitaryMatrix(gate)) { - unitaryMatrix = - helpers::multiply(unitaryMatrix, helpers::flatten(*gateMatrix)); - } + auto gateMatrix = getTwoQubitMatrix({.type = helpers::getQcType(gate), + .parameter = {/*TODO*/}, + .qubit_id = {0, 1}}); + unitaryMatrix = helpers::multiply(unitaryMatrix, gateMatrix); } - TwoQubitBasisDecomposer decomposer; - auto sequence = decomposer.twoQubitDecompose(unitaryMatrix, DEFAULT_FIDELITY, true, - std::nullopt); + auto decomposer = TwoQubitBasisDecomposer::new_inner(); + auto sequence = decomposer.twoQubitDecompose( + unitaryMatrix, DEFAULT_FIDELITY, true, std::nullopt); if (!sequence) { return mlir::failure(); } @@ -84,42 +80,50 @@ struct GateDecompositionPattern final } result.push_back(op); - while (true) { - for (auto&& user : llvm::concat(qubits[0].getUsers(), - qubits[1].getUsers())) { - auto userUnitary = mlir::dyn_cast(user); - if (!userUnitary) { - return result; - } + auto findNextInSeries = [&](mlir::Operation* user) { + auto userUnitary = mlir::dyn_cast(user); + if (!userUnitary) { + return false; + } - if (helpers::isSingleQubitOperation(userUnitary)) { - auto&& operand = userUnitary->getOperand(0); - auto* it = llvm::find(qubits, operand); - if (it == qubits.end()) { - throw std::logic_error{"Operand of single-qubit op and user of " - "qubit is not in qubits"}; - } - *it = userUnitary->getResult(0); - - result.push_back(userUnitary); - } else if (helpers::isTwoQubitOperation(userUnitary)) { - auto&& firstOperand = userUnitary->getOperand(0); - auto&& secondOperand = userUnitary->getOperand(1); - auto* firstQubitIt = llvm::find(qubits, firstOperand); - auto* secondQubitIt = llvm::find(qubits, secondOperand); - if (firstQubitIt == qubits.end() || secondQubitIt == qubits.end()) { - return result; - } - *firstQubitIt = userUnitary->getResult(0); - *secondQubitIt = userUnitary->getResult(1); + if (helpers::isSingleQubitOperation(userUnitary)) { + auto&& operand = userUnitary->getOperand(0); + auto* it = llvm::find(qubits, operand); + if (it == qubits.end()) { + throw std::logic_error{"Operand of single-qubit op and user of " + "qubit is not in qubits"}; + } + *it = userUnitary->getResult(0); - result.push_back(userUnitary); - } else { - return result; + result.push_back(userUnitary); + return true; + } + if (helpers::isTwoQubitOperation(userUnitary)) { + auto&& firstOperand = userUnitary->getOperand(0); + auto&& secondOperand = userUnitary->getOperand(1); + auto* firstQubitIt = llvm::find(qubits, firstOperand); + auto* secondQubitIt = llvm::find(qubits, secondOperand); + if (firstQubitIt == qubits.end() || secondQubitIt == qubits.end()) { + return false; } + *firstQubitIt = userUnitary->getResult(0); + *secondQubitIt = userUnitary->getResult(1); + + result.push_back(userUnitary); + return true; + } + return false; + }; + + while (true) { + assert(qubits[0].hasOneUse()); + assert(qubits[1].hasOneUse()); + bool isSeriesOngoing = findNextInSeries(*qubits[0].getUsers().begin()) || + findNextInSeries(*qubits[1].getUsers().begin()); + if (!isSeriesOngoing) { + return result; } } - return result; } /** @@ -133,7 +137,8 @@ struct GateDecompositionPattern final template static OpType createOneParameterGate(mlir::PatternRewriter& rewriter, mlir::Location location, - qc::fp parameter, mlir::ValueRange inQubits) { + qc::fp parameter, + mlir::ValueRange inQubits) { auto parameterValue = rewriter.create( location, rewriter.getF64Type(), rewriter.getF64FloatAttr(parameter)); @@ -144,22 +149,24 @@ struct GateDecompositionPattern final mlir::ValueRange{}); } - - struct QubitGateSequence { - struct Gate { - qc::OpType type{qc::I}; - std::vector parameter; - std::vector qubit_id = {0}; - }; - std::vector gates; - qc::fp globalPhase; + struct QubitGateSequence { + struct Gate { + qc::OpType type{qc::I}; + std::vector parameter; + std::vector qubit_id = {0}; }; - using OneQubitGateSequence = QubitGateSequence; - using TwoQubitGateSequence = QubitGateSequence; + std::vector gates; + qc::fp globalPhase; + }; + using OneQubitGateSequence = QubitGateSequence; + using TwoQubitGateSequence = QubitGateSequence; - static void applySeries(mlir::PatternRewriter& rewriter, const llvm::SmallVector& series, const TwoQubitGateSequence& sequence) { + static void applySeries(mlir::PatternRewriter& rewriter, + const llvm::SmallVector& series, + const TwoQubitGateSequence& sequence) { if (sequence.globalPhase != 0.0) { - createOneParameterGate(rewriter, series[0]->getLoc(), sequence.globalPhase, {}); + createOneParameterGate(rewriter, series[0]->getLoc(), + sequence.globalPhase, {}); } } @@ -230,14 +237,25 @@ struct GateDecompositionPattern final } static matrix2x2 dot(const matrix2x2& lhs, const matrix2x2& rhs) { - return lhs; + return helpers::multiply(lhs, rhs); } static matrix4x4 dot(const matrix4x4& lhs, const matrix4x4& rhs) { - return lhs; + return helpers::multiply(lhs, rhs); } - static matrix2x2 transpose(const matrix2x2& x) { return x; } - static matrix4x4 transpose(const matrix4x4& x) { return x; } + static matrix2x2 transpose(const matrix2x2& matrix) { + return {matrix[0 * 2 + 0], matrix[1 * 2 + 0], matrix[0 * 2 + 1], + matrix[1 * 2 + 1]}; + } + static matrix4x4 transpose(const matrix4x4& x) { + matrix4x4 result; + for (size_t i = 0; i < 4; ++i) { + for (size_t j = 0; j < 4; ++j) { + result[j * 4 + i] = x[i * 4 + j]; + } + } + return result; + } static matrix2x2 transpose_conjugate(const matrix2x2& matrix) { auto result = transpose(matrix); @@ -246,8 +264,60 @@ struct GateDecompositionPattern final return result; }; - static qfp determinant(const matrix2x2& x) { return 0.0; }; - static qfp determinant(const matrix4x4& x) { return 0.0; }; + static qfp determinant(const matrix2x2& mat) { + return mat[0] * mat[3] - mat[1] * mat[2]; + } + + static qfp determinant(const std::array& mat) { + return mat[0] * (mat[4] * mat[8] - mat[5] * mat[7]) - + mat[1] * (mat[3] * mat[8] - mat[5] * mat[6]) + + mat[2] * (mat[3] * mat[7] - mat[4] * mat[6]); + } + + static std::array get3x3Submatrix(const matrix4x4& mat, + int rowToBeRemoved, + int columnToBeRemoved) { + std::array, 9> result; + int subIndex = 0; + for (int i = 0; i < 4; ++i) { + if (i != rowToBeRemoved) { + for (int j = 0; j < 4; ++j) { + if (j != columnToBeRemoved) { + result[subIndex++] = mat[i * 4 + j]; + } + } + } + } + return result; + } + + static qfp determinant(const matrix4x4& mat) { + auto [l, u, rowPermutations] = helpers::LUdecomposition(mat); + auto det = C_ONE; + for (int i = 0; i < 4; ++i) { + det *= l[i * 4 + i]; + } + + if (rowPermutations % 2 != 0) { + det = -det; + } + return det; + + // auto det = -C_ZERO; + // for (int column = 0; column < 4; ++column) { + // auto submatrix = get3x3Submatrix(mat, 0, column); + // auto subDet = determinant(submatrix); + // auto tmp = mat[0 * 4 + column] * subDet; + // if (column % 2 == 0 && + // tmp != + // C_ZERO) { // TODO: better way to get negative 0.0 in determinant? + // det += tmp; + // } else if (tmp != -C_ZERO) { + // det -= tmp; + // } + // } + // return det; + } static matrix2x2 multiply(qfp factor, matrix2x2 matrix) { llvm::transform(matrix, matrix.begin(), @@ -270,15 +340,85 @@ struct GateDecompositionPattern final second_quadrant[0 * 2 + 0], second_quadrant[0 * 2 + 1], first_quadrant[1 * 2 + 0], first_quadrant[1 * 2 + 1], second_quadrant[1 * 2 + 0], second_quadrant[1 * 2 + 1], - third_quadrant[0 * 2 + 0], first_quadrant[0 * 2 + 1], + third_quadrant[0 * 2 + 0], third_quadrant[0 * 2 + 1], fourth_quadrant[0 * 2 + 0], fourth_quadrant[0 * 2 + 1], - third_quadrant[1 * 2 + 0], first_quadrant[1 * 2 + 1], + third_quadrant[1 * 2 + 0], third_quadrant[1 * 2 + 1], fourth_quadrant[1 * 2 + 0], fourth_quadrant[1 * 2 + 1], }; } + // Helper function to perform QR decomposition (simplified) + static void qrDecomposition(std::array& A, + std::array& Q, + std::array& R) { + // QR decomposition is simplified for this 4x4 case + for (size_t i = 0; i < 4; ++i) { + qc::fp norm = 0.0; + for (size_t j = 0; j < 4; ++j) { + norm += A[j * 4 + i] * A[j * 4 + i]; + } + norm = std::sqrt(norm); + + // Normalize the column + for (size_t j = 0; j < 4; ++j) { + Q[j * 4 + i] = A[j * 4 + i] / norm; + } + + // Compute the R matrix + for (size_t j = i; j < 4; ++j) { + qc::fp dot = 0.0; + for (size_t k = 0; k < 4; ++k) { + dot += Q[k * 4 + i] * A[k * 4 + j]; + } + R[i * 4 + j] = dot; + } + + // Subtract to make A orthogonal + for (size_t j = 0; j < 4; ++j) { + for (size_t k = i; k < 4; ++k) { + A[j * 4 + k] -= Q[j * 4 + i] * R[i * 4 + k]; + } + } + } + } + + // Function to perform self-adjoint eigenvalue decomposition (4x4 matrix) + static std::array // eigenvectors (4x4) + self_adjoint_evd(std::array& A, // input symmetric matrix (4x4) + std::array& s // eigenvalues + ) { + // Step 1: Zero out the upper triangle (we are only interested in the lower + // half) + for (size_t i = 0; i < 4; ++i) { + for (size_t j = i + 1; j < 4; ++j) { + A[i * 4 + j] = 0; // Set the upper triangle to zero + } + } + + // Step 2: Perform QR decomposition + std::array Q, R; + for (int iter = 0; iter < 1000; + ++iter) { // Arbitrary number of iterations for convergence + qrDecomposition(A, Q, R); + A = helpers::multiply(R, Q); // Update A = R * Q + } + + // Step 3: Extract eigenvalues (diagonal of the matrix) + for (size_t i = 0; i < 4; ++i) { + s[i] = A[i * 4 + i]; // Eigenvalues are the diagonal elements + } + + return Q; + } + // https://docs.rs/faer/latest/faer/mat/generic/struct.Mat.html#method.self_adjoint_eigen - static matrix4x4 self_adjoint_eigen_lower(const matrix4x4& x) { return x; } + static std::array + self_adjoint_eigen_lower(std::array A) { + std::array S; + auto U = self_adjoint_evd(A, S); + + return U; + } static std::tuple decompose_two_qubit_product_gate(matrix4x4 special_unitary) { @@ -299,9 +439,7 @@ struct GateDecompositionPattern final llvm::transform(r, r.begin(), [&](auto&& x) { return x / std::sqrt(det_r); }); // transpose with complex conjugate of each element - matrix2x2 r_t_conj; - llvm::transform(transpose(r), r_t_conj.begin(), - [](auto&& x) { return std::conj(x); }); + matrix2x2 r_t_conj = transpose_conjugate(r); auto temp = kroneckerProduct(identityGate, r_t_conj); temp = dot(special_unitary, temp); @@ -443,6 +581,48 @@ struct GateDecompositionPattern final static constexpr std::array, 4> IPX = {C_ZERO, IM, IM, C_ZERO}; + static matrix2x2 getSingleQubitMatrix(const QubitGateSequence::Gate& gate) { + if (gate.type == qc::SX) { + return {qfp{0.5, 0.5}, qfp{0.5, -0.5}, qfp{0.5, -0.5}, qfp{0.5, 0.5}}; + } + if (gate.type == qc::RZ) { + return rz_matrix(gate.parameter.at(0)); + } + if (gate.type == qc::X) { + return {0, 1, 1, 0}; + } + throw std::invalid_argument{ + "unsupported gate type for single qubit matrix"}; + } + + static matrix4x4 getTwoQubitMatrix(const QubitGateSequence::Gate& gate) { + if (gate.qubit_id.empty()) { + return kroneckerProduct(identityGate, identityGate); + } + if (gate.qubit_id.size() == 1) { + if (gate.qubit_id[0] == 0) { + return kroneckerProduct(identityGate, getSingleQubitMatrix(gate)); + } + if (gate.qubit_id[0] == 1) { + return kroneckerProduct(getSingleQubitMatrix(gate), identityGate); + } + throw std::logic_error{"Invalid qubit ID in compute_unitary"}; + } + if (gate.qubit_id.size() == 2) { + if (gate.type == qc::X) { + // controlled X (CX) + if (gate.qubit_id == std::vector{0, 1}) { + return {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0}; + } + if (gate.qubit_id == std::vector{1, 0}) { + return {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}; + } + } + throw std::invalid_argument{"unsupported gate type for two qubit matrix"}; + } + throw std::logic_error{"Invalid number of qubit IDs in compute_unitary"}; + } + struct TwoQubitWeylDecomposition { qc::fp a; qc::fp b; @@ -459,18 +639,24 @@ struct GateDecompositionPattern final matrix4x4 unitary_matrix; static TwoQubitWeylDecomposition - new_inner(matrix4x4 unitary_matrix, - - std::optional fidelity, + new_inner(matrix4x4 unitary_matrix, std::optional fidelity, std::optional _specialization) { auto& u = unitary_matrix; auto det_u = determinant(u); - auto det_pow = std::pow(det_u, static_cast(-0.25)); + llvm::errs() << "DET_U: " << det_u.real() << 'i' << det_u.imag() << '\n'; + // auto cpow = [](auto&& x, auto&& y) { // TODO: remove + // auto mag = std::abs(x); + // auto phase = std::arg(x); + // return std::polar(std::pow(mag, y), phase * y); }; + auto det_pow = std::pow(det_u, -qc::fp(0.25)); + llvm::errs() << "DET_POW: " << det_pow.real() << 'i' << det_pow.imag() + << '\n'; llvm::transform(u, u.begin(), [&](auto&& x) { return x * det_pow; }); auto global_phase = std::arg(det_u) / 4.; auto u_p = magic_basis_transform(u, MagicBasisTransform::OutOf); auto m2 = dot(transpose(u_p), u_p); auto default_euler_basis = EulerBasis::ZYZ; + helpers::print(m2); // M2 is a symmetric complex matrix. We need to decompose it as M2 = P D // P^T where P ∈ SO(4), D is diagonal with unit-magnitude elements. @@ -489,8 +675,9 @@ struct GateDecompositionPattern final auto state = std::mt19937{2023}; std::normal_distribution dist; auto found = false; - diagonal4x4 d; - matrix4x4 p; + diagonal4x4 d{{C_ZERO}}; + matrix4x4 p{{C_ZERO}}; + for (int i = 0; i < 100; ++i) { qc::fp rand_a; qc::fp rand_b; @@ -506,11 +693,15 @@ struct GateDecompositionPattern final rand_a = dist(state); rand_b = dist(state); } - matrix4x4 m2_real; + std::array m2_real; llvm::transform(m2, m2_real.begin(), [&](const qfp& val) { return rand_a * val.real() + rand_b * val.imag(); }); - matrix4x4 p_inner = self_adjoint_eigen_lower(m2_real); + auto p_inner_real = + std::get<1>(helpers::LUdecomposition(self_adjoint_eigen_lower(m2_real))); + matrix4x4 p_inner; + llvm::transform(p_inner_real, p_inner.begin(), + [](auto&& x) { return qfp(x, 0.0); }); auto d_inner = diagonal(dot(dot(transpose(p_inner), m2), p_inner)); matrix4x4 diag_d{}; // zero initialization diag_d[0 * 4 + 0] = d_inner[0]; @@ -1024,7 +1215,8 @@ struct GateDecompositionPattern final matrix2x2 q2l; matrix2x2 q2r; - TwoQubitBasisDecomposer + public: + static TwoQubitBasisDecomposer new_inner(qc::OpType gate = qc::X, // CX const std::vector& gate_params = {}, matrix4x4 gate_matrix = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, @@ -1366,49 +1558,6 @@ struct GateDecompositionPattern final return result; } - matrix2x2 getSingleQubitMatrix(const QubitGateSequence::Gate& gate) { - if (gate.type == qc::SX) { - return {qfp{0.5, 0.5}, qfp{0.5, -0.5}, qfp{0.5, -0.5}, qfp{0.5, 0.5}}; - } - if (gate.type == qc::RZ) { - return rz_matrix(gate.parameter.at(0)); - } - if (gate.type == qc::X) { - return {0, 1, 1, 0}; - } - throw std::invalid_argument{ - "unsupported gate type for single qubit matrix"}; - } - - matrix4x4 getTwoQubitMatrix(const QubitGateSequence::Gate& gate) { - if (gate.qubit_id.empty()) { - return kroneckerProduct(identityGate, identityGate); - } else if (gate.qubit_id.size() == 1) { - if (gate.qubit_id[0] == 0) { - return kroneckerProduct(identityGate, getSingleQubitMatrix(gate)); - } else if (gate.qubit_id[0] == 1) { - return kroneckerProduct(getSingleQubitMatrix(gate), identityGate); - } else { - throw std::logic_error{"Invalid qubit ID in compute_unitary"}; - } - } else if (gate.qubit_id.size() == 2) { - if (gate.type == qc::X) { - // controlled X (CX) - if (gate.qubit_id == std::vector{0, 1}) { - return {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0}; - } - if (gate.qubit_id == std::vector{1, 0}) { - return {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}; - } - } - throw std::invalid_argument{ - "unsupported gate type for two qubit matrix"}; - } else { - throw std::logic_error{ - "Invalid number of qubit IDs in compute_unitary"}; - } - } - /// /// Decomposition of SU(4) gate for device with SX, virtual RZ, and CNOT /// gates assuming two CNOT gates are needed. diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index 66936284c4..e949518049 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -21,6 +21,20 @@ namespace mqt::ir::opt::helpers { +// TODO: remove + template + void print(std::array, N> matrix) { + int i{}; + for (auto&& a : matrix) { + llvm::errs() << a.real() << 'i' << a.imag() << ' '; + if (++i % 4 == 0) { + llvm::errs() << '\n'; + } + } + llvm::errs() << '\n'; + + } + inline auto flatten(const dd::TwoQubitGateMatrix& matrix) { std::array, 16> result; for (std::size_t i = 0; i < result.size(); ++i) { @@ -153,34 +167,22 @@ inline std::optional mlirValueToFp(mlir::Value value) { return isTwoQubitOp; } -[[nodiscard]] inline std::optional -getUnitaryMatrix(UnitaryInterface op) { - auto type = getQcType(op); - auto parameters = getParameters(op); - - if (isTwoQubitOperation(op)) { - return dd::opToTwoQubitGateMatrix(type, parameters); - } else if (isSingleQubitOperation(op)) { - auto matrix = dd::opToSingleQubitGateMatrix(type, parameters); - // TODO - } - return std::nullopt; -} - [[nodiscard]] inline dd::GateMatrix multiply(std::complex factor, dd::GateMatrix matrix) { return {factor * matrix.at(0), factor * matrix.at(1), factor * matrix.at(2), factor * matrix.at(3)}; } +template [[nodiscard]] inline auto -multiply(const std::array, 16>& lhs, - const std::array, 16>& rhs) { - std::array, 16> result; - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - for (int k = 0; k < 4; k++) { - result[i * 4 + j] += lhs[i * 4 + k] * rhs[k * 4 + j]; +multiply(const std::array& lhs, + const std::array& rhs) { + std::array result; + const auto n = std::sqrt(N); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + for (int k = 0; k < n; k++) { + result[i * n + j] += lhs[i * n + k] * rhs[k * n + j]; } } } @@ -193,8 +195,60 @@ kroneckerProduct(dd::GateMatrix lhs, dd::GateMatrix rhs) { multiply(lhs.at(2), rhs), multiply(lhs.at(3), rhs)}; } -[[nodiscard]] inline dd::TwoQubitGateMatrix -multiply(dd::TwoQubitGateMatrix lhs, dd::TwoQubitGateMatrix rhs) { - return {}; +template +[[nodiscard]] inline auto LUdecomposition(std::array matrix) { + std::array L{}; + std::array U{}; + int rowPermutations = 0; + + for (int i = 0; i < 4; i++) { + // --- Partial pivoting: find max row in column i --- + int pivotRow = i; + auto maxVal = matrix[i * 4 + i]; + + for (int r = i + 1; r < 4; r++) { + auto val = matrix[r * 4 + i]; + if (std::abs(val) > std::abs(maxVal)) { + maxVal = val; + pivotRow = r; + } + } + + // --- Swap rows in matrix if needed --- + if (pivotRow != i) { + for (int col = 0; col < 4; ++col) { + std::swap(matrix[i * 4 + col], matrix[pivotRow * 4 + col]); + } + ++rowPermutations; + } + + // --- Compute L matrix (column-wise) --- + for (int j = 0; j < 4; j++) { + if (j < i) + L[j * 4 + i] = 0; + else { + L[j * 4 + i] = matrix[j * 4 + i]; + for (int k = 0; k < i; k++) { + L[j * 4 + i] -= L[j * 4 + k] * U[k * 4 + i]; + } + } + } + + // --- Compute U matrix (row-wise) --- + for (int j = 0; j < 4; j++) { + if (j < i) + U[i * 4 + j] = 0; + else if (j == i) + U[i * 4 + j] = 1; // Diagonal of U is set to 1 + else { + U[i * 4 + j] = matrix[i * 4 + j] / L[i * 4 + i]; + for (int k = 0; k < i; k++) { + U[i * 4 + j] -= (L[i * 4 + k] * U[k * 4 + j]) / L[i * 4 + i]; + } + } + } + } + + return std::make_tuple(L, U, rowPermutations); } } // namespace mqt::ir::opt::helpers From 84fa825997aa633dc6119a59c6b4dfa7642fea9f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 16 Oct 2025 20:05:00 +0200 Subject: [PATCH 012/237] switch to long double --- .../Transforms/GateDecompositionPattern.cpp | 213 +++++++++-------- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 216 ++++++++++-------- 2 files changed, 229 insertions(+), 200 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 1afe8d2e6d..85cf3ac4cc 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -137,7 +137,7 @@ struct GateDecompositionPattern final template static OpType createOneParameterGate(mlir::PatternRewriter& rewriter, mlir::Location location, - qc::fp parameter, + fp parameter, mlir::ValueRange inQubits) { auto parameterValue = rewriter.create( location, rewriter.getF64Type(), rewriter.getF64FloatAttr(parameter)); @@ -152,11 +152,11 @@ struct GateDecompositionPattern final struct QubitGateSequence { struct Gate { qc::OpType type{qc::I}; - std::vector parameter; + std::vector parameter; std::vector qubit_id = {0}; }; std::vector gates; - qc::fp globalPhase; + fp globalPhase; }; using OneQubitGateSequence = QubitGateSequence; using TwoQubitGateSequence = QubitGateSequence; @@ -205,27 +205,21 @@ struct GateDecompositionPattern final ZSX = 11, }; - using qfp = std::complex; - using diagonal4x4 = std::array; - using vector2d = std::vector; - using matrix2x2 = std::array; - using matrix4x4 = std::array; - - static constexpr auto sqrt2 = static_cast(1.4142135623730950488L); + static constexpr auto sqrt2 = static_cast(1.4142135623730950488L); static constexpr matrix2x2 identityGate = {1, 0, 0, 1}; static constexpr matrix2x2 hGate = {1.0 / sqrt2, 1.0 / sqrt2, 1.0 / sqrt2, -1.0 / sqrt2}; - static qc::fp remEuclid(qc::fp a, qc::fp b) { + static fp remEuclid(fp a, fp b) { auto r = std::fmod(a, b); return (r < 0.0) ? r + std::abs(b) : r; } // Wrap angle into interval [-π,π). If within atol of the endpoint, clamp // to -π - static qc::fp - mod2pi(qc::fp angle, - qc::fp angleZeroEpsilon = std::numeric_limits::epsilon()) { + static fp + mod2pi(fp angle, + fp angleZeroEpsilon = std::numeric_limits::epsilon()) { // remEuclid() isn't exactly the same as Python's % operator, but // because the RHS here is a constant and positive it is effectively // equivalent for this case @@ -247,11 +241,11 @@ struct GateDecompositionPattern final return {matrix[0 * 2 + 0], matrix[1 * 2 + 0], matrix[0 * 2 + 1], matrix[1 * 2 + 1]}; } - static matrix4x4 transpose(const matrix4x4& x) { + static matrix4x4 transpose(const matrix4x4& matrix) { matrix4x4 result; for (size_t i = 0; i < 4; ++i) { for (size_t j = 0; j < 4; ++j) { - result[j * 4 + i] = x[i * 4 + j]; + result[j * 4 + i] = matrix[i * 4 + j]; } } return result; @@ -277,7 +271,7 @@ struct GateDecompositionPattern final static std::array get3x3Submatrix(const matrix4x4& mat, int rowToBeRemoved, int columnToBeRemoved) { - std::array, 9> result; + std::array, 9> result; int subIndex = 0; for (int i = 0; i < 4; ++i) { if (i != rowToBeRemoved) { @@ -310,7 +304,8 @@ struct GateDecompositionPattern final // auto tmp = mat[0 * 4 + column] * subDet; // if (column % 2 == 0 && // tmp != - // C_ZERO) { // TODO: better way to get negative 0.0 in determinant? + // C_ZERO) { // TODO: better way to get negative 0.0 in + // determinant? // det += tmp; // } else if (tmp != -C_ZERO) { // det -= tmp; @@ -348,12 +343,12 @@ struct GateDecompositionPattern final } // Helper function to perform QR decomposition (simplified) - static void qrDecomposition(std::array& A, - std::array& Q, - std::array& R) { + static void qrDecomposition(std::array& A, + std::array& Q, + std::array& R) { // QR decomposition is simplified for this 4x4 case for (size_t i = 0; i < 4; ++i) { - qc::fp norm = 0.0; + fp norm = 0.0; for (size_t j = 0; j < 4; ++j) { norm += A[j * 4 + i] * A[j * 4 + i]; } @@ -366,7 +361,7 @@ struct GateDecompositionPattern final // Compute the R matrix for (size_t j = i; j < 4; ++j) { - qc::fp dot = 0.0; + fp dot = 0.0; for (size_t k = 0; k < 4; ++k) { dot += Q[k * 4 + i] * A[k * 4 + j]; } @@ -383,9 +378,9 @@ struct GateDecompositionPattern final } // Function to perform self-adjoint eigenvalue decomposition (4x4 matrix) - static std::array // eigenvectors (4x4) - self_adjoint_evd(std::array& A, // input symmetric matrix (4x4) - std::array& s // eigenvalues + static std::array // eigenvectors (4x4) + self_adjoint_evd(std::array& A, // input symmetric matrix (4x4) + std::array& s // eigenvalues ) { // Step 1: Zero out the upper triangle (we are only interested in the lower // half) @@ -396,7 +391,7 @@ struct GateDecompositionPattern final } // Step 2: Perform QR decomposition - std::array Q, R; + std::array Q, R; for (int iter = 0; iter < 1000; ++iter) { // Arbitrary number of iterations for convergence qrDecomposition(A, Q, R); @@ -412,15 +407,15 @@ struct GateDecompositionPattern final } // https://docs.rs/faer/latest/faer/mat/generic/struct.Mat.html#method.self_adjoint_eigen - static std::array - self_adjoint_eigen_lower(std::array A) { - std::array S; + static std::array + self_adjoint_eigen_lower(std::array A) { + std::array S; auto U = self_adjoint_evd(A, S); return U; } - static std::tuple + static std::tuple decompose_two_qubit_product_gate(matrix4x4 special_unitary) { // first quadrant matrix2x2 r = {special_unitary[0 * 4 + 0], special_unitary[0 * 4 + 1], @@ -489,38 +484,38 @@ struct GateDecompositionPattern final throw std::logic_error{"Unknown MagicBasisTransform direction!"}; } - static qc::fp trace_to_fid(const qfp& x) { + static fp trace_to_fid(const qfp& x) { auto x_abs = std::abs(x); return (4.0 + x_abs * x_abs) / 20.0; } - static qc::fp closest_partial_swap(qc::fp a, qc::fp b, qc::fp c) { + static fp closest_partial_swap(fp a, fp b, fp c) { auto m = (a + b + c) / 3.; auto [am, bm, cm] = std::array{a - m, b - m, c - m}; auto [ab, bc, ca] = std::array{a - b, b - c, c - a}; return m + am * bm * cm * (6. + ab * ab + bc * bc + ca * ca) / 18.; } - static matrix2x2 rx_matrix(qc::fp theta) { + static matrix2x2 rx_matrix(fp theta) { auto half_theta = theta / 2.; auto cos = qfp(std::cos(half_theta), 0.); auto isin = qfp(0., -std::sin(half_theta)); return {cos, isin, isin, cos}; } - static matrix2x2 ry_matrix(qc::fp theta) { + static matrix2x2 ry_matrix(fp theta) { auto half_theta = theta / 2.; auto cos = qfp(std::cos(half_theta), 0.); auto sin = qfp(std::sin(half_theta), 0.); return {cos, -sin, sin, cos}; } - static matrix2x2 rz_matrix(qc::fp theta) { + static matrix2x2 rz_matrix(fp theta) { auto ilam2 = qfp(0., 0.5 * theta); return {std::exp(-ilam2), C_ZERO, C_ZERO, std::exp(ilam2)}; } - static std::array angles_from_unitary(const matrix2x2& matrix, + static std::array angles_from_unitary(const matrix2x2& matrix, EulerBasis basis) { if (basis == EulerBasis::XYX) { return params_xyx_inner(matrix); @@ -528,7 +523,7 @@ struct GateDecompositionPattern final throw std::invalid_argument{"Unknown EulerBasis for angles_from_unitary"}; } - static std::array params_zyz_inner(const matrix2x2& matrix) { + static std::array params_zyz_inner(const matrix2x2& matrix) { auto getIndex = [](auto x, auto y) { return (y * 2) + x; }; auto determinant = [getIndex](auto&& matrix) { return (matrix.at(getIndex(0, 0)) * matrix.at(getIndex(1, 1))) - @@ -546,15 +541,15 @@ struct GateDecompositionPattern final return {theta, phi, lam, phase}; } - static std::array params_xyx_inner(const matrix2x2& matrix) { + static std::array params_xyx_inner(const matrix2x2& matrix) { auto mat_zyz = std::array{ - 0.5 * (matrix.at(0 * 2 + 0) + matrix.at(0 * 2 + 1 * 2 * 2) + + static_cast(0.5) * (matrix.at(0 * 2 + 0) + matrix.at(0 * 2 + 1 * 2 * 2) + matrix.at(1 * 2 + 0) + matrix.at(1 * 2 + 1)), - 0.5 * (matrix.at(0 * 2 + 0) - matrix.at(0 * 2 + 1 * 2 * 2) + + static_cast(0.5) * (matrix.at(0 * 2 + 0) - matrix.at(0 * 2 + 1 * 2 * 2) + matrix.at(1 * 2 + 0) - matrix.at(1 * 2 + 1)), - 0.5 * (matrix.at(0 * 2 + 0) + matrix.at(0 * 2 + 1 * 2 * 2) - + static_cast(0.5) * (matrix.at(0 * 2 + 0) + matrix.at(0 * 2 + 1 * 2 * 2) - matrix.at(1 * 2 + 0) - matrix.at(1 * 2 + 1)), - 0.5 * (matrix.at(0 * 2 + 0) - matrix.at(0 * 2 + 1 * 2 * 2) - + static_cast(0.5) * (matrix.at(0 * 2 + 0) - matrix.at(0 * 2 + 1 * 2 * 2) - matrix.at(1 * 2 + 0) + matrix.at(1 * 2 + 1)), }; auto [theta, phi, lam, phase] = params_zyz_inner(mat_zyz); @@ -568,17 +563,17 @@ struct GateDecompositionPattern final }; } - static constexpr std::complex C_ZERO{0., 0.}; - static constexpr std::complex C_ONE{1., 0.}; - static constexpr std::complex C_M_ONE{-1., 0.}; - static constexpr std::complex IM{0., 1.}; - static constexpr std::complex M_IM{0., -1.}; + static constexpr qfp C_ZERO{0., 0.}; + static constexpr qfp C_ONE{1., 0.}; + static constexpr qfp C_M_ONE{-1., 0.}; + static constexpr qfp IM{0., 1.}; + static constexpr qfp M_IM{0., -1.}; - static constexpr std::array, 4> IPZ = {IM, C_ZERO, + static constexpr std::array IPZ = {IM, C_ZERO, C_ZERO, M_IM}; - static constexpr std::array, 4> IPY = {C_ZERO, C_ONE, + static constexpr std::array IPY = {C_ZERO, C_ONE, C_M_ONE, C_ZERO}; - static constexpr std::array, 4> IPX = {C_ZERO, IM, IM, + static constexpr std::array IPX = {C_ZERO, IM, IM, C_ZERO}; static matrix2x2 getSingleQubitMatrix(const QubitGateSequence::Gate& gate) { @@ -624,38 +619,36 @@ struct GateDecompositionPattern final } struct TwoQubitWeylDecomposition { - qc::fp a; - qc::fp b; - qc::fp c; - qc::fp global_phase; - std::array, 4> K1l; - std::array, 4> K2l; - std::array, 4> K1r; - std::array, 4> K2r; + fp a; + fp b; + fp c; + fp global_phase; + std::array, 4> K1l; + std::array, 4> K2l; + std::array, 4> K1r; + std::array, 4> K2r; Specialization specialization; EulerBasis default_euler_basis; - std::optional requested_fidelity; - qc::fp calculated_fidelity; + std::optional requested_fidelity; + fp calculated_fidelity; matrix4x4 unitary_matrix; static TwoQubitWeylDecomposition - new_inner(matrix4x4 unitary_matrix, std::optional fidelity, + new_inner(matrix4x4 unitary_matrix, std::optional fidelity, std::optional _specialization) { auto& u = unitary_matrix; auto det_u = determinant(u); - llvm::errs() << "DET_U: " << det_u.real() << 'i' << det_u.imag() << '\n'; - // auto cpow = [](auto&& x, auto&& y) { // TODO: remove - // auto mag = std::abs(x); - // auto phase = std::arg(x); - // return std::polar(std::pow(mag, y), phase * y); }; - auto det_pow = std::pow(det_u, -qc::fp(0.25)); - llvm::errs() << "DET_POW: " << det_pow.real() << 'i' << det_pow.imag() - << '\n'; + auto det_pow = std::pow(det_u, -fp(0.25)); llvm::transform(u, u.begin(), [&](auto&& x) { return x * det_pow; }); + llvm::errs() << "===== U =====\n"; + helpers::print(u); auto global_phase = std::arg(det_u) / 4.; auto u_p = magic_basis_transform(u, MagicBasisTransform::OutOf); + llvm::errs() << "===== U_P =====\n"; + helpers::print(u_p); auto m2 = dot(transpose(u_p), u_p); auto default_euler_basis = EulerBasis::ZYZ; + llvm::errs() << "===== M2 =====\n"; helpers::print(m2); // M2 is a symmetric complex matrix. We need to decompose it as M2 = P D @@ -673,14 +666,14 @@ struct GateDecompositionPattern final // little bit. The fixed seed is to make failures deterministic; the // value is not important. auto state = std::mt19937{2023}; - std::normal_distribution dist; + std::normal_distribution dist; auto found = false; diagonal4x4 d{{C_ZERO}}; matrix4x4 p{{C_ZERO}}; for (int i = 0; i < 100; ++i) { - qc::fp rand_a; - qc::fp rand_b; + fp rand_a; + fp rand_b; // For debugging the algorithm use the same RNG values from the // previous Python implementation for the first random trial. // In most cases this loop only executes a single iteration and @@ -693,12 +686,12 @@ struct GateDecompositionPattern final rand_a = dist(state); rand_b = dist(state); } - std::array m2_real; + std::array m2_real; llvm::transform(m2, m2_real.begin(), [&](const qfp& val) { return rand_a * val.real() + rand_b * val.imag(); }); - auto p_inner_real = - std::get<1>(helpers::LUdecomposition(self_adjoint_eigen_lower(m2_real))); + auto p_inner_real = std::get<1>( + helpers::LUdecomposition(self_adjoint_eigen_lower(m2_real))); matrix4x4 p_inner; llvm::transform(p_inner_real, p_inner.begin(), [](auto&& x) { return qfp(x, 0.0); }); @@ -723,11 +716,11 @@ struct GateDecompositionPattern final throw std::runtime_error{ "TwoQubitWeylDecomposition: failed to diagonalize M2."}; } - std::array d_real; + std::array d_real; llvm::transform(d, d_real.begin(), [](auto&& x) { return -std::arg(x) / 2.0; }); d_real[3] = -d_real[0] - d_real[1] - d_real[2]; - std::array cs; + std::array cs; for (std::size_t i = 0; i < cs.size(); ++i) { assert(i < d_real.size()); cs[i] = remEuclid((d_real[i] + d_real[3]) / 2.0, qc::PI_2); @@ -829,11 +822,11 @@ struct GateDecompositionPattern final global_phase -= qc::PI_2; } auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); - auto is_close = [&](qc::fp ap, qc::fp bp, qc::fp cp) -> bool { + auto is_close = [&](fp ap, fp bp, fp cp) -> bool { auto da = a - ap; auto db = b - bp; auto dc = c - cp; - auto tr = 4. * qfp(std::cos(da) * std::cos(db) * std::cos(dc), + auto tr = static_cast(4.) * qfp(std::cos(da) * std::cos(db) * std::cos(dc), std::sin(da) * std::sin(db) * std::sin(dc)); if (fidelity) { return trace_to_fid(tr) >= *fidelity; @@ -1160,12 +1153,12 @@ struct GateDecompositionPattern final b - specialized.b, -c - specialized.c, }; - return 4. * qfp(std::cos(da) * std::cos(db) * std::cos(dc), + return static_cast(4.) * qfp(std::cos(da) * std::cos(db) * std::cos(dc), std::sin(da) * std::sin(db) * std::sin(dc)); } else { auto [da, db, dc] = std::array{a - specialized.a, b - specialized.b, c - specialized.c}; - return 4. * qfp(std::cos(da) * std::cos(db) * std::cos(dc), + return static_cast(4.) * qfp(std::cos(da) * std::cos(db) * std::cos(dc), std::sin(da) * std::sin(db) * std::sin(dc)); } }; @@ -1189,8 +1182,8 @@ struct GateDecompositionPattern final struct TwoQubitBasisDecomposer { qc::OpType gate; - std::vector gate_params; - qc::fp basis_fidelity; + std::vector gate_params; + fp basis_fidelity; EulerBasis euler_basis; std::optional pulse_optimize; TwoQubitWeylDecomposition basis_decomposer; @@ -1218,10 +1211,10 @@ struct GateDecompositionPattern final public: static TwoQubitBasisDecomposer new_inner(qc::OpType gate = qc::X, // CX - const std::vector& gate_params = {}, - matrix4x4 gate_matrix = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, - 1, 0}, // CX matrix - qc::fp basis_fidelity = 1.0, + const std::vector& gate_params = {}, + matrix4x4 gate_matrix = {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0}, // CX matrix + fp basis_fidelity = 1.0, EulerBasis euler_basis = EulerBasis::ZYZ, std::optional pulse_optimize = std::nullopt) { auto relative_eq = [](auto&& lhs, auto&& rhs, auto&& epsilon, @@ -1250,7 +1243,7 @@ struct GateDecompositionPattern final } return abs_diff <= abs_lhs * max_relative; }; - constexpr auto FRAC_1_SQRT_2 = 0.707106781186547524400844362104849039; + constexpr auto FRAC_1_SQRT_2 = static_cast(0.707106781186547524400844362104849039); constexpr auto K12R_ARR = std::array{ qfp(0., FRAC_1_SQRT_2), qfp(FRAC_1_SQRT_2, 0.), @@ -1268,9 +1261,9 @@ struct GateDecompositionPattern final gate_matrix, DEFAULT_FIDELITY, std::nullopt); auto super_controlled = relative_eq(basis_decomposer.a, qc::PI_4, - std::numeric_limits::epsilon(), 1e-09) && + std::numeric_limits::epsilon(), 1e-09) && relative_eq(basis_decomposer.c, 0.0, - std::numeric_limits::epsilon(), 1e-09); + std::numeric_limits::epsilon(), 1e-09); // Create some useful matrices U1, U2, U3 are equivalent to the basis, // expand as Ui = Ki1.Ubasis.Ki2 @@ -1379,20 +1372,20 @@ struct GateDecompositionPattern final std::optional twoQubitDecompose(const matrix4x4& unitaryMatrix, - std::optional _basis_fidelity, bool approximate, + std::optional _basis_fidelity, bool approximate, std::optional _num_basis_uses) { auto get_basis_fidelity = [&]() { if (approximate) { return _basis_fidelity.value_or(this->basis_fidelity); } - return 1.0; + return static_cast(1.0); }; - qc::fp basis_fidelity = get_basis_fidelity(); + fp basis_fidelity = get_basis_fidelity(); auto target_decomposed = TwoQubitWeylDecomposition::new_inner( unitaryMatrix, DEFAULT_FIDELITY, std::nullopt); auto traces = this->traces(target_decomposed); auto get_default_nbasis = [&]() { - auto minValue = std::numeric_limits::max(); + auto minValue = std::numeric_limits::max(); auto minIndex = -1; for (std::size_t i = 0; i < traces.size(); ++i) { auto value = trace_to_fid(traces[i]) * std::pow(basis_fidelity, i); @@ -1575,7 +1568,7 @@ struct GateDecompositionPattern final gates.globalPhase -= 2. * basis_decomposer.global_phase; auto get_euler_angles = [&](std::size_t startIndex, EulerBasis basis) { - std::vector> result; + std::vector> result; for (std::size_t i = startIndex; i < decomposition.size(); i += 2) { auto euler_angles = angles_from_unitary(decomposition[i], basis); gates.globalPhase += euler_angles[3]; @@ -1642,7 +1635,7 @@ struct GateDecompositionPattern final // Decompose source unitaries to zxz auto get_euler_angles = [&](std::size_t startIndex, EulerBasis basis) { - std::vector> result; + std::vector> result; for (std::size_t i = startIndex; i < decomposition.size(); i += 2) { auto euler_angles = angles_from_unitary(decomposition[i], basis); gates.globalPhase += euler_angles[3]; @@ -1756,8 +1749,8 @@ struct GateDecompositionPattern final } matrix4x4 compute_unitary(const TwoQubitGateSequence& sequence, - qc::fp global_phase) { - auto phase = std::exp(std::complex{0, global_phase}); + fp global_phase) { + auto phase = std::exp(std::complex{0, global_phase}); matrix4x4 matrix; matrix[0 * 4 + 0] = phase; matrix[1 * 4 + 1] = phase; @@ -1789,10 +1782,10 @@ struct GateDecompositionPattern final [[nodiscard]] std::array traces(TwoQubitWeylDecomposition target) const { return { - 4. * + static_cast(4.) * qfp(std::cos(target.a) * std::cos(target.b) * std::cos(target.c), std::sin(target.a) * std::sin(target.b) * std::sin(target.c)), - 4. * qfp(std::cos(qc::PI_4 - target.a) * + static_cast(4.) * qfp(std::cos(qc::PI_4 - target.a) * std::cos(basis_decomposer.b - target.b) * std::cos(target.c), std::sin(qc::PI_4 - target.a) * @@ -1806,7 +1799,7 @@ struct GateDecompositionPattern final OneQubitGateSequence generate_circuit(EulerBasis target_basis, const matrix2x2& unitaryMatrix, bool simplify, - std::optional atol) { + std::optional atol) { auto [theta, phi, lamda, phase] = angles_from_unitary(unitaryMatrix, target_basis); @@ -1832,14 +1825,14 @@ struct GateDecompositionPattern final OneQubitGateSequence unitary_to_gate_sequence_inner( matrix2x2 unitary_mat, const std::vector& target_basis_list, std::size_t qubit, - const std::vector>& + const std::vector>& error_map, // TODO: remove error_map+qubit for platform independence - bool simplify, std::optional atol) { + bool simplify, std::optional atol) { auto calculateError = [](const OneQubitGateSequence& sequence) { return sequence.gates.size(); }; - auto minError = std::numeric_limits::max(); + auto minError = std::numeric_limits::max(); OneQubitGateSequence bestCircuit; for (std::size_t i = 0; i < target_basis_list.size(); ++i) { auto& target_basis = target_basis_list[i]; @@ -1869,15 +1862,15 @@ struct GateDecompositionPattern final * indicating that they have been altered from the originals. */ [[nodiscard]] static OneQubitGateSequence - calculateRotationGates(qc::fp theta, qc::fp phi, qc::fp lambda, - qc::fp phase, qc::OpType kGate, qc::OpType aGate, - bool simplify, std::optional atol) { - qc::fp angleZeroEpsilon = atol.value_or(1e-12); + calculateRotationGates(fp theta, fp phi, fp lambda, + fp phase, qc::OpType kGate, qc::OpType aGate, + bool simplify, std::optional atol) { + fp angleZeroEpsilon = atol.value_or(1e-12); if (simplify) { angleZeroEpsilon = -1.0; } - qc::fp globalPhase = phase - ((phi + lambda) / 2.); + fp globalPhase = phase - ((phi + lambda) / 2.); std::vector gates; if (std::abs(theta) < angleZeroEpsilon) { diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index e949518049..6880e934f9 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -19,34 +19,42 @@ #include #include +namespace mqt::ir::opt { + using fp = long double; + using qfp = std::complex; + using diagonal4x4 = std::array; + using vector2d = std::vector; + using matrix2x2 = std::array; + using matrix4x4 = std::array; +} // namespace mqt::ir::opt + namespace mqt::ir::opt::helpers { // TODO: remove - template - void print(std::array, N> matrix) { - int i{}; - for (auto&& a : matrix) { - llvm::errs() << a.real() << 'i' << a.imag() << ' '; - if (++i % 4 == 0) { - llvm::errs() << '\n'; - } +template +void print(std::array, N> matrix) { + int i{}; + for (auto&& a : matrix) { + std::cerr << std::setprecision(50) << a.real() << 'i' << a.imag() << ' '; + if (++i % 4 == 0) { + llvm::errs() << '\n'; } - llvm::errs() << '\n'; - } + llvm::errs() << '\n'; +} inline auto flatten(const dd::TwoQubitGateMatrix& matrix) { - std::array, 16> result; + std::array, 16> result; for (std::size_t i = 0; i < result.size(); ++i) { result[i] = matrix[i / 4][i % 4]; } return result; } -std::optional mlirValueToFp(mlir::Value value); +std::optional mlirValueToFp(mlir::Value value); template -std::optional performMlirFloatBinaryOp(mlir::Value value, Func&& func) { +std::optional performMlirFloatBinaryOp(mlir::Value value, Func&& func) { if (auto op = value.getDefiningOp()) { auto lhs = mlirValueToFp(op.getLhs()); auto rhs = mlirValueToFp(op.getRhs()); @@ -58,7 +66,7 @@ std::optional performMlirFloatBinaryOp(mlir::Value value, Func&& func) { } template -std::optional performMlirFloatUnaryOp(mlir::Value value, Func&& func) { +std::optional performMlirFloatUnaryOp(mlir::Value value, Func&& func) { if (auto op = value.getDefiningOp()) { if (auto operand = mlirValueToFp(op.getOperand())) { return std::invoke(std::forward(func), *operand); @@ -67,7 +75,7 @@ std::optional performMlirFloatUnaryOp(mlir::Value value, Func&& func) { return std::nullopt; } -inline std::optional mlirValueToFp(mlir::Value value) { +inline std::optional mlirValueToFp(mlir::Value value) { if (auto op = value.getDefiningOp()) { if (auto attr = llvm::dyn_cast(op.getValue())) { return attr.getValueAsDouble(); @@ -75,58 +83,58 @@ inline std::optional mlirValueToFp(mlir::Value value) { return std::nullopt; } if (auto result = performMlirFloatUnaryOp( - value, [](qc::fp a) { return -a; })) { + value, [](fp a) { return -a; })) { return result; } if (auto result = performMlirFloatUnaryOp( - value, [](qc::fp a) { return a; })) { + value, [](fp a) { return a; })) { return result; } if (auto result = performMlirFloatUnaryOp( - value, [](qc::fp a) { return a; })) { + value, [](fp a) { return a; })) { return result; } if (auto result = performMlirFloatBinaryOp( - value, [](qc::fp a, qc::fp b) { return std::max(a, b); })) { + value, [](fp a, fp b) { return std::max(a, b); })) { return result; } if (auto result = performMlirFloatBinaryOp( - value, [](qc::fp a, qc::fp b) { return std::max(a, b); })) { + value, [](fp a, fp b) { return std::max(a, b); })) { return result; } if (auto result = performMlirFloatBinaryOp( - value, [](qc::fp a, qc::fp b) { return std::min(a, b); })) { + value, [](fp a, fp b) { return std::min(a, b); })) { return result; } if (auto result = performMlirFloatBinaryOp( - value, [](qc::fp a, qc::fp b) { return std::min(a, b); })) { + value, [](fp a, fp b) { return std::min(a, b); })) { return result; } if (auto result = performMlirFloatBinaryOp( - value, [](qc::fp a, qc::fp b) { return std::fmod(a, b); })) { + value, [](fp a, fp b) { return std::fmod(a, b); })) { return result; } if (auto result = performMlirFloatBinaryOp( - value, [](qc::fp a, qc::fp b) { return a + b; })) { + value, [](fp a, fp b) { return a + b; })) { return result; } if (auto result = performMlirFloatBinaryOp( - value, [](qc::fp a, qc::fp b) { return a + b; })) { + value, [](fp a, fp b) { return a + b; })) { return result; } if (auto result = performMlirFloatBinaryOp( - value, [](qc::fp a, qc::fp b) { return a + b; })) { + value, [](fp a, fp b) { return a + b; })) { return result; } if (auto result = performMlirFloatBinaryOp( - value, [](qc::fp a, qc::fp b) { return a + b; })) { + value, [](fp a, fp b) { return a + b; })) { return result; } return std::nullopt; } -[[nodiscard]] inline std::vector getParameters(UnitaryInterface op) { - std::vector parameters; +[[nodiscard]] inline std::vector getParameters(UnitaryInterface op) { + std::vector parameters; for (auto&& param : op.getParams()) { if (auto value = helpers::mlirValueToFp(param)) { parameters.push_back(*value); @@ -167,18 +175,52 @@ inline std::optional mlirValueToFp(mlir::Value value) { return isTwoQubitOp; } -[[nodiscard]] inline dd::GateMatrix multiply(std::complex factor, - dd::GateMatrix matrix) { +template +T kahanSum(const std::array& values) { + auto sum = T{}; + auto c = T{}; // Compensation for lost low-order bits + + for (auto&& value : values) { + auto y = value - c; // Correct for error so far + auto t = sum + y; // Add the value to the running sum + c = (t - sum) - y; // Recompute the error + sum = t; + } + + return sum; +} + +// Modify the matrix multiplication to use Kahan summation +template +std::array matrixMultiplyWithKahan(const std::array& lhs, + const std::array& rhs) { + std::array result; + + const std::size_t n = std::sqrt(N); + for (size_t i = 0; i < n; ++i) { + for (size_t j = 0; j < n; ++j) { + std::array terms; + for (size_t k = 0; k < n; ++k) { + terms[k] = lhs[i * n + k] * rhs[k * n + j]; + } + result[i * n + j] = kahanSum(terms); + } + } + return result; +} + +[[nodiscard]] inline matrix2x2 multiply(qfp factor, + const matrix2x2& matrix) { return {factor * matrix.at(0), factor * matrix.at(1), factor * matrix.at(2), factor * matrix.at(3)}; } -template -[[nodiscard]] inline auto -multiply(const std::array& lhs, - const std::array& rhs) { - std::array result; - const auto n = std::sqrt(N); +template +[[nodiscard]] inline auto multiply(const std::array& lhs, + const std::array& rhs) { + // return matrixMultiplyWithKahan(lhs, rhs); + std::array result{{T{}}}; + const int n = std::sqrt(N); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int k = 0; k < n; k++) { @@ -189,66 +231,60 @@ multiply(const std::array& lhs, return result; } -[[nodiscard]] inline dd::TwoQubitGateMatrix -kroneckerProduct(dd::GateMatrix lhs, dd::GateMatrix rhs) { - return {multiply(lhs.at(0), rhs), multiply(lhs.at(1), rhs), - multiply(lhs.at(2), rhs), multiply(lhs.at(3), rhs)}; -} - -template +template [[nodiscard]] inline auto LUdecomposition(std::array matrix) { - std::array L{}; - std::array U{}; - int rowPermutations = 0; - - for (int i = 0; i < 4; i++) { - // --- Partial pivoting: find max row in column i --- - int pivotRow = i; - auto maxVal = matrix[i * 4 + i]; - - for (int r = i + 1; r < 4; r++) { - auto val = matrix[r * 4 + i]; - if (std::abs(val) > std::abs(maxVal)) { - maxVal = val; - pivotRow = r; - } - } + std::array L{}; + std::array U{}; + int rowPermutations = 0; - // --- Swap rows in matrix if needed --- - if (pivotRow != i) { - for (int col = 0; col < 4; ++col) { - std::swap(matrix[i * 4 + col], matrix[pivotRow * 4 + col]); - } - ++rowPermutations; - } + for (int i = 0; i < 4; i++) { + // --- Partial pivoting: find max row in column i --- + int pivotRow = i; + auto maxVal = matrix[i * 4 + i]; + + for (int r = i + 1; r < 4; r++) { + auto val = matrix[r * 4 + i]; + if (std::abs(val) > std::abs(maxVal)) { + maxVal = val; + pivotRow = r; + } + } + + // --- Swap rows in matrix if needed --- + if (pivotRow != i) { + for (int col = 0; col < 4; ++col) { + std::swap(matrix[i * 4 + col], matrix[pivotRow * 4 + col]); + } + ++rowPermutations; + } - // --- Compute L matrix (column-wise) --- - for (int j = 0; j < 4; j++) { - if (j < i) - L[j * 4 + i] = 0; - else { - L[j * 4 + i] = matrix[j * 4 + i]; - for (int k = 0; k < i; k++) { - L[j * 4 + i] -= L[j * 4 + k] * U[k * 4 + i]; - } - } + // --- Compute L matrix (column-wise) --- + for (int j = 0; j < 4; j++) { + if (j < i) + L[j * 4 + i] = 0; + else { + L[j * 4 + i] = matrix[j * 4 + i]; + for (int k = 0; k < i; k++) { + L[j * 4 + i] -= L[j * 4 + k] * U[k * 4 + i]; } + } + } - // --- Compute U matrix (row-wise) --- - for (int j = 0; j < 4; j++) { - if (j < i) - U[i * 4 + j] = 0; - else if (j == i) - U[i * 4 + j] = 1; // Diagonal of U is set to 1 - else { - U[i * 4 + j] = matrix[i * 4 + j] / L[i * 4 + i]; - for (int k = 0; k < i; k++) { - U[i * 4 + j] -= (L[i * 4 + k] * U[k * 4 + j]) / L[i * 4 + i]; - } - } + // --- Compute U matrix (row-wise) --- + for (int j = 0; j < 4; j++) { + if (j < i) + U[i * 4 + j] = 0; + else if (j == i) + U[i * 4 + j] = 1; // Diagonal of U is set to 1 + else { + U[i * 4 + j] = matrix[i * 4 + j] / L[i * 4 + i]; + for (int k = 0; k < i; k++) { + U[i * 4 + j] -= (L[i * 4 + k] * U[k * 4 + j]) / L[i * 4 + i]; } + } } + } - return std::make_tuple(L, U, rowPermutations); + return std::make_tuple(L, U, rowPermutations); } } // namespace mqt::ir::opt::helpers From 3dd12664169918ea5ca053105fd6e578c636ef0a Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 17 Oct 2025 01:00:31 +0200 Subject: [PATCH 013/237] testing a.cpp --- mlir/lib/Dialect/MQTOpt/Transforms/a.cpp | 903 +++++++++++++++++++++++ 1 file changed, 903 insertions(+) create mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/a.cpp diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/a.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/a.cpp new file mode 100644 index 0000000000..d0bd804e45 --- /dev/null +++ b/mlir/lib/Dialect/MQTOpt/Transforms/a.cpp @@ -0,0 +1,903 @@ +#include +#include +#include +#include +#include + +using fp = long double; +using qfp = std::complex; +using diagonal4x4 = std::array; +using vector2d = std::vector; +using matrix2x2 = std::array; +using matrix4x4 = std::array; + +using logical = bool; +using integer = int; +using doublecomplex = qfp; + +matrix4x4 zgemm2(matrix4x4 a, matrix4x4 b) { + matrix4x4 c__; + // a_dim1 = *lda; + // a_offset = 1 + a_dim1; + // a -= a_offset; + // b_dim1 = *ldb; + // b_offset = 1 + b_dim1; + // b -= b_offset; + // c_dim1 = *ldc; + // c_offset = 1 + c_dim1; + // c__ -= c_offset; + + int i__1 = 4; + int i__3{}; + qfp z__1; + qfp z__2; + qfp temp; + qfp alpha {1.0, 0.0}; + for (auto j = 0; j < i__1; ++j) { + for (auto i__ = 0; i__ < 4; ++i__) { + auto i__3 = i__ + j * 4; + c__[i__3].real(0.), c__[i__3].imag(0.); + } + int i__2 = 4; + for (auto l = 0; l < i__2; ++l) { + i__3 = l + j * 4; + if (b[i__3].real() != 0. || b[i__3].imag() != 0.) { + i__3 = l + j * 4; + z__1.real(alpha.real() * b[i__3].real() - + alpha.imag() * b[i__3].imag()), + z__1.imag(alpha.real() * b[i__3].imag() + + alpha.imag() * b[i__3].real()); + temp.real(z__1.real()), temp.imag(z__1.imag()); + i__3 = 4; + for (auto i__ = 0; i__ < i__3; ++i__) { + auto i__4 = i__ + j * 4; + auto i__5 = i__ + j * 4; + auto i__6 = i__ + l * 4; + z__2.real(temp.real() * a[i__6].real() - + temp.imag() * a[i__6].imag()), + z__2.imag(temp.real() * a[i__6].imag() + + temp.imag() * a[i__6].real()); + z__1.real(c__[i__5].real() + z__2.real()), + z__1.imag(c__[i__5].imag() + z__2.imag()); + c__[i__4].real(z__1.real()), c__[i__4].imag(z__1.imag()); + /* L70: */ + } + } + /* L80: */ + } + /* L90: */ + } + return c__; +} + +int zgemm_(char* transa, char* transb, integer* m, integer* n, integer* k, + doublecomplex* alpha, doublecomplex* a, integer* lda, + doublecomplex* b, integer* ldb, doublecomplex* beta, + doublecomplex* c__, integer* ldc); +matrix4x4 zgemm(matrix4x4 a, matrix4x4 b) { + qfp alpha{1.0, 0.0}; + qfp beta{1.0, 0.0}; + int dimension = 4; + matrix4x4 result; + zgemm_("n", "n", &dimension, &dimension, &dimension, &alpha, a.data(), + &dimension, b.data(), &dimension, &beta, result.data(), &dimension); + return result; +} + +void d_cnjg(doublecomplex* r, doublecomplex* z) { *r = std::conj(*z); } + +/* Subroutine */ int zgemm_(char* transa, char* transb, integer* m, integer* n, + integer* k, doublecomplex* alpha, doublecomplex* a, + integer* lda, doublecomplex* b, integer* ldb, + doublecomplex* beta, doublecomplex* c__, + integer* ldc) { + /* System generated locals */ + integer a_dim1, a_offset, b_dim1, b_offset, c_dim1, c_offset, i__1, i__2, + i__3, i__4, i__5, i__6; + doublecomplex z__1, z__2, z__3, z__4; + + /* Local variables */ + static integer i__, j, l, info; + static logical nota, notb; + static doublecomplex temp; + static logical conja, conjb; + static integer nrowa, nrowb; + + /* + Purpose + ======= + + ZGEMM performs one of the matrix-matrix operations + + C := alpha*op( A )*op( B ) + beta*C, + + where op( X ) is one of + + op( X ) = X or op( X ) = X' or op( X ) = conjg( X' ), + + alpha and beta are scalars, and A, B and C are matrices, with op( A ) + an m by k matrix, op( B ) a k by n matrix and C an m by n matrix. + + Arguments + ========== + + TRANSA - CHARACTER*1. + On entry, TRANSA specifies the form of op( A ) to be used in + the matrix multiplication as follows: + + TRANSA = 'N' or 'n', op( A ) = A. + + TRANSA = 'T' or 't', op( A ) = A'. + + TRANSA = 'C' or 'c', op( A ) = conjg( A' ). + + Unchanged on exit. + + TRANSB - CHARACTER*1. + On entry, TRANSB specifies the form of op( B ) to be used in + the matrix multiplication as follows: + + TRANSB = 'N' or 'n', op( B ) = B. + + TRANSB = 'T' or 't', op( B ) = B'. + + TRANSB = 'C' or 'c', op( B ) = conjg( B' ). + + Unchanged on exit. + + M - INTEGER. + On entry, M specifies the number of rows of the matrix + op( A ) and of the matrix C. M must be at least zero. + Unchanged on exit. + + N - INTEGER. + On entry, N specifies the number of columns of the matrix + op( B ) and the number of columns of the matrix C. N must be + at least zero. + Unchanged on exit. + + K - INTEGER. + On entry, K specifies the number of columns of the matrix + op( A ) and the number of rows of the matrix op( B ). K must + be at least zero. + Unchanged on exit. + + ALPHA - COMPLEX*16 . + On entry, ALPHA specifies the scalar alpha. + Unchanged on exit. + + A - COMPLEX*16 array of DIMENSION ( LDA, ka ), where ka is + k when TRANSA = 'N' or 'n', and is m otherwise. + Before entry with TRANSA = 'N' or 'n', the leading m by k + part of the array A must contain the matrix A, otherwise + the leading k by m part of the array A must contain the + matrix A. + Unchanged on exit. + + LDA - INTEGER. + On entry, LDA specifies the first dimension of A as declared + in the calling (sub) program. When TRANSA = 'N' or 'n' then + LDA must be at least max( 1, m ), otherwise LDA must be at + least max( 1, k ). + Unchanged on exit. + + B - COMPLEX*16 array of DIMENSION ( LDB, kb ), where kb is + n when TRANSB = 'N' or 'n', and is k otherwise. + Before entry with TRANSB = 'N' or 'n', the leading k by n + part of the array B must contain the matrix B, otherwise + the leading n by k part of the array B must contain the + matrix B. + Unchanged on exit. + + LDB - INTEGER. + On entry, LDB specifies the first dimension of B as declared + in the calling (sub) program. When TRANSB = 'N' or 'n' then + LDB must be at least max( 1, k ), otherwise LDB must be at + least max( 1, n ). + Unchanged on exit. + + BETA - COMPLEX*16 . + On entry, BETA specifies the scalar beta. When BETA is + supplied as zero then C need not be set on input. + Unchanged on exit. + + C - COMPLEX*16 array of DIMENSION ( LDC, n ). + Before entry, the leading m by n part of the array C must + contain the matrix C, except when beta is zero, in which + case C need not be set on entry. + On exit, the array C is overwritten by the m by n matrix + ( alpha*op( A )*op( B ) + beta*C ). + + LDC - INTEGER. + On entry, LDC specifies the first dimension of C as declared + in the calling (sub) program. LDC must be at least + max( 1, m ). + Unchanged on exit. + + Further Details + =============== + + Level 3 Blas routine. + + -- Written on 8-February-1989. + Jack Dongarra, Argonne National Laboratory. + Iain Duff, AERE Harwell. + Jeremy Du Croz, Numerical Algorithms Group Ltd. + Sven Hammarling, Numerical Algorithms Group Ltd. + + ===================================================================== + + + Set NOTA and NOTB as true if A and B respectively are not + conjugated or transposed, set CONJA and CONJB as true if A and + B respectively are to be transposed but not conjugated and set + NROWA and NROWB as the number of rows and columns of A + and the number of rows of B respectively. + */ + + auto lsame_ = [](char* a, char* b) { + return std::string{a} == std::string{b}; + }; + + /* Parameter adjustments */ + a_dim1 = *lda; + a_offset = 1 + a_dim1; + a -= a_offset; + b_dim1 = *ldb; + b_offset = 1 + b_dim1; + b -= b_offset; + c_dim1 = *ldc; + c_offset = 1 + c_dim1; + c__ -= c_offset; + + /* Function Body */ + nota = lsame_(transa, "N"); + notb = lsame_(transb, "N"); + conja = lsame_(transa, "C"); + conjb = lsame_(transb, "C"); + if (nota) { + nrowa = *m; + } else { + nrowa = *k; + } + if (notb) { + nrowb = *k; + } else { + nrowb = *n; + } + + /* Test the input parameters. */ + + info = 0; + if (!nota && !conja && !lsame_(transa, "T")) { + info = 1; + } else if (!notb && !conjb && !lsame_(transb, "T")) { + info = 2; + } else if (*m < 0) { + info = 3; + } else if (*n < 0) { + info = 4; + } else if (*k < 0) { + info = 5; + } else if (*lda < std::max(1, nrowa)) { + info = 8; + } else if (*ldb < std::max(1, nrowb)) { + info = 10; + } else if (*ldc < std::max(1, *m)) { + info = 13; + } + if (info != 0) { + // xerbla_("ZGEMM ", &info); + return 0; + } + + /* Quick return if possible. */ + + if (*m == 0 || *n == 0 || + (alpha->real() == 0. && alpha->imag() == 0. || *k == 0) && + (beta->real() == 1. && beta->imag() == 0.)) { + return 0; + } + + /* And when alpha.eq.zero. */ + + if (alpha->real() == 0. && alpha->imag() == 0.) { + if (beta->real() == 0. && beta->imag() == 0.) { + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + i__3 = i__ + j * c_dim1; + c__[i__3].real(0.), c__[i__3].imag(0.); + /* L10: */ + } + /* L20: */ + } + } else { + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + i__3 = i__ + j * c_dim1; + i__4 = i__ + j * c_dim1; + z__1.real(beta->real() * c__[i__4].real() - + beta->imag() * c__[i__4].imag()), + z__1.imag(beta->real() * c__[i__4].imag() + + beta->imag() * c__[i__4].real()); + c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + /* L30: */ + } + /* L40: */ + } + } + return 0; + } + + /* Start the operations. */ + + if (notb) { + if (nota) { + + /* Form C := alpha*A*B + beta*C. */ + + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + if (beta->real() == 0. && beta->imag() == 0.) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + i__3 = i__ + j * c_dim1; + c__[i__3].real(0.), c__[i__3].imag(0.); + /* L50: */ + } + } else if (beta->real() != 1. || beta->imag() != 0.) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + i__3 = i__ + j * c_dim1; + i__4 = i__ + j * c_dim1; + z__1.real(beta->real() * c__[i__4].real() - + beta->imag() * c__[i__4].imag()), + z__1.imag(beta->real() * c__[i__4].imag() + + beta->imag() * c__[i__4].real()); + c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + /* L60: */ + } + } + i__2 = *k; + for (l = 1; l <= i__2; ++l) { + i__3 = l + j * b_dim1; + if (b[i__3].real() != 0. || b[i__3].imag() != 0.) { + i__3 = l + j * b_dim1; + z__1.real(alpha->real() * b[i__3].real() - + alpha->imag() * b[i__3].imag()), + z__1.imag(alpha->real() * b[i__3].imag() + + alpha->imag() * b[i__3].real()); + temp.real(z__1.real()), temp.imag(z__1.imag()); + i__3 = *m; + for (i__ = 1; i__ <= i__3; ++i__) { + i__4 = i__ + j * c_dim1; + i__5 = i__ + j * c_dim1; + i__6 = i__ + l * a_dim1; + z__2.real(temp.real() * a[i__6].real() - + temp.imag() * a[i__6].imag()), + z__2.imag(temp.real() * a[i__6].imag() + + temp.imag() * a[i__6].real()); + z__1.real(c__[i__5].real() + z__2.real()), + z__1.imag(c__[i__5].imag() + z__2.imag()); + c__[i__4].real(z__1.real()), c__[i__4].imag(z__1.imag()); + /* L70: */ + } + } + /* L80: */ + } + /* L90: */ + } + } else if (conja) { + + /* Form C := alpha*conjg( A' )*B + beta*C. */ + + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + temp.real(0.), temp.imag(0.); + i__3 = *k; + for (l = 1; l <= i__3; ++l) { + d_cnjg(&z__3, &a[l + i__ * a_dim1]); + i__4 = l + j * b_dim1; + z__2.real(z__3.real() * b[i__4].real() - + z__3.imag() * b[i__4].imag()), + z__2.imag(z__3.real() * b[i__4].imag() + + z__3.imag() * b[i__4].real()); + z__1.real(temp.real() + z__2.real()), + z__1.imag(temp.imag() + z__2.imag()); + temp.real(z__1.real()), temp.imag(z__1.imag()); + /* L100: */ + } + if (beta->real() == 0. && beta->imag() == 0.) { + i__3 = i__ + j * c_dim1; + z__1.real(alpha->real() * temp.real() - + alpha->imag() * temp.imag()), + z__1.imag(alpha->real() * temp.imag() + + alpha->imag() * temp.real()); + c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + } else { + i__3 = i__ + j * c_dim1; + z__2.real(alpha->real() * temp.real() - + alpha->imag() * temp.imag()), + z__2.imag(alpha->real() * temp.imag() + + alpha->imag() * temp.real()); + i__4 = i__ + j * c_dim1; + z__3.real(beta->real() * c__[i__4].real() - + beta->imag() * c__[i__4].imag()), + z__3.imag(beta->real() * c__[i__4].imag() + + beta->imag() * c__[i__4].real()); + z__1.real(z__2.real() + z__3.real()), + z__1.imag(z__2.imag() + z__3.imag()); + c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + } + /* L110: */ + } + /* L120: */ + } + } else { + + /* Form C := alpha*A'*B + beta*C */ + + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + temp.real(0.), temp.imag(0.); + i__3 = *k; + for (l = 1; l <= i__3; ++l) { + i__4 = l + i__ * a_dim1; + i__5 = l + j * b_dim1; + z__2.real(a[i__4].real() * b[i__5].real() - + a[i__4].imag() * b[i__5].imag()), + z__2.imag(a[i__4].real() * b[i__5].imag() + + a[i__4].imag() * b[i__5].real()); + z__1.real(temp.real() + z__2.real()), + z__1.imag(temp.imag() + z__2.imag()); + temp.real(z__1.real()), temp.imag(z__1.imag()); + /* L130: */ + } + if (beta->real() == 0. && beta->imag() == 0.) { + i__3 = i__ + j * c_dim1; + z__1.real(alpha->real() * temp.real() - + alpha->imag() * temp.imag()), + z__1.imag(alpha->real() * temp.imag() + + alpha->imag() * temp.real()); + c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + } else { + i__3 = i__ + j * c_dim1; + z__2.real(alpha->real() * temp.real() - + alpha->imag() * temp.imag()), + z__2.imag(alpha->real() * temp.imag() + + alpha->imag() * temp.real()); + i__4 = i__ + j * c_dim1; + z__3.real(beta->real() * c__[i__4].real() - + beta->imag() * c__[i__4].imag()), + z__3.imag(beta->real() * c__[i__4].imag() + + beta->imag() * c__[i__4].real()); + z__1.real(z__2.real() + z__3.real()), + z__1.imag(z__2.imag() + z__3.imag()); + c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + } + /* L140: */ + } + /* L150: */ + } + } + } else if (nota) { + if (conjb) { + + /* Form C := alpha*A*conjg( B' ) + beta*C. */ + + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + if (beta->real() == 0. && beta->imag() == 0.) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + i__3 = i__ + j * c_dim1; + c__[i__3].real(0.), c__[i__3].imag(0.); + /* L160: */ + } + } else if (beta->real() != 1. || beta->imag() != 0.) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + i__3 = i__ + j * c_dim1; + i__4 = i__ + j * c_dim1; + z__1.real(beta->real() * c__[i__4].real() - + beta->imag() * c__[i__4].imag()), + z__1.imag(beta->real() * c__[i__4].imag() + + beta->imag() * c__[i__4].real()); + c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + /* L170: */ + } + } + i__2 = *k; + for (l = 1; l <= i__2; ++l) { + i__3 = j + l * b_dim1; + if (b[i__3].real() != 0. || b[i__3].imag() != 0.) { + d_cnjg(&z__2, &b[j + l * b_dim1]); + z__1.real(alpha->real() * z__2.real() - + alpha->imag() * z__2.imag()), + z__1.imag(alpha->real() * z__2.imag() + + alpha->imag() * z__2.real()); + temp.real(z__1.real()), temp.imag(z__1.imag()); + i__3 = *m; + for (i__ = 1; i__ <= i__3; ++i__) { + i__4 = i__ + j * c_dim1; + i__5 = i__ + j * c_dim1; + i__6 = i__ + l * a_dim1; + z__2.real(temp.real() * a[i__6].real() - + temp.imag() * a[i__6].imag()), + z__2.imag(temp.real() * a[i__6].imag() + + temp.imag() * a[i__6].real()); + z__1.real(c__[i__5].real() + z__2.real()), + z__1.imag(c__[i__5].imag() + z__2.imag()); + c__[i__4].real(z__1.real()), c__[i__4].imag(z__1.imag()); + /* L180: */ + } + } + /* L190: */ + } + /* L200: */ + } + } else { + + /* Form C := alpha*A*B' + beta*C */ + + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + if (beta->real() == 0. && beta->imag() == 0.) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + i__3 = i__ + j * c_dim1; + c__[i__3].real(0.), c__[i__3].imag(0.); + /* L210: */ + } + } else if (beta->real() != 1. || beta->imag() != 0.) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + i__3 = i__ + j * c_dim1; + i__4 = i__ + j * c_dim1; + z__1.real(beta->real() * c__[i__4].real() - + beta->imag() * c__[i__4].imag()), + z__1.imag(beta->real() * c__[i__4].imag() + + beta->imag() * c__[i__4].real()); + c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + /* L220: */ + } + } + i__2 = *k; + for (l = 1; l <= i__2; ++l) { + i__3 = j + l * b_dim1; + if (b[i__3].real() != 0. || b[i__3].imag() != 0.) { + i__3 = j + l * b_dim1; + z__1.real(alpha->real() * b[i__3].real() - + alpha->imag() * b[i__3].imag()), + z__1.imag(alpha->real() * b[i__3].imag() + + alpha->imag() * b[i__3].real()); + temp.real(z__1.real()), temp.imag(z__1.imag()); + i__3 = *m; + for (i__ = 1; i__ <= i__3; ++i__) { + i__4 = i__ + j * c_dim1; + i__5 = i__ + j * c_dim1; + i__6 = i__ + l * a_dim1; + z__2.real(temp.real() * a[i__6].real() - + temp.imag() * a[i__6].imag()), + z__2.imag(temp.real() * a[i__6].imag() + + temp.imag() * a[i__6].real()); + z__1.real(c__[i__5].real() + z__2.real()), + z__1.imag(c__[i__5].imag() + z__2.imag()); + c__[i__4].real(z__1.real()), c__[i__4].imag(z__1.imag()); + /* L230: */ + } + } + /* L240: */ + } + /* L250: */ + } + } + } else if (conja) { + if (conjb) { + + /* Form C := alpha*conjg( A' )*conjg( B' ) + beta*C. */ + + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + temp.real(0.), temp.imag(0.); + i__3 = *k; + for (l = 1; l <= i__3; ++l) { + d_cnjg(&z__3, &a[l + i__ * a_dim1]); + d_cnjg(&z__4, &b[j + l * b_dim1]); + z__2.real(z__3.real() * z__4.real() - z__3.imag() * z__4.imag()), + z__2.imag(z__3.real() * z__4.imag() + + z__3.imag() * z__4.real()); + z__1.real(temp.real() + z__2.real()), + z__1.imag(temp.imag() + z__2.imag()); + temp.real(z__1.real()), temp.imag(z__1.imag()); + /* L260: */ + } + if (beta->real() == 0. && beta->imag() == 0.) { + i__3 = i__ + j * c_dim1; + z__1.real(alpha->real() * temp.real() - + alpha->imag() * temp.imag()), + z__1.imag(alpha->real() * temp.imag() + + alpha->imag() * temp.real()); + c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + } else { + i__3 = i__ + j * c_dim1; + z__2.real(alpha->real() * temp.real() - + alpha->imag() * temp.imag()), + z__2.imag(alpha->real() * temp.imag() + + alpha->imag() * temp.real()); + i__4 = i__ + j * c_dim1; + z__3.real(beta->real() * c__[i__4].real() - + beta->imag() * c__[i__4].imag()), + z__3.imag(beta->real() * c__[i__4].imag() + + beta->imag() * c__[i__4].real()); + z__1.real(z__2.real() + z__3.real()), + z__1.imag(z__2.imag() + z__3.imag()); + c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + } + /* L270: */ + } + /* L280: */ + } + } else { + + /* Form C := alpha*conjg( A' )*B' + beta*C */ + + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + temp.real(0.), temp.imag(0.); + i__3 = *k; + for (l = 1; l <= i__3; ++l) { + d_cnjg(&z__3, &a[l + i__ * a_dim1]); + i__4 = j + l * b_dim1; + z__2.real(z__3.real() * b[i__4].real() - + z__3.imag() * b[i__4].imag()), + z__2.imag(z__3.real() * b[i__4].imag() + + z__3.imag() * b[i__4].real()); + z__1.real(temp.real() + z__2.real()), + z__1.imag(temp.imag() + z__2.imag()); + temp.real(z__1.real()), temp.imag(z__1.imag()); + /* L290: */ + } + if (beta->real() == 0. && beta->imag() == 0.) { + i__3 = i__ + j * c_dim1; + z__1.real(alpha->real() * temp.real() - + alpha->imag() * temp.imag()), + z__1.imag(alpha->real() * temp.imag() + + alpha->imag() * temp.real()); + c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + } else { + i__3 = i__ + j * c_dim1; + z__2.real(alpha->real() * temp.real() - + alpha->imag() * temp.imag()), + z__2.imag(alpha->real() * temp.imag() + + alpha->imag() * temp.real()); + i__4 = i__ + j * c_dim1; + z__3.real(beta->real() * c__[i__4].real() - + beta->imag() * c__[i__4].imag()), + z__3.imag(beta->real() * c__[i__4].imag() + + beta->imag() * c__[i__4].real()); + z__1.real(z__2.real() + z__3.real()), + z__1.imag(z__2.imag() + z__3.imag()); + c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + } + /* L300: */ + } + /* L310: */ + } + } + } else { + if (conjb) { + + /* Form C := alpha*A'*conjg( B' ) + beta*C */ + + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + temp.real(0.), temp.imag(0.); + i__3 = *k; + for (l = 1; l <= i__3; ++l) { + i__4 = l + i__ * a_dim1; + d_cnjg(&z__3, &b[j + l * b_dim1]); + z__2.real(a[i__4].real() * z__3.real() - + a[i__4].imag() * z__3.imag()), + z__2.imag(a[i__4].real() * z__3.imag() + + a[i__4].imag() * z__3.real()); + z__1.real(temp.real() + z__2.real()), + z__1.imag(temp.imag() + z__2.imag()); + temp.real(z__1.real()), temp.imag(z__1.imag()); + /* L320: */ + } + if (beta->real() == 0. && beta->imag() == 0.) { + i__3 = i__ + j * c_dim1; + z__1.real(alpha->real() * temp.real() - + alpha->imag() * temp.imag()), + z__1.imag(alpha->real() * temp.imag() + + alpha->imag() * temp.real()); + c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + } else { + i__3 = i__ + j * c_dim1; + z__2.real(alpha->real() * temp.real() - + alpha->imag() * temp.imag()), + z__2.imag(alpha->real() * temp.imag() + + alpha->imag() * temp.real()); + i__4 = i__ + j * c_dim1; + z__3.real(beta->real() * c__[i__4].real() - + beta->imag() * c__[i__4].imag()), + z__3.imag(beta->real() * c__[i__4].imag() + + beta->imag() * c__[i__4].real()); + z__1.real(z__2.real() + z__3.real()), + z__1.imag(z__2.imag() + z__3.imag()); + c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + } + /* L330: */ + } + /* L340: */ + } + } else { + + /* Form C := alpha*A'*B' + beta*C */ + + i__1 = *n; + for (j = 1; j <= i__1; ++j) { + i__2 = *m; + for (i__ = 1; i__ <= i__2; ++i__) { + temp.real(0.), temp.imag(0.); + i__3 = *k; + for (l = 1; l <= i__3; ++l) { + i__4 = l + i__ * a_dim1; + i__5 = j + l * b_dim1; + z__2.real(a[i__4].real() * b[i__5].real() - + a[i__4].imag() * b[i__5].imag()), + z__2.imag(a[i__4].real() * b[i__5].imag() + + a[i__4].imag() * b[i__5].real()); + z__1.real(temp.real() + z__2.real()), + z__1.imag(temp.imag() + z__2.imag()); + temp.real(z__1.real()), temp.imag(z__1.imag()); + /* L350: */ + } + if (beta->real() == 0. && beta->imag() == 0.) { + i__3 = i__ + j * c_dim1; + z__1.real(alpha->real() * temp.real() - + alpha->imag() * temp.imag()), + z__1.imag(alpha->real() * temp.imag() + + alpha->imag() * temp.real()); + c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + } else { + i__3 = i__ + j * c_dim1; + z__2.real(alpha->real() * temp.real() - + alpha->imag() * temp.imag()), + z__2.imag(alpha->real() * temp.imag() + + alpha->imag() * temp.real()); + i__4 = i__ + j * c_dim1; + z__3.real(beta->real() * c__[i__4].real() - + beta->imag() * c__[i__4].imag()), + z__3.imag(beta->real() * c__[i__4].imag() + + beta->imag() * c__[i__4].real()); + z__1.real(z__2.real() + z__3.real()), + z__1.imag(z__2.imag() + z__3.imag()); + c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + } + /* L360: */ + } + /* L370: */ + } + } + } + + return 0; + + /* End of ZGEMM . */ + +} /* zgemm_ */ + +template void print(std::array, N> matrix) { + int i{}; + for (auto&& a : matrix) { + std::cerr << std::setprecision(50) << a.real() << '+' << a.imag() << "i, "; + if (++i % 4 == 0) { + std::cerr << '\n'; + } + } + std::cerr << '\n'; +} + +auto mul(qfp a, qfp b) { + return qfp((a.real() * b.real() - a.imag() * b.imag()), + (a.real() * b.imag() + a.imag() * b.real())); +} + +// Function to perform SYRK (Symmetric Rank-K update) on 4x4 matrices stored in +// std::array +matrix4x4 syrk(bool upper, fp alpha, const matrix4x4& A, fp beta) { + matrix4x4 C; + // Iterate over the matrix rows and columns + for (size_t i = 0; i < 4; ++i) { + for (size_t j = (upper ? i : 0); j < 4; ++j) { + // Compute the dot product A[i, :] * A[j, :] (real and imaginary + // separately) + qfp sum{0.0, 0.0}; // Initialize sum as a complex number (0.0 + 0.0i) + + for (size_t k = 0; k < 4; ++k) { + sum += A[i * 4 + k] * std::conj(A[j * 4 + k]); // A[i, :] * A[j, :] + } + + // Apply the SYRK update: C(i, j) = alpha * sum + beta * C(i, j) + C[i * 4 + j] = alpha * sum + beta * C[i * 4 + j]; + + // If updating the lower triangle, mirror the values from the upper + // triangle + if (!upper && i != j) { + C[j * 4 + i] = C[i * 4 + j]; // Maintain symmetry + } + } + } + return C; +} + +template +[[nodiscard]] inline auto multiply(const std::array& lhs, + const std::array& rhs) { + // return matrixMultiplyWithKahan(lhs, rhs); + std::array result{}; + const int n = std::sqrt(N); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + for (int k = 0; k < n; k++) { + std::cout << std::setprecision(17) << lhs[i * n + k] << " * " + << rhs[k * n + j] << " = " + << mul(lhs[i * n + k], rhs[k * n + j]) << '\n'; + result[i * n + j] += mul(lhs[i * n + k], rhs[k * n + j]); + std::cout << std::setprecision(50) << "->" << result[i * n + j] << '\n'; + } + std::cout << "\n===\n"; + } + } + return result; +} + +static matrix4x4 transpose(const matrix4x4& matrix) { + matrix4x4 result; + for (size_t i = 0; i < 4; ++i) { + for (size_t j = 0; j < 4; ++j) { + result[j * 4 + i] = matrix[i * 4 + j]; + } + } + return result; +} + +int main() { + + matrix4x4 a = {qfp(0.3535533905932738, +0.35355339059327373), + qfp(-0.35355339059327373, +0.3535533905932738), + qfp(-0.35355339059327373, +0.3535533905932738), + qfp(0.3535533905932738, +0.35355339059327373), + qfp(0.35355339059327373, -0.3535533905932738), + qfp(0.3535533905932738, +0.35355339059327373), + qfp(-0.3535533905932738, -0.35355339059327373), + qfp(-0.35355339059327373, +0.3535533905932738), + qfp(0.35355339059327373, -0.3535533905932738), + qfp(-0.3535533905932738, -0.35355339059327373), + qfp(0.3535533905932738, +0.35355339059327373), + qfp(-0.35355339059327373, +0.3535533905932738), + qfp(0.3535533905932738, +0.35355339059327373), + qfp(0.35355339059327373, -0.3535533905932738), + qfp(0.35355339059327373, -0.3535533905932738), + qfp(0.3535533905932738, +0.35355339059327373)}; + + // print(multiply(transpose(a), transpose(transpose(a)))); + // print(syrk(false, 1.0, transpose(a), 0.0)); + print(zgemm2(transpose(a), a)); +} From 3c214ad4ac67a245146469d1e6d5c55ffed0c78e Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 19 Oct 2025 20:26:57 +0200 Subject: [PATCH 014/237] add zgemm for testing, start euler decomposition implementation --- .../Transforms/GateDecompositionPattern.cpp | 344 +++++++++++++----- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 2 + mlir/lib/Dialect/MQTOpt/Transforms/a.cpp | 77 ++-- 3 files changed, 285 insertions(+), 138 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 85cf3ac4cc..756cf5b85f 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -136,8 +136,7 @@ struct GateDecompositionPattern final */ template static OpType createOneParameterGate(mlir::PatternRewriter& rewriter, - mlir::Location location, - fp parameter, + mlir::Location location, fp parameter, mlir::ValueRange inQubits) { auto parameterValue = rewriter.create( location, rewriter.getF64Type(), rewriter.getF64FloatAttr(parameter)); @@ -217,9 +216,8 @@ struct GateDecompositionPattern final // Wrap angle into interval [-π,π). If within atol of the endpoint, clamp // to -π - static fp - mod2pi(fp angle, - fp angleZeroEpsilon = std::numeric_limits::epsilon()) { + static fp mod2pi(fp angle, + fp angleZeroEpsilon = std::numeric_limits::epsilon()) { // remEuclid() isn't exactly the same as Python's % operator, but // because the RHS here is a constant and positive it is effectively // equivalent for this case @@ -241,11 +239,14 @@ struct GateDecompositionPattern final return {matrix[0 * 2 + 0], matrix[1 * 2 + 0], matrix[0 * 2 + 1], matrix[1 * 2 + 1]}; } - static matrix4x4 transpose(const matrix4x4& matrix) { - matrix4x4 result; - for (size_t i = 0; i < 4; ++i) { - for (size_t j = 0; j < 4; ++j) { - result[j * 4 + i] = matrix[i * 4 + j]; + + template + static std::array transpose(const std::array& matrix) { + const std::size_t n = std::sqrt(N); + std::array result; + for (size_t i = 0; i < n; ++i) { + for (size_t j = 0; j < n; ++j) { + result[j * n + i] = matrix[i * n + j]; } } return result; @@ -342,77 +343,216 @@ struct GateDecompositionPattern final }; } - // Helper function to perform QR decomposition (simplified) - static void qrDecomposition(std::array& A, - std::array& Q, - std::array& R) { - // QR decomposition is simplified for this 4x4 case - for (size_t i = 0; i < 4; ++i) { - fp norm = 0.0; - for (size_t j = 0; j < 4; ++j) { - norm += A[j * 4 + i] * A[j * 4 + i]; - } - norm = std::sqrt(norm); + // return Q, R such that A = Q * R + static void qrDecomposition(const rmatrix4x4& A, rmatrix4x4& Q, + rmatrix4x4& R) { + // array of factor Q1, Q2, ... Qm + std::vector qv(4); - // Normalize the column - for (size_t j = 0; j < 4; ++j) { - Q[j * 4 + i] = A[j * 4 + i] / norm; - } + // temp array + auto z(A); + rmatrix4x4 z1; - // Compute the R matrix - for (size_t j = i; j < 4; ++j) { - fp dot = 0.0; - for (size_t k = 0; k < 4; ++k) { - dot += Q[k * 4 + i] * A[k * 4 + j]; - } - R[i * 4 + j] = dot; - } + auto vmadd = [](const auto& a, const auto& b, double s, auto& c) { + for (int i = 0; i < 4; i++) + c[i] = a[i] + s * b[i]; + }; - // Subtract to make A orthogonal - for (size_t j = 0; j < 4; ++j) { - for (size_t k = i; k < 4; ++k) { - A[j * 4 + k] -= Q[j * 4 + i] * R[i * 4 + k]; - } + auto compute_householder_factor = [](rmatrix4x4& mat, + const rdiagonal4x4& v) { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + mat[i + 4 * j] = -2.0 * v[i] * v[j]; + for (int i = 0; i < 4; i++) + mat[i * 4 + i] += 1; + }; + + // take c-th column of a matrix, put results in Vector v + auto extract_column = [](const rmatrix4x4& m, rdiagonal4x4& v, int c) { + for (int i = 0; i < 4; i++) + v[i] = m[i + 4 * c]; + }; + + auto compute_minor = [](rmatrix4x4& lhs, const rmatrix4x4& rhs, int d) { + for (int i = 0; i < d; i++) + lhs[i * 4 + i] = 1.0; + for (int i = d; i < 4; i++) + for (int j = d; j < 4; j++) + lhs[i + 4 * j] = rhs[i + 4 * j]; + }; + + auto norm = [](auto&& m) { + double sum = 0; + for (int i = 0; i < m.size(); i++) + sum += m[i] * m[i]; + return sqrt(sum); + }; + + auto rescale_unit = [&](auto& m) { + auto factor = norm(m); + for (int i = 0; i < m.size(); i++) + m[i] /= factor; + }; + + for (int k = 0; k < 4 && k < 4 - 1; k++) { + + rdiagonal4x4 e{}, x{}; + double a{}; + + // compute minor + compute_minor(z1, z, k); + + // extract k-th column into x + extract_column(z1, x, k); + + a = norm(x); + if (A[k * 4 + k] > 0) + a = -a; + + for (int i = 0; i < 4; i++) + e[i] = (i == k) ? 1 : 0; + + // e = x + a*e + vmadd(x, e, a, e); + + // e = e / ||e|| + rescale_unit(e); + + // qv[k] = I - 2 *e*e^T + compute_householder_factor(qv[k], e); + + // z = qv[k] * z1 + z = helpers::multiply(qv[k], z1); + } + + Q = qv[0]; + + // after this loop, we will obtain Q (up to a transpose operation) + for (int i = 1; i < 4 && i < 4 - 1; i++) { + + z1 = helpers::multiply(qv[i], Q); + Q = z1; + } + + R = helpers::multiply(Q, A); + Q = transpose(Q); + } + + void tridiagonalization_inplace(rmatrix4x4& mat, CoeffVectorType& hCoeffs) { + auto n = 4; + + auto makeHouseholder = [](llvm::SmallVector& essential, fp& tau, fp& beta) { + std::vector tail { essential.begin() + 1, essential.end() }; + + auto squaredNorm = [](auto&& v) { + qfp sum{}; + for (auto&& x : v) { + sum += qfp(std::real(x) * std::real(x), std::imag(x) * std::imag(x)); } + return sum.real() + sum.imag(); + }; + + auto tailSqNorm = essential.size() == 1 ? 0.0 : squaredNorm(tail); + fp c0 = essential[0]; + const fp tol = (std::numeric_limits::min)(); + + if (tailSqNorm <= tol && std::norm(std::imag(c0)) <= tol) { + tau = 0; + beta = std::real(c0); + llvm::fill(essential, 0); + } else { + beta = std::sqrt(std::norm(c0) + tailSqNorm); + if (std::real(c0) >= fp(0)) beta = -beta; + for (std::size_t i = 0; i < essential.size(); ++i) { + essential[i] = tail[i] / (c0 - beta); + } + tau = (beta - c0) / beta; // TODO: std::conj for complex? + } + }; + + for (auto i = 0; i < n - 1; ++i) { + auto remainingSize = n - i - 1; + fp beta; + fp h; + + // matA.col(i).tail(remainingSize).makeHouseholderInPlace(h, beta); + llvm::SmallVector tmp; + for (int j = n - remainingSize; j < n; ++j) { + tmp.push_back(mat[j * n + i]); } + makeHouseholder(tmp, h, beta); + + // Apply similarity transformation to remaining columns, + // i.e., A = H A H' where H = I - h v v' and v = matA.col(i).tail(n-i-1) + matA.col(i).coeffRef(i + 1) = fp(1); + + hCoeffs.tail(n - i - 1).noalias() = + (matA.bottomRightCorner(remainingSize, remainingSize).template selfadjointView() * + (conj(h) * matA.col(i).tail(remainingSize))); + + hCoeffs.tail(n - i - 1) += + (conj(h) * RealScalar(-0.5) * (hCoeffs.tail(remainingSize).dot(matA.col(i).tail(remainingSize)))) * + matA.col(i).tail(n - i - 1); + + matA.bottomRightCorner(remainingSize, remainingSize) + .template selfadjointView() + .rankUpdate(matA.col(i).tail(remainingSize), hCoeffs.tail(remainingSize), Scalar(-1)); + + matA.col(i).coeffRef(i + 1) = beta; + hCoeffs.coeffRef(i) = h; } +} // Function to perform self-adjoint eigenvalue decomposition (4x4 matrix) - static std::array // eigenvectors (4x4) - self_adjoint_evd(std::array& A, // input symmetric matrix (4x4) - std::array& s // eigenvalues + static rmatrix4x4 // eigenvectors (4x4) + self_adjoint_evd(rmatrix4x4 A, // input symmetric matrix (4x4) + rdiagonal4x4& s // eigenvalues ) { - // Step 1: Zero out the upper triangle (we are only interested in the lower - // half) - for (size_t i = 0; i < 4; ++i) { - for (size_t j = i + 1; j < 4; ++j) { - A[i * 4 + j] = 0; // Set the upper triangle to zero + rmatrix4x4 U = {1, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 1}; // Start with identity + + auto isConverged = [](const rmatrix4x4& A, double tol = 1e-10) -> bool { + double sum = 0.0; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + if (i != j) + sum += A[i + 4 * j] * A[i + 4 * j]; + } } - } + return std::sqrt(sum) < tol; + }; - // Step 2: Perform QR decomposition - std::array Q, R; - for (int iter = 0; iter < 1000; - ++iter) { // Arbitrary number of iterations for convergence + rmatrix4x4 Q{}; + rmatrix4x4 R{}; + + constexpr auto maxIters = 100; + for (int iter = 0; iter < maxIters; ++iter) { qrDecomposition(A, Q, R); - A = helpers::multiply(R, Q); // Update A = R * Q - } - // Step 3: Extract eigenvalues (diagonal of the matrix) - for (size_t i = 0; i < 4; ++i) { - s[i] = A[i * 4 + i]; // Eigenvalues are the diagonal elements + // A = R * Q + A = helpers::multiply(R, Q); + + // eigenVectors = eigenVectors * Q + U = helpers::multiply(U, Q); + + if (isConverged(A)) { + break; + } } - return Q; + for (int i = 0; i < 4; ++i) { + s[i] = A[i * 4 + i]; + } + return U; } // https://docs.rs/faer/latest/faer/mat/generic/struct.Mat.html#method.self_adjoint_eigen - static std::array + static std::pair, std::array> self_adjoint_eigen_lower(std::array A) { std::array S; auto U = self_adjoint_evd(A, S); - return U; + return {U, S}; } static std::tuple @@ -516,7 +656,7 @@ struct GateDecompositionPattern final } static std::array angles_from_unitary(const matrix2x2& matrix, - EulerBasis basis) { + EulerBasis basis) { if (basis == EulerBasis::XYX) { return params_xyx_inner(matrix); } @@ -543,14 +683,18 @@ struct GateDecompositionPattern final static std::array params_xyx_inner(const matrix2x2& matrix) { auto mat_zyz = std::array{ - static_cast(0.5) * (matrix.at(0 * 2 + 0) + matrix.at(0 * 2 + 1 * 2 * 2) + - matrix.at(1 * 2 + 0) + matrix.at(1 * 2 + 1)), - static_cast(0.5) * (matrix.at(0 * 2 + 0) - matrix.at(0 * 2 + 1 * 2 * 2) + - matrix.at(1 * 2 + 0) - matrix.at(1 * 2 + 1)), - static_cast(0.5) * (matrix.at(0 * 2 + 0) + matrix.at(0 * 2 + 1 * 2 * 2) - - matrix.at(1 * 2 + 0) - matrix.at(1 * 2 + 1)), - static_cast(0.5) * (matrix.at(0 * 2 + 0) - matrix.at(0 * 2 + 1 * 2 * 2) - - matrix.at(1 * 2 + 0) + matrix.at(1 * 2 + 1)), + static_cast(0.5) * + (matrix.at(0 * 2 + 0) + matrix.at(0 * 2 + 1 * 2 * 2) + + matrix.at(1 * 2 + 0) + matrix.at(1 * 2 + 1)), + static_cast(0.5) * + (matrix.at(0 * 2 + 0) - matrix.at(0 * 2 + 1 * 2 * 2) + + matrix.at(1 * 2 + 0) - matrix.at(1 * 2 + 1)), + static_cast(0.5) * + (matrix.at(0 * 2 + 0) + matrix.at(0 * 2 + 1 * 2 * 2) - + matrix.at(1 * 2 + 0) - matrix.at(1 * 2 + 1)), + static_cast(0.5) * + (matrix.at(0 * 2 + 0) - matrix.at(0 * 2 + 1 * 2 * 2) - + matrix.at(1 * 2 + 0) + matrix.at(1 * 2 + 1)), }; auto [theta, phi, lam, phase] = params_zyz_inner(mat_zyz); auto new_phi = mod2pi(phi + qc::PI, 0.); @@ -569,12 +713,9 @@ struct GateDecompositionPattern final static constexpr qfp IM{0., 1.}; static constexpr qfp M_IM{0., -1.}; - static constexpr std::array IPZ = {IM, C_ZERO, - C_ZERO, M_IM}; - static constexpr std::array IPY = {C_ZERO, C_ONE, - C_M_ONE, C_ZERO}; - static constexpr std::array IPX = {C_ZERO, IM, IM, - C_ZERO}; + static constexpr std::array IPZ = {IM, C_ZERO, C_ZERO, M_IM}; + static constexpr std::array IPY = {C_ZERO, C_ONE, C_M_ONE, C_ZERO}; + static constexpr std::array IPX = {C_ZERO, IM, IM, C_ZERO}; static matrix2x2 getSingleQubitMatrix(const QubitGateSequence::Gate& gate) { if (gate.type == qc::SX) { @@ -623,10 +764,10 @@ struct GateDecompositionPattern final fp b; fp c; fp global_phase; - std::array, 4> K1l; - std::array, 4> K2l; - std::array, 4> K1r; - std::array, 4> K2r; + std::array K1l; + std::array K2l; + std::array K1r; + std::array K2r; Specialization specialization; EulerBasis default_euler_basis; std::optional requested_fidelity; @@ -638,7 +779,7 @@ struct GateDecompositionPattern final std::optional _specialization) { auto& u = unitary_matrix; auto det_u = determinant(u); - auto det_pow = std::pow(det_u, -fp(0.25)); + auto det_pow = std::pow(det_u, static_cast(-0.25)); llvm::transform(u, u.begin(), [&](auto&& x) { return x * det_pow; }); llvm::errs() << "===== U =====\n"; helpers::print(u); @@ -690,8 +831,7 @@ struct GateDecompositionPattern final llvm::transform(m2, m2_real.begin(), [&](const qfp& val) { return rand_a * val.real() + rand_b * val.imag(); }); - auto p_inner_real = std::get<1>( - helpers::LUdecomposition(self_adjoint_eigen_lower(m2_real))); + auto p_inner_real = self_adjoint_eigen_lower(m2_real).first; matrix4x4 p_inner; llvm::transform(p_inner_real, p_inner.begin(), [](auto&& x) { return qfp(x, 0.0); }); @@ -704,7 +844,7 @@ struct GateDecompositionPattern final auto compare = dot(dot(p_inner, diag_d), transpose(p_inner)); found = llvm::all_of_zip(compare, m2, [](auto&& a, auto&& b) { - return std::abs(a - b) < 1.0e-13; + return std::abs(a - b) <= 1.0e-13; }); if (found) { p = p_inner; @@ -826,8 +966,9 @@ struct GateDecompositionPattern final auto da = a - ap; auto db = b - bp; auto dc = c - cp; - auto tr = static_cast(4.) * qfp(std::cos(da) * std::cos(db) * std::cos(dc), - std::sin(da) * std::sin(db) * std::sin(dc)); + auto tr = static_cast(4.) * + qfp(std::cos(da) * std::cos(db) * std::cos(dc), + std::sin(da) * std::sin(db) * std::sin(dc)); if (fidelity) { return trace_to_fid(tr) >= *fidelity; } @@ -1153,13 +1294,15 @@ struct GateDecompositionPattern final b - specialized.b, -c - specialized.c, }; - return static_cast(4.) * qfp(std::cos(da) * std::cos(db) * std::cos(dc), - std::sin(da) * std::sin(db) * std::sin(dc)); + return static_cast(4.) * + qfp(std::cos(da) * std::cos(db) * std::cos(dc), + std::sin(da) * std::sin(db) * std::sin(dc)); } else { auto [da, db, dc] = std::array{a - specialized.a, b - specialized.b, c - specialized.c}; - return static_cast(4.) * qfp(std::cos(da) * std::cos(db) * std::cos(dc), - std::sin(da) * std::sin(db) * std::sin(dc)); + return static_cast(4.) * + qfp(std::cos(da) * std::cos(db) * std::cos(dc), + std::sin(da) * std::sin(db) * std::sin(dc)); } }; auto tr = get_tr(); @@ -1214,8 +1357,7 @@ struct GateDecompositionPattern final const std::vector& gate_params = {}, matrix4x4 gate_matrix = {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, // CX matrix - fp basis_fidelity = 1.0, - EulerBasis euler_basis = EulerBasis::ZYZ, + fp basis_fidelity = 1.0, EulerBasis euler_basis = EulerBasis::ZYZ, std::optional pulse_optimize = std::nullopt) { auto relative_eq = [](auto&& lhs, auto&& rhs, auto&& epsilon, auto&& max_relative) { @@ -1243,7 +1385,8 @@ struct GateDecompositionPattern final } return abs_diff <= abs_lhs * max_relative; }; - constexpr auto FRAC_1_SQRT_2 = static_cast(0.707106781186547524400844362104849039); + constexpr auto FRAC_1_SQRT_2 = + static_cast(0.707106781186547524400844362104849039); constexpr auto K12R_ARR = std::array{ qfp(0., FRAC_1_SQRT_2), qfp(FRAC_1_SQRT_2, 0.), @@ -1785,12 +1928,13 @@ struct GateDecompositionPattern final static_cast(4.) * qfp(std::cos(target.a) * std::cos(target.b) * std::cos(target.c), std::sin(target.a) * std::sin(target.b) * std::sin(target.c)), - static_cast(4.) * qfp(std::cos(qc::PI_4 - target.a) * - std::cos(basis_decomposer.b - target.b) * - std::cos(target.c), - std::sin(qc::PI_4 - target.a) * - std::sin(basis_decomposer.b - target.b) * - std::sin(target.c)), + static_cast(4.) * + qfp(std::cos(qc::PI_4 - target.a) * + std::cos(basis_decomposer.b - target.b) * + std::cos(target.c), + std::sin(qc::PI_4 - target.a) * + std::sin(basis_decomposer.b - target.b) * + std::sin(target.c)), qfp(4. * std::cos(target.c), 0.), qfp(4., 0.), }; @@ -1862,9 +2006,9 @@ struct GateDecompositionPattern final * indicating that they have been altered from the originals. */ [[nodiscard]] static OneQubitGateSequence - calculateRotationGates(fp theta, fp phi, fp lambda, - fp phase, qc::OpType kGate, qc::OpType aGate, - bool simplify, std::optional atol) { + calculateRotationGates(fp theta, fp phi, fp lambda, fp phase, + qc::OpType kGate, qc::OpType aGate, bool simplify, + std::optional atol) { fp angleZeroEpsilon = atol.value_or(1e-12); if (simplify) { angleZeroEpsilon = -1.0; diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index 6880e934f9..cb302440d2 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -23,9 +23,11 @@ namespace mqt::ir::opt { using fp = long double; using qfp = std::complex; using diagonal4x4 = std::array; + using rdiagonal4x4 = std::array; using vector2d = std::vector; using matrix2x2 = std::array; using matrix4x4 = std::array; + using rmatrix4x4 = std::array; } // namespace mqt::ir::opt namespace mqt::ir::opt::helpers { diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/a.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/a.cpp index d0bd804e45..a4da79e473 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/a.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/a.cpp @@ -71,25 +71,27 @@ matrix4x4 zgemm2(matrix4x4 a, matrix4x4 b) { } int zgemm_(char* transa, char* transb, integer* m, integer* n, integer* k, - doublecomplex* alpha, doublecomplex* a, integer* lda, - doublecomplex* b, integer* ldb, doublecomplex* beta, - doublecomplex* c__, integer* ldc); + doublecomplex* alpha, matrix4x4 a, integer* lda, + matrix4x4 b, integer* ldb, doublecomplex* beta, + matrix4x4& c__, integer* ldc); matrix4x4 zgemm(matrix4x4 a, matrix4x4 b) { qfp alpha{1.0, 0.0}; qfp beta{1.0, 0.0}; int dimension = 4; - matrix4x4 result; - zgemm_("n", "n", &dimension, &dimension, &dimension, &alpha, a.data(), - &dimension, b.data(), &dimension, &beta, result.data(), &dimension); + matrix4x4 result{}; + // zgemm_("n", "n", &dimension, &dimension, &dimension, &alpha, a.data(), + // &dimension, b.data(), &dimension, &beta, result.data(), &dimension); + zgemm_("N", "N", &dimension, &dimension, &dimension, &alpha, a, + &dimension, b, &dimension, &beta, result, &dimension); return result; } void d_cnjg(doublecomplex* r, doublecomplex* z) { *r = std::conj(*z); } /* Subroutine */ int zgemm_(char* transa, char* transb, integer* m, integer* n, - integer* k, doublecomplex* alpha, doublecomplex* a, - integer* lda, doublecomplex* b, integer* ldb, - doublecomplex* beta, doublecomplex* c__, + integer* k, doublecomplex* alpha, matrix4x4 a, + integer* lda, matrix4x4 b, integer* ldb, + doublecomplex* beta, matrix4x4& c__, integer* ldc) { /* System generated locals */ integer a_dim1, a_offset, b_dim1, b_offset, c_dim1, c_offset, i__1, i__2, @@ -242,13 +244,13 @@ void d_cnjg(doublecomplex* r, doublecomplex* z) { *r = std::conj(*z); } /* Parameter adjustments */ a_dim1 = *lda; a_offset = 1 + a_dim1; - a -= a_offset; + // a -= a_offset; b_dim1 = *ldb; b_offset = 1 + b_dim1; - b -= b_offset; + // b -= b_offset; c_dim1 = *ldc; c_offset = 1 + c_dim1; - c__ -= c_offset; + // c__ -= c_offset; /* Function Body */ nota = lsame_(transa, "N"); @@ -346,7 +348,7 @@ void d_cnjg(doublecomplex* r, doublecomplex* z) { *r = std::conj(*z); } i__2 = *m; for (i__ = 1; i__ <= i__2; ++i__) { i__3 = i__ + j * c_dim1; - c__[i__3].real(0.), c__[i__3].imag(0.); + c__[i__3 - c_offset].real(0.), c__[i__3 - c_offset].imag(0.); /* L50: */ } } else if (beta->real() != 1. || beta->imag() != 0.) { @@ -354,36 +356,36 @@ void d_cnjg(doublecomplex* r, doublecomplex* z) { *r = std::conj(*z); } for (i__ = 1; i__ <= i__2; ++i__) { i__3 = i__ + j * c_dim1; i__4 = i__ + j * c_dim1; - z__1.real(beta->real() * c__[i__4].real() - - beta->imag() * c__[i__4].imag()), - z__1.imag(beta->real() * c__[i__4].imag() + - beta->imag() * c__[i__4].real()); - c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + z__1.real(beta->real() * c__[i__4 - c_offset].real() - + beta->imag() * c__[i__4 - c_offset].imag()), + z__1.imag(beta->real() * c__[i__4 - c_offset].imag() + + beta->imag() * c__[i__4 - c_offset].real()); + c__[i__3 - c_offset].real(z__1.real()), c__[i__3 - c_offset].imag(z__1.imag()); /* L60: */ } } i__2 = *k; for (l = 1; l <= i__2; ++l) { i__3 = l + j * b_dim1; - if (b[i__3].real() != 0. || b[i__3].imag() != 0.) { + if (b[i__3 - b_offset].real() != 0. || b[i__3 - b_offset].imag() != 0.) { i__3 = l + j * b_dim1; - z__1.real(alpha->real() * b[i__3].real() - - alpha->imag() * b[i__3].imag()), - z__1.imag(alpha->real() * b[i__3].imag() + - alpha->imag() * b[i__3].real()); + z__1.real(alpha->real() * b[i__3 - b_offset].real() - + alpha->imag() * b[i__3 - b_offset].imag()), + z__1.imag(alpha->real() * b[i__3 - b_offset].imag() + + alpha->imag() * b[i__3 - b_offset].real()); temp.real(z__1.real()), temp.imag(z__1.imag()); i__3 = *m; for (i__ = 1; i__ <= i__3; ++i__) { i__4 = i__ + j * c_dim1; i__5 = i__ + j * c_dim1; i__6 = i__ + l * a_dim1; - z__2.real(temp.real() * a[i__6].real() - - temp.imag() * a[i__6].imag()), - z__2.imag(temp.real() * a[i__6].imag() + - temp.imag() * a[i__6].real()); - z__1.real(c__[i__5].real() + z__2.real()), - z__1.imag(c__[i__5].imag() + z__2.imag()); - c__[i__4].real(z__1.real()), c__[i__4].imag(z__1.imag()); + z__2.real(temp.real() * a[i__6 - a_offset].real() - + temp.imag() * a[i__6 - a_offset].imag()), + z__2.imag(temp.real() * a[i__6 - a_offset].imag() + + temp.imag() * a[i__6 - a_offset].real()); + z__1.real(c__[i__5 - c_offset].real() + z__2.real()), + z__1.imag(c__[i__5 - c_offset].imag() + z__2.imag()); + c__[i__4 - c_offset].real(z__1.real()), c__[i__4 - c_offset].imag(z__1.imag()); /* L70: */ } } @@ -419,7 +421,7 @@ void d_cnjg(doublecomplex* r, doublecomplex* z) { *r = std::conj(*z); } alpha->imag() * temp.imag()), z__1.imag(alpha->real() * temp.imag() + alpha->imag() * temp.real()); - c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + c__[i__3 - c_offset].real(z__1.real()), c__[i__3 - c_offset].imag(z__1.imag()); } else { i__3 = i__ + j * c_dim1; z__2.real(alpha->real() * temp.real() - @@ -427,13 +429,13 @@ void d_cnjg(doublecomplex* r, doublecomplex* z) { *r = std::conj(*z); } z__2.imag(alpha->real() * temp.imag() + alpha->imag() * temp.real()); i__4 = i__ + j * c_dim1; - z__3.real(beta->real() * c__[i__4].real() - - beta->imag() * c__[i__4].imag()), - z__3.imag(beta->real() * c__[i__4].imag() + - beta->imag() * c__[i__4].real()); + z__3.real(beta->real() * c__[i__4 - c_offset].real() - + beta->imag() * c__[i__4 - c_offset].imag()), + z__3.imag(beta->real() * c__[i__4 - c_offset].imag() + + beta->imag() * c__[i__4 - c_offset].real()); z__1.real(z__2.real() + z__3.real()), z__1.imag(z__2.imag() + z__3.imag()); - c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); + c__[i__3 - c_offset].real(z__1.real()), c__[i__3 - c_offset].imag(z__1.imag()); } /* L110: */ } @@ -879,7 +881,6 @@ static matrix4x4 transpose(const matrix4x4& matrix) { } int main() { - matrix4x4 a = {qfp(0.3535533905932738, +0.35355339059327373), qfp(-0.35355339059327373, +0.3535533905932738), qfp(-0.35355339059327373, +0.3535533905932738), @@ -899,5 +900,5 @@ int main() { // print(multiply(transpose(a), transpose(transpose(a)))); // print(syrk(false, 1.0, transpose(a), 0.0)); - print(zgemm2(transpose(a), a)); + print(zgemm(transpose(a), a)); } From 62aa91879254f5c47069aea32eab090cc962eccc Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 21 Oct 2025 22:15:51 +0200 Subject: [PATCH 015/237] non-working but finished snapshot --- .../Transforms/GateDecompositionPattern.cpp | 436 +++++----------- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 268 +++++++++- mlir/lib/Dialect/MQTOpt/Transforms/b.h | 474 ++++++++++++++++++ 3 files changed, 838 insertions(+), 340 deletions(-) create mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/b.h diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 756cf5b85f..959aa2c7e6 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -25,6 +25,8 @@ #include #include +#include "b.h" + namespace mqt::ir::opt { /** @@ -46,7 +48,7 @@ struct GateDecompositionPattern final return mlir::failure(); } - matrix4x4 unitaryMatrix = kroneckerProduct(identityGate, identityGate); + matrix4x4 unitaryMatrix = helpers::kroneckerProduct(identityGate, identityGate); for (auto&& gate : series) { auto gateMatrix = getTwoQubitMatrix({.type = helpers::getQcType(gate), .parameter = {/*TODO*/}, @@ -228,335 +230,109 @@ struct GateDecompositionPattern final return wrapped; } - static matrix2x2 dot(const matrix2x2& lhs, const matrix2x2& rhs) { - return helpers::multiply(lhs, rhs); - } - static matrix4x4 dot(const matrix4x4& lhs, const matrix4x4& rhs) { - return helpers::multiply(lhs, rhs); - } - - static matrix2x2 transpose(const matrix2x2& matrix) { - return {matrix[0 * 2 + 0], matrix[1 * 2 + 0], matrix[0 * 2 + 1], - matrix[1 * 2 + 1]}; - } - - template - static std::array transpose(const std::array& matrix) { - const std::size_t n = std::sqrt(N); - std::array result; - for (size_t i = 0; i < n; ++i) { - for (size_t j = 0; j < n; ++j) { - result[j * n + i] = matrix[i * n + j]; - } - } - return result; - } - - static matrix2x2 transpose_conjugate(const matrix2x2& matrix) { - auto result = transpose(matrix); - llvm::transform(result, result.begin(), - [](auto&& x) { return std::conj(x); }); - return result; - }; - - static qfp determinant(const matrix2x2& mat) { - return mat[0] * mat[3] - mat[1] * mat[2]; - } - - static qfp determinant(const std::array& mat) { - return mat[0] * (mat[4] * mat[8] - mat[5] * mat[7]) - - mat[1] * (mat[3] * mat[8] - mat[5] * mat[6]) + - mat[2] * (mat[3] * mat[7] - mat[4] * mat[6]); - } - - static std::array get3x3Submatrix(const matrix4x4& mat, - int rowToBeRemoved, - int columnToBeRemoved) { - std::array, 9> result; - int subIndex = 0; - for (int i = 0; i < 4; ++i) { - if (i != rowToBeRemoved) { - for (int j = 0; j < 4; ++j) { - if (j != columnToBeRemoved) { - result[subIndex++] = mat[i * 4 + j]; + // GPT generated + void tridiagonalize_inplace(rmatrix4x4& A) { + auto dot3 = [](auto&& a, auto&& b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + }; + // Householder for column 0 + { + auto x = std::array{A[1 * 4 + 0], A[8 + 0], + A[12 + 0]}; // Elements [1,0], [2,0], [3,0] + auto norm_x = std::sqrt(dot3(x, x)); + if (norm_x > 1e-12) { + auto sign = (x[0] >= 0) ? 1.0 : -1.0; + auto u0 = x[0] + sign * norm_x; + auto denom = std::sqrt(u0 * u0 + x[1] * x[1] + x[2] * x[2]); + auto v = std::array{u0 / denom, x[1] / denom, x[2] / denom}; + + // Apply H = I - 2vvᵀ to A from both sides: A := H A H + // Only affect rows and columns 1-3 + + // temp = A * v + std::array temp = {0, 0, 0}; + for (int i = 1; i < 4; ++i) { + for (int j = 1; j < 4; ++j) { + temp[i - 1] += A[i * 4 + j] * v[j - 1]; } } - } - } - return result; - } - - static qfp determinant(const matrix4x4& mat) { - auto [l, u, rowPermutations] = helpers::LUdecomposition(mat); - auto det = C_ONE; - for (int i = 0; i < 4; ++i) { - det *= l[i * 4 + i]; - } - - if (rowPermutations % 2 != 0) { - det = -det; - } - return det; - - // auto det = -C_ZERO; - // for (int column = 0; column < 4; ++column) { - // auto submatrix = get3x3Submatrix(mat, 0, column); - // auto subDet = determinant(submatrix); - // auto tmp = mat[0 * 4 + column] * subDet; - // if (column % 2 == 0 && - // tmp != - // C_ZERO) { // TODO: better way to get negative 0.0 in - // determinant? - // det += tmp; - // } else if (tmp != -C_ZERO) { - // det -= tmp; - // } - // } - // return det; - } - - static matrix2x2 multiply(qfp factor, matrix2x2 matrix) { - llvm::transform(matrix, matrix.begin(), - [&](auto&& x) { return factor * x; }); - return matrix; - } - - static matrix4x4 kroneckerProduct(const matrix2x2& lhs, - const matrix2x2& rhs) { - return from(multiply(lhs[0 * 2 + 0], rhs), multiply(lhs[0 * 2 + 1], rhs), - multiply(lhs[1 * 2 + 0], rhs), multiply(lhs[1 * 2 + 1], rhs)); - } - - static matrix4x4 from(const matrix2x2& first_quadrant, - const matrix2x2& second_quadrant, - const matrix2x2& third_quadrant, - const matrix2x2& fourth_quadrant) { - return { - first_quadrant[0 * 2 + 0], first_quadrant[0 * 2 + 1], - second_quadrant[0 * 2 + 0], second_quadrant[0 * 2 + 1], - first_quadrant[1 * 2 + 0], first_quadrant[1 * 2 + 1], - second_quadrant[1 * 2 + 0], second_quadrant[1 * 2 + 1], - third_quadrant[0 * 2 + 0], third_quadrant[0 * 2 + 1], - fourth_quadrant[0 * 2 + 0], fourth_quadrant[0 * 2 + 1], - third_quadrant[1 * 2 + 0], third_quadrant[1 * 2 + 1], - fourth_quadrant[1 * 2 + 0], fourth_quadrant[1 * 2 + 1], - }; - } - - // return Q, R such that A = Q * R - static void qrDecomposition(const rmatrix4x4& A, rmatrix4x4& Q, - rmatrix4x4& R) { - // array of factor Q1, Q2, ... Qm - std::vector qv(4); - - // temp array - auto z(A); - rmatrix4x4 z1; - - auto vmadd = [](const auto& a, const auto& b, double s, auto& c) { - for (int i = 0; i < 4; i++) - c[i] = a[i] + s * b[i]; - }; - - auto compute_householder_factor = [](rmatrix4x4& mat, - const rdiagonal4x4& v) { - for (int i = 0; i < 4; i++) - for (int j = 0; j < 4; j++) - mat[i + 4 * j] = -2.0 * v[i] * v[j]; - for (int i = 0; i < 4; i++) - mat[i * 4 + i] += 1; - }; - - // take c-th column of a matrix, put results in Vector v - auto extract_column = [](const rmatrix4x4& m, rdiagonal4x4& v, int c) { - for (int i = 0; i < 4; i++) - v[i] = m[i + 4 * c]; - }; - - auto compute_minor = [](rmatrix4x4& lhs, const rmatrix4x4& rhs, int d) { - for (int i = 0; i < d; i++) - lhs[i * 4 + i] = 1.0; - for (int i = d; i < 4; i++) - for (int j = d; j < 4; j++) - lhs[i + 4 * j] = rhs[i + 4 * j]; - }; - - auto norm = [](auto&& m) { - double sum = 0; - for (int i = 0; i < m.size(); i++) - sum += m[i] * m[i]; - return sqrt(sum); - }; - - auto rescale_unit = [&](auto& m) { - auto factor = norm(m); - for (int i = 0; i < m.size(); i++) - m[i] /= factor; - }; - - for (int k = 0; k < 4 && k < 4 - 1; k++) { - - rdiagonal4x4 e{}, x{}; - double a{}; - - // compute minor - compute_minor(z1, z, k); - - // extract k-th column into x - extract_column(z1, x, k); - a = norm(x); - if (A[k * 4 + k] > 0) - a = -a; - - for (int i = 0; i < 4; i++) - e[i] = (i == k) ? 1 : 0; - - // e = x + a*e - vmadd(x, e, a, e); - - // e = e / ||e|| - rescale_unit(e); - - // qv[k] = I - 2 *e*e^T - compute_householder_factor(qv[k], e); - - // z = qv[k] * z1 - z = helpers::multiply(qv[k], z1); - } - - Q = qv[0]; - - // after this loop, we will obtain Q (up to a transpose operation) - for (int i = 1; i < 4 && i < 4 - 1; i++) { - - z1 = helpers::multiply(qv[i], Q); - Q = z1; - } - - R = helpers::multiply(Q, A); - Q = transpose(Q); - } - - void tridiagonalization_inplace(rmatrix4x4& mat, CoeffVectorType& hCoeffs) { - auto n = 4; - - auto makeHouseholder = [](llvm::SmallVector& essential, fp& tau, fp& beta) { - std::vector tail { essential.begin() + 1, essential.end() }; - - auto squaredNorm = [](auto&& v) { - qfp sum{}; - for (auto&& x : v) { - sum += qfp(std::real(x) * std::real(x), std::imag(x) * std::imag(x)); + // w = 2 * (A*v - (vᵀ*A*v)*v) + auto alpha = dot3(v, temp); + std::array w; + for (int i = 0; i < 3; ++i) + w[i] = 2.0 * (temp[i] - alpha * v[i]); + + // Update A = A - v wᵀ - w vᵀ + for (int i = 1; i < 4; ++i) { + for (int j = i; j < 4; ++j) { + auto delta = v[i - 1] * w[j - 1] + w[i - 1] * v[j - 1]; + A[i * 4 + j] -= delta; + if (i != j) + A[j * 4 + i] -= delta; // symmetry + } + } } - return sum.real() + sum.imag(); - }; - - auto tailSqNorm = essential.size() == 1 ? 0.0 : squaredNorm(tail); - fp c0 = essential[0]; - const fp tol = (std::numeric_limits::min)(); - - if (tailSqNorm <= tol && std::norm(std::imag(c0)) <= tol) { - tau = 0; - beta = std::real(c0); - llvm::fill(essential, 0); - } else { - beta = std::sqrt(std::norm(c0) + tailSqNorm); - if (std::real(c0) >= fp(0)) beta = -beta; - for (std::size_t i = 0; i < essential.size(); ++i) { - essential[i] = tail[i] / (c0 - beta); } - tau = (beta - c0) / beta; // TODO: std::conj for complex? - } - }; - - for (auto i = 0; i < n - 1; ++i) { - auto remainingSize = n - i - 1; - fp beta; - fp h; - - // matA.col(i).tail(remainingSize).makeHouseholderInPlace(h, beta); - llvm::SmallVector tmp; - for (int j = n - remainingSize; j < n; ++j) { - tmp.push_back(mat[j * n + i]); - } - makeHouseholder(tmp, h, beta); - - // Apply similarity transformation to remaining columns, - // i.e., A = H A H' where H = I - h v v' and v = matA.col(i).tail(n-i-1) - matA.col(i).coeffRef(i + 1) = fp(1); - hCoeffs.tail(n - i - 1).noalias() = - (matA.bottomRightCorner(remainingSize, remainingSize).template selfadjointView() * - (conj(h) * matA.col(i).tail(remainingSize))); - - hCoeffs.tail(n - i - 1) += - (conj(h) * RealScalar(-0.5) * (hCoeffs.tail(remainingSize).dot(matA.col(i).tail(remainingSize)))) * - matA.col(i).tail(n - i - 1); - - matA.bottomRightCorner(remainingSize, remainingSize) - .template selfadjointView() - .rankUpdate(matA.col(i).tail(remainingSize), hCoeffs.tail(remainingSize), Scalar(-1)); - - matA.col(i).coeffRef(i + 1) = beta; - hCoeffs.coeffRef(i) = h; - } -} - - // Function to perform self-adjoint eigenvalue decomposition (4x4 matrix) - static rmatrix4x4 // eigenvectors (4x4) - self_adjoint_evd(rmatrix4x4 A, // input symmetric matrix (4x4) - rdiagonal4x4& s // eigenvalues - ) { - rmatrix4x4 U = {1, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 1}; // Start with identity - - auto isConverged = [](const rmatrix4x4& A, double tol = 1e-10) -> bool { - double sum = 0.0; - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 4; ++j) { - if (i != j) - sum += A[i + 4 * j] * A[i + 4 * j]; + // Householder for column 1 (submatrix 2x2 at bottom right) + { + auto x = std::array{A[10], A[15]}; // Elements [2,1], [3,1] + auto norm_x = std::sqrt(x[0] * x[0] + x[1] * x[1]); + if (norm_x > 1e-12) { + auto sign = (x[0] >= 0) ? 1.0 : -1.0; + auto u0 = x[0] + sign * norm_x; + auto denom = std::sqrt(u0 * u0 + x[1] * x[1]); + auto v = std::array{u0 / denom, x[1] / denom}; + + // temp = A * v + std::array temp = {0, 0}; + for (int i = 2; i < 4; ++i) { + for (int j = 2; j < 4; ++j) { + temp[i - 2] += A[i * 4 + j] * v[j - 2]; + } } - } - return std::sqrt(sum) < tol; - }; - - rmatrix4x4 Q{}; - rmatrix4x4 R{}; - - constexpr auto maxIters = 100; - for (int iter = 0; iter < maxIters; ++iter) { - qrDecomposition(A, Q, R); - - // A = R * Q - A = helpers::multiply(R, Q); - - // eigenVectors = eigenVectors * Q - U = helpers::multiply(U, Q); - if (isConverged(A)) { - break; + // w = 2 * (A*v - (vᵀ*A*v)*v) + auto alpha = v[0] * temp[0] + v[1] * temp[1]; + std::array w; + for (int i = 0; i < 2; ++i) + w[i] = 2.0 * (temp[i] - alpha * v[i]); + + // Update A = A - v wᵀ - w vᵀ + for (int i = 2; i < 4; ++i) { + for (int j = i; j < 4; ++j) { + auto delta = v[i - 2] * w[j - 2] + w[i - 2] * v[j - 2]; + A[i * 4 + j] -= delta; + if (i != j) + A[j * 4 + i] -= delta; + } + } } } - for (int i = 0; i < 4; ++i) { - s[i] = A[i * 4 + i]; - } - return U; + // Now A is tridiagonal } - // https://docs.rs/faer/latest/faer/mat/generic/struct.Mat.html#method.self_adjoint_eigen - static std::pair, std::array> - self_adjoint_eigen_lower(std::array A) { - std::array S; - auto U = self_adjoint_evd(A, S); + static std::pair + self_adjoint_eigen_lower(rmatrix4x4 A) { + // rdiagonal4x4 S; + // auto U = self_adjoint_evd(A, S); + + // rmatrix4x4 U; + // jacobi_eigen_decomposition(A, U, S); + + auto [U, S] = self_adjoint_evd(A); return {U, S}; } static std::tuple decompose_two_qubit_product_gate(matrix4x4 special_unitary) { + using helpers::dot; + using helpers::kroneckerProduct; + using helpers::transpose_conjugate; + using helpers::determinant; // first quadrant matrix2x2 r = {special_unitary[0 * 4 + 0], special_unitary[0 * 4 + 1], special_unitary[1 * 4 + 0], special_unitary[1 * 4 + 1]}; @@ -597,13 +373,9 @@ struct GateDecompositionPattern final return {l, r, phase}; } - static diagonal4x4 diagonal(const matrix4x4& matrix) { - return {matrix[0 * 4 + 0], matrix[1 * 4 + 1], matrix[2 * 4 + 2], - matrix[3 * 4 + 3]}; - } - static matrix4x4 magic_basis_transform(const matrix4x4& unitary, MagicBasisTransform direction) { + using helpers::dot; constexpr matrix4x4 B_NON_NORMALIZED = { C_ONE, IM, C_ZERO, C_ZERO, C_ZERO, C_ZERO, IM, C_ONE, C_ZERO, C_ZERO, IM, C_M_ONE, C_ONE, M_IM, C_ZERO, C_ZERO, @@ -707,12 +479,6 @@ struct GateDecompositionPattern final }; } - static constexpr qfp C_ZERO{0., 0.}; - static constexpr qfp C_ONE{1., 0.}; - static constexpr qfp C_M_ONE{-1., 0.}; - static constexpr qfp IM{0., 1.}; - static constexpr qfp M_IM{0., -1.}; - static constexpr std::array IPZ = {IM, C_ZERO, C_ZERO, M_IM}; static constexpr std::array IPY = {C_ZERO, C_ONE, C_M_ONE, C_ZERO}; static constexpr std::array IPX = {C_ZERO, IM, IM, C_ZERO}; @@ -732,6 +498,8 @@ struct GateDecompositionPattern final } static matrix4x4 getTwoQubitMatrix(const QubitGateSequence::Gate& gate) { + using helpers::kroneckerProduct; + if (gate.qubit_id.empty()) { return kroneckerProduct(identityGate, identityGate); } @@ -777,6 +545,10 @@ struct GateDecompositionPattern final static TwoQubitWeylDecomposition new_inner(matrix4x4 unitary_matrix, std::optional fidelity, std::optional _specialization) { + using helpers::dot; + using helpers::determinant; + using helpers::transpose; + using helpers::diagonal; auto& u = unitary_matrix; auto det_u = determinant(u); auto det_pow = std::pow(det_u, static_cast(-0.25)); @@ -836,6 +608,11 @@ struct GateDecompositionPattern final llvm::transform(p_inner_real, p_inner.begin(), [](auto&& x) { return qfp(x, 0.0); }); auto d_inner = diagonal(dot(dot(transpose(p_inner), m2), p_inner)); + + llvm::errs() << "===== D_INNER =====\n"; + helpers::print(d_inner); + llvm::errs() << "===== P_INNER =====\n"; + helpers::print(p_inner); matrix4x4 diag_d{}; // zero initialization diag_d[0 * 4 + 0] = d_inner[0]; diag_d[1 * 4 + 1] = d_inner[1]; @@ -843,6 +620,8 @@ struct GateDecompositionPattern final diag_d[3 * 4 + 3] = d_inner[3]; auto compare = dot(dot(p_inner, diag_d), transpose(p_inner)); + llvm::errs() << "===== COMPARE =====\n"; + helpers::print(compare); found = llvm::all_of_zip(compare, m2, [](auto&& a, auto&& b) { return std::abs(a - b) <= 1.0e-13; }); @@ -1359,6 +1138,9 @@ struct GateDecompositionPattern final 0, 0}, // CX matrix fp basis_fidelity = 1.0, EulerBasis euler_basis = EulerBasis::ZYZ, std::optional pulse_optimize = std::nullopt) { + using helpers::dot; + using helpers::transpose_conjugate; + auto relative_eq = [](auto&& lhs, auto&& rhs, auto&& epsilon, auto&& max_relative) { // Handle same infinities @@ -1612,14 +1394,17 @@ struct GateDecompositionPattern final private: [[nodiscard]] std::vector decomp0_inner(const TwoQubitWeylDecomposition& target) const { + using helpers::dot; return { - dot(target.K1r, target.K2r), + dot(target.K1r, target.K2r), dot(target.K1l, target.K2l), }; } [[nodiscard]] std::vector decomp1_inner(const TwoQubitWeylDecomposition& target) const { + using helpers::dot; + using helpers::transpose_conjugate; // FIXME: fix for z!=0 and c!=0 using closest reflection (not always in // the Weyl chamber) return { @@ -1632,6 +1417,7 @@ struct GateDecompositionPattern final [[nodiscard]] std::vector decomp2_supercontrolled_inner( const TwoQubitWeylDecomposition& target) const { + using helpers::dot; return { dot(q2r, target.K2r), dot(q2l, target.K2l), @@ -1644,6 +1430,7 @@ struct GateDecompositionPattern final [[nodiscard]] std::vector decomp3_supercontrolled_inner( const TwoQubitWeylDecomposition& target) const { + using helpers::dot; return { dot(u3r, target.K2r), dot(u3l, target.K2l), @@ -1707,6 +1494,7 @@ struct GateDecompositionPattern final TwoQubitGateSequence get_sx_vz_2cx_efficient_euler( const std::vector& decomposition, const TwoQubitWeylDecomposition& target_decomposed) { + using helpers::dot; TwoQubitGateSequence gates{.globalPhase = target_decomposed.global_phase}; gates.globalPhase -= 2. * basis_decomposer.global_phase; @@ -1771,6 +1559,7 @@ struct GateDecompositionPattern final std::optional get_sx_vz_3cx_efficient_euler( const std::vector& decomposition, const TwoQubitWeylDecomposition& target_decomposed) { + using helpers::dot; TwoQubitGateSequence gates{.globalPhase = target_decomposed.global_phase}; gates.globalPhase -= 3. * basis_decomposer.global_phase; gates.globalPhase = remEuclid(gates.globalPhase, qc::TAU); @@ -1893,6 +1682,7 @@ struct GateDecompositionPattern final matrix4x4 compute_unitary(const TwoQubitGateSequence& sequence, fp global_phase) { + using helpers::dot; auto phase = std::exp(std::complex{0, global_phase}); matrix4x4 matrix; matrix[0 * 4 + 0] = phase; diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index cb302440d2..3ae7eaa7bd 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -20,21 +20,27 @@ #include namespace mqt::ir::opt { - using fp = long double; - using qfp = std::complex; - using diagonal4x4 = std::array; - using rdiagonal4x4 = std::array; - using vector2d = std::vector; - using matrix2x2 = std::array; - using matrix4x4 = std::array; - using rmatrix4x4 = std::array; +using fp = double; +using qfp = std::complex; +using diagonal4x4 = std::array; +using rdiagonal4x4 = std::array; +using vector2d = std::vector; +using matrix2x2 = std::array; +using matrix4x4 = std::array; +using rmatrix4x4 = std::array; + +constexpr qfp C_ZERO{0., 0.}; +constexpr qfp C_ONE{1., 0.}; +constexpr qfp C_M_ONE{-1., 0.}; +constexpr qfp IM{0., 1.}; +constexpr qfp M_IM{0., -1.}; + } // namespace mqt::ir::opt namespace mqt::ir::opt::helpers { // TODO: remove -template -void print(std::array, N> matrix) { +template void print(std::array, N> matrix) { int i{}; for (auto&& a : matrix) { std::cerr << std::setprecision(50) << a.real() << 'i' << a.imag() << ' '; @@ -211,18 +217,25 @@ std::array matrixMultiplyWithKahan(const std::array& lhs, return result; } -[[nodiscard]] inline matrix2x2 multiply(qfp factor, - const matrix2x2& matrix) { - return {factor * matrix.at(0), factor * matrix.at(1), factor * matrix.at(2), - factor * matrix.at(3)}; +template +[[nodiscard]] inline Container multiply(fp factor, Container matrix) { + llvm::transform(matrix, std::begin(matrix), + [&](auto&& x) { return factor * x; }); + return matrix; +} + +template +[[nodiscard]] inline Container multiply(qfp factor, Container matrix) { + llvm::transform(matrix, std::begin(matrix), + [&](auto&& x) { return factor * x; }); + return matrix; } template [[nodiscard]] inline auto multiply(const std::array& lhs, const std::array& rhs) { - // return matrixMultiplyWithKahan(lhs, rhs); - std::array result{{T{}}}; - const int n = std::sqrt(N); + std::array result{}; + const int n = std::sqrt(lhs.size()); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { for (int k = 0; k < n; k++) { @@ -233,6 +246,25 @@ template return result; } +template +[[nodiscard]] inline auto multiply(const std::vector& lhs, + const std::vector& rhs, int columnsLhs) { + int rowsLhs = lhs.size() / columnsLhs; + int rowsRhs = columnsLhs; + int columnsRhs = rhs.size() / rowsRhs; + + std::vector result(rowsLhs * columnsRhs, T{}); + for (int i = 0; i < rowsLhs; i++) { + for (int j = 0; j < columnsRhs; j++) { + for (int k = 0; k < columnsLhs; k++) { + result[i * columnsRhs + j] += + lhs[i * columnsLhs + k] * rhs[k * columnsRhs + j]; + } + } + } + return result; +} + template [[nodiscard]] inline auto LUdecomposition(std::array matrix) { std::array L{}; @@ -289,4 +321,206 @@ template return std::make_tuple(L, U, rowPermutations); } + +template +using ValueType = typename std::remove_cvref_t::value_type; + +template +static auto diagonal(Container&& matrix) { + const int n = std::sqrt(matrix.size()); + auto result = [&]() { + using T = std::remove_cvref_t; + if constexpr (std::is_same_v && Offset == 0) { + return diagonal4x4{}; + } else if constexpr (std::is_same_v && Offset == 0) { + return rdiagonal4x4{}; + } else { + return std::vector>(n - std::abs(Offset)); + } + }(); + for (std::size_t i = 0; i < result.size(); ++i) { + auto x = Offset > 0 ? i + Offset : i; + auto y = Offset < 0 ? i - Offset : i; + result[i] = matrix[y * n + x]; + } + return result; +} + +template +auto submatrix(Container&& matrix, int rowStart, int columnStart, int numRows, + int numColumns) { + const int n = std::sqrt(matrix.size()); + assert((rowStart + numRows) <= n); + assert((columnStart + numColumns) <= n); + + std::vector> result(numRows * numColumns); + for (int i = 0; i < numColumns; ++i) { + for (int j = 0; j < numRows; ++j) { + result[j * numColumns + i] = matrix[(rowStart + j) * n + (columnStart + i)]; + } + } + return result; +} + +template +auto assignSubmatrix(Lhs&& lhs, Rhs&& rhs, int rowStart, int columnStart, + int rowSize, int columnSize) { + const int n = std::sqrt(lhs.size()); + assert((rowStart + rowSize) < n); + assert((columnStart + columnSize) < n); + assert(columnSize * rowSize == rhs.size()); + + for (int i = 0; i < columnSize; ++i) { + for (int j = 0; j < rowSize; ++j) { + lhs[(rowStart + j) * 4 + (columnStart + i)] = rhs[j * rowSize + i]; + } + } +} + +template inline auto dot(Args&&... args) { + return helpers::multiply(std::forward(args)...); +} + +template +inline auto vectorsDot(Lhs&& lhs, Rhs&& rhs) { + return std::inner_product(lhs.begin(), lhs.end(), rhs.begin(), + typename Lhs::value_type{}); +} + +template auto add(Lhs lhs, Rhs&& rhs) { + assert(lhs.size() == rhs.size()); + for (int i = 0; i < lhs.size(); ++i) { + lhs[i] += rhs[i]; + } + return lhs; +} + +inline matrix2x2 transpose(const matrix2x2& matrix) { + return {matrix[0 * 2 + 0], matrix[1 * 2 + 0], matrix[0 * 2 + 1], + matrix[1 * 2 + 1]}; +} + +template auto transpose(Container&& matrix) { + const std::size_t n = std::sqrt(matrix.size()); + auto result{matrix}; + for (size_t i = 0; i < n; ++i) { + for (size_t j = 0; j < n; ++j) { + result[j * n + i] = matrix[i * n + j]; + } + } + return result; +} + +template auto conj(T&& x) { + using U = std::remove_cvref_t; + if constexpr (std::is_same_v) { + return std::conj(x); + } else if constexpr (std::is_same_v) { + return x; + } else { + static_assert(!sizeof(U), "Unimplemented case for helpers::conj"); + } +} + +template auto conjugate(Container matrix) { + llvm::transform(matrix, matrix.begin(), [](auto&& x) { return conj(x); }); + return matrix; +} + +template +inline auto transpose_conjugate(Container&& matrix) { + auto result = transpose(matrix); + result = conjugate(matrix); + return result; +} + +inline qfp determinant(const matrix2x2& mat) { + return mat[0] * mat[3] - mat[1] * mat[2]; +} + +inline qfp determinant(const std::array& mat) { + return mat[0] * (mat[4] * mat[8] - mat[5] * mat[7]) - + mat[1] * (mat[3] * mat[8] - mat[5] * mat[6]) + + mat[2] * (mat[3] * mat[7] - mat[4] * mat[6]); +} + +inline std::array get3x3Submatrix(const matrix4x4& mat, + int rowToBeRemoved, + int columnToBeRemoved) { + std::array, 9> result; + int subIndex = 0; + for (int i = 0; i < 4; ++i) { + if (i != rowToBeRemoved) { + for (int j = 0; j < 4; ++j) { + if (j != columnToBeRemoved) { + result[subIndex++] = mat[i * 4 + j]; + } + } + } + } + return result; +} + +inline qfp determinant(const matrix4x4& mat) { + auto [l, u, rowPermutations] = helpers::LUdecomposition(mat); + auto det = C_ONE; + for (int i = 0; i < 4; ++i) { + det *= l[i * 4 + i]; + } + + if (rowPermutations % 2 != 0) { + det = -det; + } + return det; + + // auto det = -C_ZERO; + // for (int column = 0; column < 4; ++column) { + // auto submatrix = get3x3Submatrix(mat, 0, column); + // auto subDet = determinant(submatrix); + // auto tmp = mat[0 * 4 + column] * subDet; + // if (column % 2 == 0 && + // tmp != + // C_ZERO) { // TODO: better way to get negative 0.0 in + // determinant? + // det += tmp; + // } else if (tmp != -C_ZERO) { + // det -= tmp; + // } + // } + // return det; +} + +template +inline std::array from(const std::array& first_quadrant, + const std::array& second_quadrant, + const std::array& third_quadrant, + const std::array& fourth_quadrant) { + return { + first_quadrant[0 * 2 + 0], first_quadrant[0 * 2 + 1], + second_quadrant[0 * 2 + 0], second_quadrant[0 * 2 + 1], + first_quadrant[1 * 2 + 0], first_quadrant[1 * 2 + 1], + second_quadrant[1 * 2 + 0], second_quadrant[1 * 2 + 1], + third_quadrant[0 * 2 + 0], third_quadrant[0 * 2 + 1], + fourth_quadrant[0 * 2 + 0], fourth_quadrant[0 * 2 + 1], + third_quadrant[1 * 2 + 0], third_quadrant[1 * 2 + 1], + fourth_quadrant[1 * 2 + 0], fourth_quadrant[1 * 2 + 1], + }; +} + +template inline auto toArray4(const std::vector& vec) { + std::array result; + assert(vec.size() == result.size()); + for (std::size_t i = 0; i < vec.size(); ++i) { + result[i] = vec[i]; + } + return result; +} + +template +inline std::array kroneckerProduct(const std::array& lhs, + const std::array& rhs) { + return from(multiply(lhs[0 * 2 + 0], rhs), multiply(lhs[0 * 2 + 1], rhs), + multiply(lhs[1 * 2 + 0], rhs), multiply(lhs[1 * 2 + 1], rhs)); +} + } // namespace mqt::ir::opt::helpers diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/b.h b/mlir/lib/Dialect/MQTOpt/Transforms/b.h new file mode 100644 index 0000000000..6b3af6b29c --- /dev/null +++ b/mlir/lib/Dialect/MQTOpt/Transforms/b.h @@ -0,0 +1,474 @@ +#pragma once + +#include "Helpers.h" + +namespace mqt::ir::opt { +void tridiagonalization_inplace(rmatrix4x4& mat, rdiagonal4x4& hCoeffs) { + auto n = 4; + + auto makeHouseholder = [](llvm::SmallVector& essential, fp& tau, + fp& beta) { + std::vector tail{essential.begin(), essential.end()}; + + auto squaredNorm = [](auto&& v) { + qfp sum{}; + for (auto&& x : v) { + sum += qfp(std::real(x) * std::real(x), std::imag(x) * std::imag(x)); + } + return sum.real() + sum.imag(); + }; + + auto tailSqNorm = essential.size() == 1 ? 0.0 : squaredNorm(tail); + fp c0 = essential[0]; + const fp tol = (std::numeric_limits::min)(); + + if (tailSqNorm <= tol && std::norm(std::imag(c0)) <= tol) { + tau = 0; + beta = std::real(c0); + llvm::fill(essential, 0); + } else { + beta = std::sqrt(std::norm(c0) + tailSqNorm); + if (std::real(c0) >= 0.0) { + beta = -beta; + } + for (std::size_t i = 0; i < essential.size(); ++i) { + essential[i] = tail[i] / (c0 - beta); + } + tau = helpers::conj((beta - c0) / beta); + } + }; + + auto lowerSelfadjointView = [](auto matrix) { + const int n = std::sqrt(matrix.size()); + for (int i = 0; i < n; ++i) { + for (int j = 0; j <= i; ++j) { + matrix[j * n + i] = matrix[i * n + j]; + } + } + return matrix; + }; + + auto bottomRightCorner = [](auto&& matrix, int rows, int columns) { + const int n = std::sqrt(matrix.size()); + return helpers::submatrix(matrix, n - rows, n - columns, rows, columns); + }; + + auto getColumn = [](const rmatrix4x4& matrix, int column) { + rdiagonal4x4 result; + for (int j = 0; j < 4; ++j) { + result[j] = matrix[j * 4 + column]; + } + return result; + }; + + auto getTail = [](auto&& array, int size) { + std::vector result(size); + for (int i = 0; i < size; ++i) { + result[i] = array[array.size() - size + i]; + } + return result; + }; + + auto rankUpdate = [](auto matrix, auto&& u, auto&& v, auto&& alpha) { + rmatrix4x4 add1{}; + for (int i = 0; i < u.size(); ++i) { + for (int j = 0; j < v.size(); ++j) { + add1[j * 4 + i] += alpha * u[i] * helpers::conj(v[j]); + } + } + rmatrix4x4 add2{}; + for (int i = 0; i < v.size(); ++i) { + for (int j = 0; j < u.size(); ++j) { + add2[j * 4 + i] = helpers::conj(alpha) * v[i] * helpers::conj(u[j]); + } + } + for (int i = 0; i < matrix.size(); ++i) { + matrix[i] += add1[i] + add2[i]; + } + return matrix; + }; + + for (auto i = 0; i < n - 1; ++i) { + auto remainingSize = n - i - 1; + fp beta; + fp h; + + // matA.col(i).tail(remainingSize).makeHouseholderInPlace(h, beta); + llvm::SmallVector tmp; + for (int j = n - remainingSize; j < n; ++j) { + tmp.push_back(mat[j * n + i]); + } + makeHouseholder(tmp, h, beta); + + // Apply similarity transformation to remaining columns, + // i.e., A = H A H' where H = I - h v v' and v = matA.col(i).tail(n-i-1) + // matA.col(i).coeffRef(i + 1) = fp(1); + mat[(i + 1) * n + i] = 1.0; + + // hCoeffs.tail(n - i - 1).noalias() = + // (matA.bottomRightCorner(remainingSize, remainingSize).template + // selfadjointView() * + // (conj(h) * matA.col(i).tail(remainingSize))); + auto tmp2 = helpers::multiply( + lowerSelfadjointView( + bottomRightCorner(mat, remainingSize, remainingSize)), + helpers::multiply(helpers::conj(h), + getTail(getColumn(mat, i), remainingSize)), + remainingSize); + for (int a = 0; a < remainingSize; ++a) { + hCoeffs[i + 1 + a] = tmp2[a]; + } + + // hCoeffs.tail(n - i - 1) += + // (conj(h) * RealScalar(-0.5) * + // (hCoeffs.tail(remainingSize).dot(matA.col(i).tail(remainingSize)))) + // * matA.col(i).tail(n - i - 1); + auto tmpFactor = + helpers::conj(h) * static_cast(-0.5) * + helpers::vectorsDot(getTail(hCoeffs, remainingSize), + getTail(getColumn(mat, i), remainingSize)); + tmp2 = helpers::multiply(tmpFactor, + helpers::submatrix(mat, i + 1, i, n - i - 1, 1)); + for (int a = 0; a < remainingSize; ++a) { + hCoeffs[i + 1 + a] += tmp2[a]; + } + + // matA.bottomRightCorner(remainingSize, remainingSize) + // .template selfadjointView() + // .rankUpdate(matA.col(i).tail(remainingSize), + // hCoeffs.tail(remainingSize), Scalar(-1)); + auto updatedMatrix = bottomRightCorner(mat, remainingSize, remainingSize); + updatedMatrix = lowerSelfadjointView(updatedMatrix); + updatedMatrix = rankUpdate( + updatedMatrix, helpers::submatrix(mat, n - remainingSize, i, remainingSize, 1), + std::vector{hCoeffs.begin() + (n - remainingSize), hCoeffs.end()}, + -1.0); + // update bottom right corner + helpers::assignSubmatrix(mat, updatedMatrix, n - remainingSize, + n - remainingSize, remainingSize, remainingSize); + + // matA.col(i).coeffRef(i + 1) = beta; + mat[(i + 1) * n + i] = beta; + // hCoeffs.coeffRef(i) = h; + hCoeffs[i] = h; + } +} + +void tridiagonal_qr_step(rdiagonal4x4& diag, std::vector& subdiag, + int start, int end, rmatrix4x4& matrixQ) { + // Wilkinson Shift. + auto td = (diag[end - 1] - diag[end]) * static_cast(0.5); + auto e = subdiag[end - 1]; + // Note that thanks to scaling, e^2 or td^2 cannot overflow, however they can + // still underflow thus leading to inf/NaN values when using the following + // commented code: + // RealScalar e2 = numext::abs2(subdiag[end-1]); + // RealScalar mu = diag[end] - e2 / (td + (td>0 ? 1 : -1) * sqrt(td*td + + // e2)); + // This explain the following, somewhat more complicated, version: + auto mu = diag[end]; + if (td == 0.0) { + mu -= std::abs(e); + } else if (e != 0.0) { + const auto e2 = std::norm(e); + const auto h = std::hypot(td, e); + if (e2 == 0.0) { + mu -= e / ((td + (td > static_cast(0) ? h : -h)) / e); + } else { + mu -= e2 / (td + (td > static_cast(0) ? h : -h)); + } + } + + auto x = diag[start] - mu; + auto z = subdiag[start]; + // If z ever becomes zero, the Givens rotation will be the identity and + // z will stay zero for all future iterations. + for (int k = start; k < end && z != 0.0; ++k) { + struct JacobiRotation { + fp c; + fp s; + + void makeGivens(fp p, fp q) { + if (q == 0.0) { + c = p < 0 ? -1 : 1; + s = 0; + } else if (p == 0.0) { + c = 0; + s = q < 0 ? 1 : -1; + } else if (std::abs(p) > std::abs(q)) { + auto t = q / p; + auto u = std::sqrt(1.0 + std::norm(t)); + if (p < 0.0) + u = -u; + c = 1.0 / u; + s = -t * c; + } else { + auto t = p / q; + auto u = std::sqrt(1.0 + std::norm(t)); + if (q < 0) + u = -u; + s = -1.0 / u; + c = -t * s; + } + } + + void applyOnTheRight(rmatrix4x4& matrix, int p, int q) { + const int n = std::sqrt(matrix.size()); + auto x = helpers::submatrix(matrix, 0, p, n, 1); + auto y = helpers::submatrix(matrix, 0, q, n, 1); + auto j = *this; + j.transpose(); + + if (j.c == 0.0 && j.s == 0.0) { + return; + } + + for (int i = 0; i < n; ++i) { + auto xi = x[i]; + auto yi = y[i]; + x[i] = j.c * xi + helpers::conj(j.s) * yi; + y[i] = -j.s * xi + helpers::conj(j.c) * yi; + } + + helpers::assignSubmatrix(matrix, x, 0, p, n, 1); + helpers::assignSubmatrix(matrix, y, 0, q, n, 1); + } + + void transpose() { s = -helpers::conj(s); } + } rot; + rot.makeGivens(x, z); + + // do T = G' T G + auto sdk = rot.s * diag[k] + rot.c * subdiag[k]; + auto dkp1 = rot.s * subdiag[k] + rot.c * diag[k + 1]; + + diag[k] = rot.c * (rot.c * diag[k] - rot.s * subdiag[k]) - + rot.s * (rot.c * subdiag[k] - rot.s * diag[k + 1]); + diag[k + 1] = rot.s * sdk + rot.c * dkp1; + subdiag[k] = rot.c * sdk - rot.s * dkp1; + + if (k > start) + subdiag[k - 1] = rot.c * subdiag[k - 1] - rot.s * z; + + // "Chasing the bulge" to return to triangular form. + x = subdiag[k]; + if (k < end - 1) { + z = -rot.s * subdiag[k + 1]; + subdiag[k + 1] = rot.c * subdiag[k + 1]; + } + + // apply the givens rotation to the unit matrix Q = Q * G + rot.applyOnTheRight(matrixQ, k, k + 1); + } +} + +void computeFromTridiagonal_impl(rdiagonal4x4& diag, std::vector& subdiag, + const int maxIterations, + bool computeEigenvectors, rmatrix4x4& eivec) { + auto n = diag.size(); + auto end = n - 1; + int start = 0; + int iter = 0; // total number of iterations + + constexpr auto considerAsZero = (std::numeric_limits::min)(); + const auto precision_inv = + static_cast(1.0) / std::numeric_limits::epsilon(); + while (end > 0) { + for (int i = start; i < end; ++i) { + if (std::abs(subdiag[i]) < considerAsZero) { + subdiag[i] = static_cast(0); + } else { + // abs(subdiag[i]) <= epsilon * sqrt(abs(diag[i]) + abs(diag[i+1])) + // Scaled to prevent underflows. + const auto scaled_subdiag = precision_inv * subdiag[i]; + if (scaled_subdiag * scaled_subdiag <= + (std::abs(diag[i]) + std::abs(diag[i + 1]))) { + subdiag[i] = static_cast(0); + } + } + } + + // find the largest unreduced block at the end of the matrix. + while (end > 0 && subdiag[end - 1] == 0.0) { + end--; + } + if (end <= 0) { + break; + } + + // if we spent too many iterations, we give up + iter++; + if (iter > maxIterations * n) { + break; + } + + start = end - 1; + while (start > 0 && subdiag[start - 1] != 0.0) { + start--; + } + + tridiagonal_qr_step(diag, subdiag, start, end, eivec); + } + // Sort eigenvalues and corresponding vectors. + // TODO make the sort optional ? + // TODO use a better sort algorithm !! + if (iter > maxIterations * n) { + throw std::runtime_error{"No convergence for eigenvalue decomposition!"}; + } + for (int i = 0; i < n - 1; ++i) { + // diag.segment(i, n - i).minCoeff(&k); + int k = std::distance(diag.begin() + i, + std::min_element(diag.begin() + i, diag.end())); + if (k > 0 && k < n - i) { + std::swap(diag[i], diag[k + i]); + if (computeEigenvectors) { + for (int j = 0; j < n; ++j) { + std::swap(eivec[j * n + i], eivec[j * n + (k + i)]); + } + } + } + } +} + +void householderSequenceEval(rmatrix4x4& m_vectors, + const rdiagonal4x4& m_coeffs, int length, + int shift) { + auto essentialVector = [&](const rmatrix4x4& vectors, int k) { + int start = k + 1 + shift; + std::vector result; + result.reserve(4 - start); + for (int j = start; j < 4; ++j) { + result.push_back(vectors[j * 4 + k]); + } + return result; + }; + + auto applyThisOnTheLeft = [&](auto&& vectors, auto& dst) { + const auto n = std::sqrt(dst.size()); + for (int k = 0; k < length; ++k) { + int actual_k = n - k - 1; + int dstRows = n - shift - actual_k; + + // TODO + // auto sub_dst = dst.bottomRows(dstRows); + // sub_dst.applyHouseholderOnTheLeft(essentialVector(vectors, actual_k), + // m_coeffs[actual_k]); + } + }; + + auto applyHouseholderOnTheLeft = [](std::vector& matrix, + const std::vector& essential, + const fp& tau) { + const int n = std::sqrt(matrix.size()); + if (tau != 0.0) { + auto firstRow = helpers::submatrix(matrix, 0, 0, 1, n); + auto bottom = helpers::submatrix(matrix, 1, 0, n - 1, n); + + auto tmp = helpers::multiply(helpers::transpose_conjugate(essential), + bottom, essential.size()); + tmp = helpers::add(tmp, firstRow); + + auto tmp2 = helpers::add( + firstRow, helpers::multiply(-1.0, helpers::multiply(tau, tmp))); + // insert first row (first 4 elements) + llvm::copy(tmp2, matrix.begin()); + + tmp2 = helpers::add( + bottom, helpers::multiply(-tau, helpers::multiply(essential, tmp, + essential.size()))); + // insert all rows except first row + llvm::copy(tmp2, matrix.begin() + n); + } + }; + + auto applyHouseholderOnTheRight = [](std::vector& matrix, + const std::vector& essential, + const fp& tau) { + const int n = std::sqrt(matrix.size()); + if (tau != 0.0) { + auto firstColumn = helpers::submatrix(matrix, 0, 0, n, 1); + auto right = helpers::submatrix(matrix, 0, 1, n, n - 1); + + auto tmp = helpers::multiply(right, essential, n - 1); + tmp = helpers::add(tmp, firstColumn); + + auto tmp2 = helpers::add(firstColumn, helpers::multiply(-tau, tmp)); + auto tmp3 = helpers::add( + right, + helpers::multiply( + -tau, helpers::multiply( + tmp, helpers::transpose_conjugate(essential), 1))); + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + if (i == 0) { + // insert first column (first 4 elements) + matrix[j * n + i] = tmp2[j]; + } else { + // insert all except first column + matrix[j * n + i] = tmp3[j * (n - 1) + i]; + } + } + } + } + }; + + rmatrix4x4 dst; + const int n = std::sqrt(m_vectors.size()); + const int vecs = length; + dst = helpers::kroneckerProduct(std::array{1.0, 0.0, 0.0, 1.0}, + {1.0, 0.0, 0.0, 1.0}); + for (int k = vecs - 1; k >= 0; --k) { + int cornerSize = n - k - shift; + auto bottomRightCorner = helpers::submatrix( + dst, n - cornerSize, n - cornerSize, cornerSize, cornerSize); + applyHouseholderOnTheLeft(bottomRightCorner, essentialVector(m_vectors, k), + m_coeffs[k]); + // update bottom right corner + helpers::assignSubmatrix(dst, bottomRightCorner, n - cornerSize, + n - cornerSize, cornerSize, cornerSize); + } +} + +// https://docs.rs/faer/latest/faer/mat/generic/struct.Mat.html#method.self_adjoint_eigen +static std::pair self_adjoint_evd(rmatrix4x4 matrix) { + + auto lowerTriangularView = [](auto matrix) { + const int n = std::sqrt(matrix.size()); + for (int i = 0; i < n; ++i) { + for (int j = 0; j <= i; ++j) { + matrix[j * n + i] = 0.0; + } + } + return matrix; + }; + + rdiagonal4x4 eigenvalues{1}; + rmatrix4x4 eigenvectors; + int n = 4; + + // map the matrix coefficients to [-1:1] to avoid over- and underflow. + eigenvectors = lowerTriangularView(matrix); + fp scale = *llvm::max_element(eigenvalues, [](auto&& a, auto&& b) { + return std::abs(a) < std::abs(b); + }); + if (scale == 0.0) + scale = static_cast(1.0); + for (auto& eigenvector : eigenvectors) { + eigenvector /= scale; + } + rdiagonal4x4 hCoeffs; + tridiagonalization_inplace(eigenvectors, hCoeffs); + eigenvalues = helpers::diagonal(eigenvectors); + auto subdiag = helpers::diagonal<-1>(eigenvectors); + householderSequenceEval(eigenvectors, helpers::conjugate(hCoeffs), n - 1, 1); + + computeFromTridiagonal_impl(eigenvalues, subdiag, 1000, true, eigenvectors); + + // scale back the eigen values + for (auto& eigenvalue : eigenvalues) { + eigenvalue *= scale; + } + + return {eigenvectors, eigenvalues}; +} +} // namespace mqt::ir::opt From eec099c84af07ee3d4e85075dc8c52803be54a89 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 22 Oct 2025 11:49:03 +0200 Subject: [PATCH 016/237] fix out-of-bounds issues --- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 16 +++++++++------- mlir/lib/Dialect/MQTOpt/Transforms/b.h | 9 ++------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index 3ae7eaa7bd..31c0bfcd27 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -252,6 +252,8 @@ template int rowsLhs = lhs.size() / columnsLhs; int rowsRhs = columnsLhs; int columnsRhs = rhs.size() / rowsRhs; + assert(rowsLhs * columnsLhs == lhs.size()); + assert(rowsRhs * columnsRhs == rhs.size()); std::vector result(rowsLhs * columnsRhs, T{}); for (int i = 0; i < rowsLhs; i++) { @@ -364,15 +366,15 @@ auto submatrix(Container&& matrix, int rowStart, int columnStart, int numRows, template auto assignSubmatrix(Lhs&& lhs, Rhs&& rhs, int rowStart, int columnStart, - int rowSize, int columnSize) { + int numRows, int numColumns) { const int n = std::sqrt(lhs.size()); - assert((rowStart + rowSize) < n); - assert((columnStart + columnSize) < n); - assert(columnSize * rowSize == rhs.size()); + assert((rowStart + numRows) <= n); + assert((columnStart + numColumns) <= n); + assert(numColumns * numRows == rhs.size()); - for (int i = 0; i < columnSize; ++i) { - for (int j = 0; j < rowSize; ++j) { - lhs[(rowStart + j) * 4 + (columnStart + i)] = rhs[j * rowSize + i]; + for (int i = 0; i < numColumns; ++i) { + for (int j = 0; j < numRows; ++j) { + lhs[(rowStart + j) * n + (columnStart + i)] = rhs[j * numColumns + i]; } } } diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/b.h b/mlir/lib/Dialect/MQTOpt/Transforms/b.h index 6b3af6b29c..f8f65d9649 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/b.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/b.h @@ -335,12 +335,7 @@ void householderSequenceEval(rmatrix4x4& m_vectors, int shift) { auto essentialVector = [&](const rmatrix4x4& vectors, int k) { int start = k + 1 + shift; - std::vector result; - result.reserve(4 - start); - for (int j = start; j < 4; ++j) { - result.push_back(vectors[j * 4 + k]); - } - return result; + return helpers::submatrix(vectors, start, k, 4 - start, 1); }; auto applyThisOnTheLeft = [&](auto&& vectors, auto& dst) { @@ -375,7 +370,7 @@ void householderSequenceEval(rmatrix4x4& m_vectors, tmp2 = helpers::add( bottom, helpers::multiply(-tau, helpers::multiply(essential, tmp, - essential.size()))); + 1))); // insert all rows except first row llvm::copy(tmp2, matrix.begin() + n); } From b1a42862edfdcb164f727e911195b966187625f4 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 22 Oct 2025 15:51:09 +0200 Subject: [PATCH 017/237] test using Armadillo --- CMakeLists.txt | 18 +++++++++++ .../Dialect/MQTOpt/Transforms/CMakeLists.txt | 2 +- .../Transforms/GateDecompositionPattern.cpp | 17 ++++++++-- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 21 +++++++++++-- mlir/lib/Dialect/MQTOpt/Transforms/b.h | 2 +- mlir/lib/Dialect/MQTOpt/Transforms/d.h | 31 +++++++++++++++++++ 6 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/d.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 86f93ccc8e..651ea24dbe 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,24 @@ include(PackageAddTest) include(Cache) include(AddMQTCoreLibrary) +# include(FetchContent) +# FetchContent_Declare( +# armadillo +# GIT_REPOSITORY https://gitlab.com/conradsnicta/armadillo-code.git +# GIT_TAG 15.0.1 +# ) +# FetchContent_MakeAvailable(armadillo) +find_package(Armadillo 15 REQUIRED) +if(Armadillo_FOUND AND NOT TARGET Armadillo::Armadillo) + add_library(Armadillo::Armadillo INTERFACE IMPORTED) + set_target_properties( + Armadillo::Armadillo + PROPERTIES + INTERFACE_LINK_LIBRARIES "${ARMADILLO_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${ARMADILLO_INCLUDE_DIRS}" + ) +endif() + option(BUILD_MQT_CORE_BINDINGS "Build the MQT Core Python bindings" OFF) if(BUILD_MQT_CORE_BINDINGS) # ensure that the BINDINGS option is set diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt b/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt index 6badce698e..2098ff4af0 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt +++ b/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt @@ -7,7 +7,7 @@ # Licensed under the MIT License get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) -set(LIBRARIES ${dialect_libs} MQT::CoreIR MQT::CoreDD) +set(LIBRARIES ${dialect_libs} MQT::CoreIR MQT::CoreDD Armadillo::Armadillo) add_compile_options(-fexceptions) file(GLOB_RECURSE TRANSFORMS_SOURCES *.cpp) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 959aa2c7e6..805fe08394 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -25,7 +25,7 @@ #include #include -#include "b.h" +#include "d.h" namespace mqt::ir::opt { @@ -324,7 +324,7 @@ struct GateDecompositionPattern final auto [U, S] = self_adjoint_evd(A); - return {U, S}; + return std::make_pair(U, S); } static std::tuple @@ -564,6 +564,15 @@ struct GateDecompositionPattern final llvm::errs() << "===== M2 =====\n"; helpers::print(m2); + arma::Mat U(4, 4); + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + U.at(j, i) = u_p[j * 4 + i]; + } + } + auto x = U.st() * U; + std::cerr << "ARMA\n" << U.t() << "\n\n" << U << "\n\n" << x << std::endl; + // M2 is a symmetric complex matrix. We need to decompose it as M2 = P D // P^T where P ∈ SO(4), D is diagonal with unit-magnitude elements. // @@ -603,7 +612,7 @@ struct GateDecompositionPattern final llvm::transform(m2, m2_real.begin(), [&](const qfp& val) { return rand_a * val.real() + rand_b * val.imag(); }); - auto p_inner_real = self_adjoint_eigen_lower(m2_real).first; + rmatrix4x4 p_inner_real = self_adjoint_eigen_lower(m2_real).first; matrix4x4 p_inner; llvm::transform(p_inner_real, p_inner.begin(), [](auto&& x) { return qfp(x, 0.0); }); @@ -638,6 +647,7 @@ struct GateDecompositionPattern final std::array d_real; llvm::transform(d, d_real.begin(), [](auto&& x) { return -std::arg(x) / 2.0; }); + helpers::print(d_real, "D_REAL"); d_real[3] = -d_real[0] - d_real[1] - d_real[2]; std::array cs; for (std::size_t i = 0; i < cs.size(); ++i) { @@ -680,6 +690,7 @@ struct GateDecompositionPattern final temp[1 * 4 + 1] = std::exp(IM * d_real[1]); temp[2 * 4 + 2] = std::exp(IM * d_real[2]); temp[3 * 4 + 3] = std::exp(IM * d_real[3]); + helpers::print(temp, "TEMP"); auto k1 = magic_basis_transform(dot(dot(u_p, p), temp), MagicBasisTransform::Into); auto k2 = magic_basis_transform(transpose(p), MagicBasisTransform::Into); diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index 31c0bfcd27..77c1d12abc 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -40,10 +40,27 @@ constexpr qfp M_IM{0., -1.}; namespace mqt::ir::opt::helpers { // TODO: remove -template void print(std::array, N> matrix) { +template void print(std::array, N> matrix, std::string s = "") { int i{}; + if (!s.empty()) { + llvm::errs() << "=== " << s << " ===\n"; + } + for (auto&& a : matrix) { + std::cerr << std::setprecision(17) << a.real() << 'i' << a.imag() << ' '; + if (++i % 4 == 0) { + llvm::errs() << '\n'; + } + } + llvm::errs() << '\n'; +} + +template void print(std::array matrix, std::string s = "") { + int i{}; + if (!s.empty()) { + llvm::errs() << "=== " << s << " ===\n"; + } for (auto&& a : matrix) { - std::cerr << std::setprecision(50) << a.real() << 'i' << a.imag() << ' '; + std::cerr << std::setprecision(17) << a << ' '; if (++i % 4 == 0) { llvm::errs() << '\n'; } diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/b.h b/mlir/lib/Dialect/MQTOpt/Transforms/b.h index f8f65d9649..3d610ff96d 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/b.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/b.h @@ -425,7 +425,7 @@ void householderSequenceEval(rmatrix4x4& m_vectors, } // https://docs.rs/faer/latest/faer/mat/generic/struct.Mat.html#method.self_adjoint_eigen -static std::pair self_adjoint_evd(rmatrix4x4 matrix) { +inline std::pair self_adjoint_evd(rmatrix4x4 matrix) { auto lowerTriangularView = [](auto matrix) { const int n = std::sqrt(matrix.size()); diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/d.h b/mlir/lib/Dialect/MQTOpt/Transforms/d.h new file mode 100644 index 0000000000..6d2a849f50 --- /dev/null +++ b/mlir/lib/Dialect/MQTOpt/Transforms/d.h @@ -0,0 +1,31 @@ +#pragma once + +#include "Helpers.h" + +#include + +namespace mqt::ir::opt { + +auto self_adjoint_evd(rmatrix4x4 A) { + arma::Mat a(4, 4, arma::fill::scalar_holder(0.0)); + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + a.at(j, i) = A[j * 4 + i]; + } + } + arma::Mat vecs; + arma::Col vals; + arma::eig_sym(vals, vecs, a, "std"); + rmatrix4x4 rvecs; + rdiagonal4x4 rvals; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + rvecs[j * 4 + i] = vecs.at(j, i); + } + rvals[i] = vals.at(i); + } + std::cerr << "========\n" << vecs << "========\n" << std::endl; + std::cerr << "========\n" << vals << "========\n" << std::endl; + return std::make_pair(rvecs, rvals); +} +} // namespace mqt::ir::opt From cee23a71b225a022f9a35bc365ed1f9e3d3b1e4f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 22 Oct 2025 19:22:44 +0200 Subject: [PATCH 018/237] minor fix --- .../MQTOpt/Transforms/GateDecompositionPattern.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 805fe08394..a726f9b9a5 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -1745,21 +1745,21 @@ struct GateDecompositionPattern final const matrix2x2& unitaryMatrix, bool simplify, std::optional atol) { - auto [theta, phi, lamda, phase] = + auto [theta, phi, lambda, phase] = angles_from_unitary(unitaryMatrix, target_basis); switch (target_basis) { case EulerBasis::ZYZ: - return calculateRotationGates(theta, phi, lamda, phase, qc::RZ, qc::RY, + return calculateRotationGates(theta, phi, lambda, phase, qc::RZ, qc::RY, simplify, atol); case EulerBasis::ZXZ: - return calculateRotationGates(theta, phi, lamda, phase, qc::RZ, qc::RX, + return calculateRotationGates(theta, phi, lambda, phase, qc::RZ, qc::RX, simplify, atol); case EulerBasis::XZX: - return calculateRotationGates(theta, phi, lamda, phase, qc::RX, qc::RZ, + return calculateRotationGates(theta, phi, lambda, phase, qc::RX, qc::RZ, simplify, atol); case EulerBasis::XYX: - return calculateRotationGates(theta, phi, lamda, phase, qc::RX, qc::RY, + return calculateRotationGates(theta, phi, lambda, phase, qc::RX, qc::RY, simplify, atol); default: // TODO: allow other bases From 5550adc3a9b5ee306517a9df4132568ab27cb178 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 24 Oct 2025 23:31:15 +0200 Subject: [PATCH 019/237] tmp --- .../Transforms/GateDecompositionPattern.cpp | 124 ++++-------------- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 25 +++- 2 files changed, 49 insertions(+), 100 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index a726f9b9a5..ea44eb6b94 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -230,89 +230,6 @@ struct GateDecompositionPattern final return wrapped; } - // GPT generated - void tridiagonalize_inplace(rmatrix4x4& A) { - auto dot3 = [](auto&& a, auto&& b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; - }; - // Householder for column 0 - { - auto x = std::array{A[1 * 4 + 0], A[8 + 0], - A[12 + 0]}; // Elements [1,0], [2,0], [3,0] - auto norm_x = std::sqrt(dot3(x, x)); - if (norm_x > 1e-12) { - auto sign = (x[0] >= 0) ? 1.0 : -1.0; - auto u0 = x[0] + sign * norm_x; - auto denom = std::sqrt(u0 * u0 + x[1] * x[1] + x[2] * x[2]); - auto v = std::array{u0 / denom, x[1] / denom, x[2] / denom}; - - // Apply H = I - 2vvᵀ to A from both sides: A := H A H - // Only affect rows and columns 1-3 - - // temp = A * v - std::array temp = {0, 0, 0}; - for (int i = 1; i < 4; ++i) { - for (int j = 1; j < 4; ++j) { - temp[i - 1] += A[i * 4 + j] * v[j - 1]; - } - } - - // w = 2 * (A*v - (vᵀ*A*v)*v) - auto alpha = dot3(v, temp); - std::array w; - for (int i = 0; i < 3; ++i) - w[i] = 2.0 * (temp[i] - alpha * v[i]); - - // Update A = A - v wᵀ - w vᵀ - for (int i = 1; i < 4; ++i) { - for (int j = i; j < 4; ++j) { - auto delta = v[i - 1] * w[j - 1] + w[i - 1] * v[j - 1]; - A[i * 4 + j] -= delta; - if (i != j) - A[j * 4 + i] -= delta; // symmetry - } - } - } - } - - // Householder for column 1 (submatrix 2x2 at bottom right) - { - auto x = std::array{A[10], A[15]}; // Elements [2,1], [3,1] - auto norm_x = std::sqrt(x[0] * x[0] + x[1] * x[1]); - if (norm_x > 1e-12) { - auto sign = (x[0] >= 0) ? 1.0 : -1.0; - auto u0 = x[0] + sign * norm_x; - auto denom = std::sqrt(u0 * u0 + x[1] * x[1]); - auto v = std::array{u0 / denom, x[1] / denom}; - - // temp = A * v - std::array temp = {0, 0}; - for (int i = 2; i < 4; ++i) { - for (int j = 2; j < 4; ++j) { - temp[i - 2] += A[i * 4 + j] * v[j - 2]; - } - } - - // w = 2 * (A*v - (vᵀ*A*v)*v) - auto alpha = v[0] * temp[0] + v[1] * temp[1]; - std::array w; - for (int i = 0; i < 2; ++i) - w[i] = 2.0 * (temp[i] - alpha * v[i]); - - // Update A = A - v wᵀ - w vᵀ - for (int i = 2; i < 4; ++i) { - for (int j = i; j < 4; ++j) { - auto delta = v[i - 2] * w[j - 2] + w[i - 2] * v[j - 2]; - A[i * 4 + j] -= delta; - if (i != j) - A[j * 4 + i] -= delta; - } - } - } - } - - // Now A is tridiagonal - } // https://docs.rs/faer/latest/faer/mat/generic/struct.Mat.html#method.self_adjoint_eigen static std::pair self_adjoint_eigen_lower(rmatrix4x4 A) { @@ -324,6 +241,14 @@ struct GateDecompositionPattern final auto [U, S] = self_adjoint_evd(A); + // TODO: not in original code + if (helpers::determinant(U) + 1.0 < std::numeric_limits::epsilon()) { + // if determinant of eigenvector matrix is -1.0, multiply first eigenvector by -1.0 + for (int i = 0; i < 4; ++i) { + U[i * 4 + 0] *= -1.0; + } + } + return std::make_pair(U, S); } @@ -333,6 +258,7 @@ struct GateDecompositionPattern final using helpers::kroneckerProduct; using helpers::transpose_conjugate; using helpers::determinant; + helpers::print(special_unitary, "SPECIAL_UNITARY"); // first quadrant matrix2x2 r = {special_unitary[0 * 4 + 0], special_unitary[0 * 4 + 1], special_unitary[1 * 4 + 0], special_unitary[1 * 4 + 1]}; @@ -387,6 +313,7 @@ struct GateDecompositionPattern final C_ZERO, qfp(0., -0.5), qfp(0., -0.5), C_ZERO, C_ZERO, qfp(0.5, 0.), qfp(-0.5, 0.), C_ZERO, }; + helpers::print(unitary, "UNITARY in MAGIC BASIS TRANSFORM"); if (direction == MagicBasisTransform::OutOf) { return dot(dot(B_NON_NORMALIZED_DAGGER, unitary), B_NON_NORMALIZED); } @@ -564,14 +491,14 @@ struct GateDecompositionPattern final llvm::errs() << "===== M2 =====\n"; helpers::print(m2); - arma::Mat U(4, 4); - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 4; ++j) { - U.at(j, i) = u_p[j * 4 + i]; - } - } - auto x = U.st() * U; - std::cerr << "ARMA\n" << U.t() << "\n\n" << U << "\n\n" << x << std::endl; + // arma::Mat U(4, 4); + // for (int i = 0; i < 4; ++i) { + // for (int j = 0; j < 4; ++j) { + // U.at(j, i) = u_p[j * 4 + i]; + // } + // } + // auto x = U.st() * U; + // std::cerr << "ARMA\n" << U.t() << "\n\n" << U << "\n\n" << x << std::endl; // M2 is a symmetric complex matrix. We need to decompose it as M2 = P D // P^T where P ∈ SO(4), D is diagonal with unit-magnitude elements. @@ -652,8 +579,9 @@ struct GateDecompositionPattern final std::array cs; for (std::size_t i = 0; i < cs.size(); ++i) { assert(i < d_real.size()); - cs[i] = remEuclid((d_real[i] + d_real[3]) / 2.0, qc::PI_2); + cs[i] = remEuclid((d_real[i] + d_real[3]) / 2.0, qc::TAU); } + helpers::print(cs, "CS"); decltype(cs) cstemp; llvm::transform(cs, cstemp.begin(), [](auto&& x) { auto tmp = remEuclid(x, qc::PI_2); @@ -662,18 +590,21 @@ struct GateDecompositionPattern final std::array order{0, 1, 2}; llvm::stable_sort(order, [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); - std::tie(order[0], order[1], order[2]) = {order[1], order[2], order[0]}; - std::tie(cs[0], cs[1], cs[2]) = {cs[order[0]], cs[order[1]], + helpers::print(order, "ORDER (1)"); + std::tie(order[0], order[1], order[2]) = std::tuple{order[1], order[2], order[0]}; + helpers::print(order, "ORDER (2)"); + std::tie(cs[0], cs[1], cs[2]) = std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; - std::tie(d_real[0], d_real[1], d_real[2]) = { + std::tie(d_real[0], d_real[1], d_real[2]) = std::tuple{ d_real[order[0]], d_real[order[1]], d_real[order[2]]}; + helpers::print(d_real, "D_REAL (sorted)"); // swap columns of p according to order constexpr auto P_ROW_LENGTH = 4; auto p_orig = p; for (std::size_t i = 0; i < order.size(); ++i) { for (std::size_t row = 0; row < P_ROW_LENGTH; ++row) { - std::swap(p[row * 3 + i], p_orig[row * 3 + order[i]]); + std::swap(p[row * P_ROW_LENGTH + i], p_orig[row * P_ROW_LENGTH + order[i]]); } } @@ -691,6 +622,7 @@ struct GateDecompositionPattern final temp[2 * 4 + 2] = std::exp(IM * d_real[2]); temp[3 * 4 + 3] = std::exp(IM * d_real[3]); helpers::print(temp, "TEMP"); + helpers::print(p, "P"); auto k1 = magic_basis_transform(dot(dot(u_p, p), temp), MagicBasisTransform::Into); auto k2 = magic_basis_transform(transpose(p), MagicBasisTransform::Into); diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index 77c1d12abc..c323477eff 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -68,6 +68,20 @@ template void print(std::array matrix, std::string s = "" llvm::errs() << '\n'; } +template void print(std::array matrix, std::string s = "") { + int i{}; + if (!s.empty()) { + llvm::errs() << "=== " << s << " ===\n"; + } + for (auto&& a : matrix) { + std::cerr << a << ' '; + if (++i % 4 == 0) { + llvm::errs() << '\n'; + } + } + llvm::errs() << '\n'; +} + inline auto flatten(const dd::TwoQubitGateMatrix& matrix) { std::array, 16> result; for (std::size_t i = 0; i < result.size(); ++i) { @@ -453,11 +467,13 @@ inline auto transpose_conjugate(Container&& matrix) { return result; } -inline qfp determinant(const matrix2x2& mat) { +template +inline T determinant(const std::array& mat) { return mat[0] * mat[3] - mat[1] * mat[2]; } -inline qfp determinant(const std::array& mat) { +template +inline T determinant(const std::array& mat) { return mat[0] * (mat[4] * mat[8] - mat[5] * mat[7]) - mat[1] * (mat[3] * mat[8] - mat[5] * mat[6]) + mat[2] * (mat[3] * mat[7] - mat[4] * mat[6]); @@ -480,9 +496,10 @@ inline std::array get3x3Submatrix(const matrix4x4& mat, return result; } -inline qfp determinant(const matrix4x4& mat) { +template +inline T determinant(const std::array& mat) { auto [l, u, rowPermutations] = helpers::LUdecomposition(mat); - auto det = C_ONE; + T det = 1.0; for (int i = 0; i < 4; ++i) { det *= l[i * 4 + i]; } From b54150055cee02d4ff45fe1aad6157399b49f0ec Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 27 Oct 2025 10:02:41 +0100 Subject: [PATCH 020/237] work on gate application --- .../Transforms/GateDecompositionPattern.cpp | 108 ++++++++++++++---- 1 file changed, 83 insertions(+), 25 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index ea44eb6b94..74d9d76f1c 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -48,7 +48,8 @@ struct GateDecompositionPattern final return mlir::failure(); } - matrix4x4 unitaryMatrix = helpers::kroneckerProduct(identityGate, identityGate); + matrix4x4 unitaryMatrix = + helpers::kroneckerProduct(identityGate, identityGate); for (auto&& gate : series) { auto gateMatrix = getTwoQubitMatrix({.type = helpers::getQcType(gate), .parameter = {/*TODO*/}, @@ -60,6 +61,7 @@ struct GateDecompositionPattern final auto sequence = decomposer.twoQubitDecompose( unitaryMatrix, DEFAULT_FIDELITY, true, std::nullopt); if (!sequence) { + llvm::errs() << "NO SEQUENCE GENERATED!\n"; return mlir::failure(); } @@ -150,6 +152,24 @@ struct GateDecompositionPattern final mlir::ValueRange{}); } + template + static OpType createGate(mlir::PatternRewriter& rewriter, + mlir::Location location, mlir::ValueRange inQubits, + mlir::ValueRange ctrlQubits, + std::vector parameters) { + mlir::SmallVector parameterValues; + for (auto&& parameter : parameters) { + auto parameterValue = rewriter.create( + location, rewriter.getF64Type(), rewriter.getF64FloatAttr(parameter)); + parameterValues.push_back(parameterValue); + } + + return rewriter.create( + location, inQubits.getType(), ctrlQubits.getType(), mlir::TypeRange{}, + mlir::DenseF64ArrayAttr{}, mlir::DenseBoolArrayAttr{}, parameterValues, + inQubits, ctrlQubits, mlir::ValueRange{}); + } + struct QubitGateSequence { struct Gate { qc::OpType type{qc::I}; @@ -163,11 +183,40 @@ struct GateDecompositionPattern final using TwoQubitGateSequence = QubitGateSequence; static void applySeries(mlir::PatternRewriter& rewriter, - const llvm::SmallVector& series, + llvm::SmallVector& series, const TwoQubitGateSequence& sequence) { + auto location = series[0]->getLoc(); + auto inQubits = series[0].getAllInQubits(); if (sequence.globalPhase != 0.0) { - createOneParameterGate(rewriter, series[0]->getLoc(), - sequence.globalPhase, {}); + createOneParameterGate(rewriter, location, sequence.globalPhase, + {}); + } + + std::cerr << "GATE SEQUENCE!: " << std::flush; + for (auto&& gate : sequence.gates) { + if (gate.type == qc::X) { + mlir::SmallVector inCtrlQubits; + if (gate.qubit_id.size() > 1) { + inCtrlQubits.push_back(inQubits[gate.qubit_id[1]]); + } + createGate(rewriter, location, {inQubits[0]}, inCtrlQubits, + gate.parameter); + } + if (gate.type == qc::RX) { + mlir::SmallVector qubits; + for (auto&& x : gate.qubit_id) { + qubits.push_back(inQubits[x]); + } + createGate(rewriter, location, qubits, {}, gate.parameter); + } + if (gate.type == qc::RY) { + } + if (gate.type == qc::RZ) { + } + } + + for (auto&& op : series) { + rewriter.replaceOp(op, op.getAllInQubits()); } } @@ -359,6 +408,12 @@ struct GateDecompositionPattern final if (basis == EulerBasis::XYX) { return params_xyx_inner(matrix); } + if (basis == EulerBasis::ZYZ) { + return params_zyz_inner(matrix); + } + if (basis == EulerBasis::ZXZ) { + return params_zxz_inner(matrix); + } throw std::invalid_argument{"Unknown EulerBasis for angles_from_unitary"}; } @@ -380,20 +435,21 @@ struct GateDecompositionPattern final return {theta, phi, lam, phase}; } + static std::array params_zxz_inner(const matrix2x2& matrix) { + auto [theta, phi, lam, phase] = params_zyz_inner(matrix); + return {theta, phi + qc::PI / 2., lam - qc::PI / 2., phase}; + } + static std::array params_xyx_inner(const matrix2x2& matrix) { auto mat_zyz = std::array{ - static_cast(0.5) * - (matrix.at(0 * 2 + 0) + matrix.at(0 * 2 + 1 * 2 * 2) + - matrix.at(1 * 2 + 0) + matrix.at(1 * 2 + 1)), - static_cast(0.5) * - (matrix.at(0 * 2 + 0) - matrix.at(0 * 2 + 1 * 2 * 2) + - matrix.at(1 * 2 + 0) - matrix.at(1 * 2 + 1)), - static_cast(0.5) * - (matrix.at(0 * 2 + 0) + matrix.at(0 * 2 + 1 * 2 * 2) - - matrix.at(1 * 2 + 0) - matrix.at(1 * 2 + 1)), - static_cast(0.5) * - (matrix.at(0 * 2 + 0) - matrix.at(0 * 2 + 1 * 2 * 2) - - matrix.at(1 * 2 + 0) + matrix.at(1 * 2 + 1)), + static_cast(0.5) * (matrix.at(0 * 2 + 0) + matrix.at(0 * 2 + 1) + + matrix.at(1 * 2 + 0) + matrix.at(1 * 2 + 1)), + static_cast(0.5) * (matrix.at(0 * 2 + 0) - matrix.at(0 * 2 + 1) + + matrix.at(1 * 2 + 0) - matrix.at(1 * 2 + 1)), + static_cast(0.5) * (matrix.at(0 * 2 + 0) + matrix.at(0 * 2 + 1) - + matrix.at(1 * 2 + 0) - matrix.at(1 * 2 + 1)), + static_cast(0.5) * (matrix.at(0 * 2 + 0) - matrix.at(0 * 2 + 1) - + matrix.at(1 * 2 + 0) + matrix.at(1 * 2 + 1)), }; auto [theta, phi, lam, phase] = params_zyz_inner(mat_zyz); auto new_phi = mod2pi(phi + qc::PI, 0.); @@ -472,10 +528,10 @@ struct GateDecompositionPattern final static TwoQubitWeylDecomposition new_inner(matrix4x4 unitary_matrix, std::optional fidelity, std::optional _specialization) { - using helpers::dot; using helpers::determinant; - using helpers::transpose; using helpers::diagonal; + using helpers::dot; + using helpers::transpose; auto& u = unitary_matrix; auto det_u = determinant(u); auto det_pow = std::pow(det_u, static_cast(-0.25)); @@ -591,12 +647,13 @@ struct GateDecompositionPattern final llvm::stable_sort(order, [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); helpers::print(order, "ORDER (1)"); - std::tie(order[0], order[1], order[2]) = std::tuple{order[1], order[2], order[0]}; + std::tie(order[0], order[1], order[2]) = + std::tuple{order[1], order[2], order[0]}; helpers::print(order, "ORDER (2)"); - std::tie(cs[0], cs[1], cs[2]) = std::tuple{cs[order[0]], cs[order[1]], - cs[order[2]]}; - std::tie(d_real[0], d_real[1], d_real[2]) = std::tuple{ - d_real[order[0]], d_real[order[1]], d_real[order[2]]}; + std::tie(cs[0], cs[1], cs[2]) = + std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; + std::tie(d_real[0], d_real[1], d_real[2]) = + std::tuple{d_real[order[0]], d_real[order[1]], d_real[order[2]]}; helpers::print(d_real, "D_REAL (sorted)"); // swap columns of p according to order @@ -604,7 +661,8 @@ struct GateDecompositionPattern final auto p_orig = p; for (std::size_t i = 0; i < order.size(); ++i) { for (std::size_t row = 0; row < P_ROW_LENGTH; ++row) { - std::swap(p[row * P_ROW_LENGTH + i], p_orig[row * P_ROW_LENGTH + order[i]]); + std::swap(p[row * P_ROW_LENGTH + i], + p_orig[row * P_ROW_LENGTH + order[i]]); } } @@ -1339,7 +1397,7 @@ struct GateDecompositionPattern final decomp0_inner(const TwoQubitWeylDecomposition& target) const { using helpers::dot; return { - dot(target.K1r, target.K2r), + dot(target.K1r, target.K2r), dot(target.K1l, target.K2l), }; } From 0b80624df67313206b8c2f394d8ab4cdca5ed213 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 27 Oct 2025 10:04:02 +0100 Subject: [PATCH 021/237] use laughing-umbrella implementation for decompose_two_qubit_product_gate --- .../Transforms/GateDecompositionPattern.cpp | 93 +++++++++++-------- 1 file changed, 52 insertions(+), 41 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 74d9d76f1c..847de97f6a 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -303,54 +303,65 @@ struct GateDecompositionPattern final static std::tuple decompose_two_qubit_product_gate(matrix4x4 special_unitary) { - using helpers::dot; - using helpers::kroneckerProduct; - using helpers::transpose_conjugate; - using helpers::determinant; - helpers::print(special_unitary, "SPECIAL_UNITARY"); - // first quadrant - matrix2x2 r = {special_unitary[0 * 4 + 0], special_unitary[0 * 4 + 1], - special_unitary[1 * 4 + 0], special_unitary[1 * 4 + 1]}; - auto det_r = determinant(r); - if (std::abs(det_r) < 0.1) { - // third quadrant - r = {special_unitary[2 * 4 + 0], special_unitary[2 * 4 + 1], - special_unitary[3 * 4 + 0], special_unitary[3 * 4 + 1]}; - det_r = determinant(r); - } - if (std::abs(det_r) < 0.1) { - throw std::runtime_error{ - "decompose_two_qubit_product_gate: unable to decompose: det_r < 0.1"}; - } - llvm::transform(r, r.begin(), - [&](auto&& x) { return x / std::sqrt(det_r); }); - // transpose with complex conjugate of each element - matrix2x2 r_t_conj = transpose_conjugate(r); - - auto temp = kroneckerProduct(identityGate, r_t_conj); - temp = dot(special_unitary, temp); - - // [[a, b, c, d], - // [e, f, g, h], => [[a, c], - // [i, j, k, l], [i, k]] - // [m, n, o, p]] - matrix2x2 l = {temp[0 * 4 + 0], temp[0 * 4 + 2], temp[2 * 4 + 0], - temp[2 * 4 + 2]}; - auto det_l = determinant(l); - if (std::abs(det_l) < 0.9) { + using helpers::determinant; + using helpers::dot; + using helpers::kroneckerProduct; + using helpers::transpose_conjugate; + helpers::print(special_unitary, "SPECIAL_UNITARY"); + + auto* it = llvm::max_element(special_unitary, [](auto&& a, auto&& b) { + return std::abs(a) < std::abs(b); + }); + if (it == special_unitary.end()) { throw std::runtime_error{ - "decompose_two_qubit_product_gate: unable to decompose: detL < 0.9"}; + "No maximal element in decompose_two_qubit_product_gate!"}; } - llvm::transform(l, l.begin(), - [&](auto&& x) { return x / std::sqrt(det_l); }); - auto phase = std::arg(det_l) / 2.; + auto pos = std::distance(special_unitary.begin(), it); + int i = pos % 4; + int j = pos / 4; - return {l, r, phase}; + auto u1_set = [](int i) { + if (i % 2 == 0) { + return std::make_pair(0, 2); + } + return std::make_pair(1, 3); + }; + + auto u2_set = [](int i) { + if (i < 2) { + return std::make_pair(0, 1); + } + return std::make_pair(2, 3); + }; + + auto to_su = [](auto&& u) { + return helpers::multiply(std::pow(qfp(determinant(u)), -0.25), u); + }; + + matrix2x2 u1; + u1[0 * 2 + 0] = special_unitary[u1_set(i).first * 4 + u1_set(j).first]; + u1[0 * 2 + 1] = special_unitary[u1_set(i).first * 4 + u1_set(j).second]; + u1[1 * 2 + 0] = special_unitary[u1_set(i).second * 4 + u1_set(j).first]; + u1[1 * 2 + 1] = special_unitary[u1_set(i).second * 4 + u1_set(j).second]; + matrix2x2 u2; + u1[0 * 2 + 0] = special_unitary[u2_set(i).first * 4 + u2_set(j).first]; + u1[0 * 2 + 1] = special_unitary[u2_set(i).first * 4 + u2_set(j).second]; + u1[1 * 2 + 0] = special_unitary[u2_set(i).second * 4 + u2_set(j).first]; + u1[1 * 2 + 1] = special_unitary[u2_set(i).second * 4 + u2_set(j).second]; + + u1 = to_su(u1); + u2 = to_su(u2); + + std::cerr << i << ',' << j << std::endl; + auto phase = special_unitary[i * 4 + j] / + (u1[(i / 2) * 2 + (j / 2)] * u2[(i % 2) * 2 + (j % 2)]); + + return {u1, u2, phase.real()}; // TODO: return only real part? } static matrix4x4 magic_basis_transform(const matrix4x4& unitary, MagicBasisTransform direction) { - using helpers::dot; + using helpers::dot; constexpr matrix4x4 B_NON_NORMALIZED = { C_ONE, IM, C_ZERO, C_ZERO, C_ZERO, C_ZERO, IM, C_ONE, C_ZERO, C_ZERO, IM, C_M_ONE, C_ONE, M_IM, C_ZERO, C_ZERO, From 299d6f11b105db39707bf4b3ca2b9e44b62f5c7a Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 27 Oct 2025 10:05:13 +0100 Subject: [PATCH 022/237] add more eigen decomposition implementations --- .../Transforms/GateDecompositionPattern.cpp | 3 +- mlir/lib/Dialect/MQTOpt/Transforms/a.h | 82 ++ mlir/lib/Dialect/MQTOpt/Transforms/c.h | 142 +++ mlir/lib/Dialect/MQTOpt/Transforms/e.h | 976 ++++++++++++++++++ mlir/lib/Dialect/MQTOpt/Transforms/f.h | 97 ++ mlir/lib/Dialect/MQTOpt/Transforms/g.h | 32 + 6 files changed, 1330 insertions(+), 2 deletions(-) create mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/a.h create mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/c.h create mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/e.h create mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/f.h create mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/g.h diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 847de97f6a..27c4d43063 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -9,6 +9,7 @@ */ #include "Helpers.h" +#include "g.h" #include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" #include "mlir/Dialect/MQTOpt/Transforms/Passes.h" @@ -25,8 +26,6 @@ #include #include -#include "d.h" - namespace mqt::ir::opt { /** diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/a.h b/mlir/lib/Dialect/MQTOpt/Transforms/a.h new file mode 100644 index 0000000000..57e4678a34 --- /dev/null +++ b/mlir/lib/Dialect/MQTOpt/Transforms/a.h @@ -0,0 +1,82 @@ +#pragma once + +#include "Helpers.h" + +namespace mqt::ir::opt { + +// Function to compute the norm (Frobenius norm) of a matrix +double norm(const std::array& A) { + double sum = 0.0; + for (size_t i = 0; i < 16; ++i) { + sum += A[i] * A[i]; + } + return sqrt(sum); +} + +// Function to perform a Jacobi rotation +void jacobi_rotate(std::array& A, std::array& V, int p, int q) { + // Compute the Jacobi rotation matrix + double theta = 0.5 * atan2(2 * A[p * 4 + q], A[q * 4 + q] - A[p * 4 + p]); + double c = cos(theta); + double s = sin(theta); + + // Apply the rotation to matrix A + for (int i = 0; i < 4; ++i) { + double ap = A[i * 4 + p]; + double aq = A[i * 4 + q]; + A[i * 4 + p] = c * ap - s * aq; + A[i * 4 + q] = s * ap + c * aq; + } + + // Apply the rotation to matrix V (eigenvectors) + for (int i = 0; i < 4; ++i) { + double vi_p = V[i * 4 + p]; + double vi_q = V[i * 4 + q]; + V[i * 4 + p] = c * vi_p - s * vi_q; + V[i * 4 + q] = s * vi_p + c * vi_q; + } +} + +// Function to perform the Jacobi method for eigenvalue decomposition +void jacobi_eigen_decomposition(std::array& A, std::array& V, std::array& eigenvalues, double tolerance = 1e-9, int max_iterations = 1000) { + // Initialize the eigenvector matrix V to identity + V.fill(0.0); + for (int i = 0; i < 4; ++i) { + V[i * 4 + i] = 1.0; + } + + int iterations = 0; + double off_diagonal_norm = norm(A); + + // Jacobi rotation iterations + while (off_diagonal_norm > tolerance && iterations < max_iterations) { + // Find the largest off-diagonal element + double max_off_diag = 0.0; + int p = 0, q = 0; + for (int i = 0; i < 3; ++i) { + for (int j = i + 1; j < 4; ++j) { + double abs_val = fabs(A[i * 4 + j]); + if (abs_val > max_off_diag) { + max_off_diag = abs_val; + p = i; + q = j; + } + } + } + + // Perform a Jacobi rotation if necessary + if (max_off_diag > tolerance) { + jacobi_rotate(A, V, p, q); + } + + // Update the off-diagonal norm + off_diagonal_norm = norm(A); + ++iterations; + } + + // Extract the eigenvalues from the diagonal of A + for (int i = 0; i < 4; ++i) { + eigenvalues[i] = A[i * 4 + i]; + } +} +} diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/c.h b/mlir/lib/Dialect/MQTOpt/Transforms/c.h new file mode 100644 index 0000000000..315ed0a69d --- /dev/null +++ b/mlir/lib/Dialect/MQTOpt/Transforms/c.h @@ -0,0 +1,142 @@ +#pragma once + +#include "Helpers.h" + +namespace mqt::ir::opt { +// return Q, R such that A = Q * R +static void qrDecomposition(const rmatrix4x4& A, rmatrix4x4& Q, rmatrix4x4& R) { + // array of factor Q1, Q2, ... Qm + std::vector qv(4); + + // temp array + auto z(A); + rmatrix4x4 z1; + + auto vmadd = [](const auto& a, const auto& b, double s, auto& c) { + for (int i = 0; i < 4; i++) + c[i] = a[i] + s * b[i]; + }; + + auto compute_householder_factor = [](rmatrix4x4& mat, const rdiagonal4x4& v) { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + mat[i + 4 * j] = -2.0 * v[i] * v[j]; + for (int i = 0; i < 4; i++) + mat[i * 4 + i] += 1; + }; + + // take c-th column of a matrix, put results in Vector v + auto extract_column = [](const rmatrix4x4& m, rdiagonal4x4& v, int c) { + for (int i = 0; i < 4; i++) + v[i] = m[i + 4 * c]; + }; + + auto compute_minor = [](rmatrix4x4& lhs, const rmatrix4x4& rhs, int d) { + for (int i = 0; i < d; i++) + lhs[i * 4 + i] = 1.0; + for (int i = d; i < 4; i++) + for (int j = d; j < 4; j++) + lhs[i + 4 * j] = rhs[i + 4 * j]; + }; + + auto norm = [](auto&& m) { + double sum = 0; + for (int i = 0; i < m.size(); i++) + sum += m[i] * m[i]; + return sqrt(sum); + }; + + auto rescale_unit = [&](auto& m) { + auto factor = norm(m); + for (int i = 0; i < m.size(); i++) + m[i] /= factor; + }; + + for (int k = 0; k < 4 && k < 4 - 1; k++) { + + rdiagonal4x4 e{}, x{}; + double a{}; + + // compute minor + compute_minor(z1, z, k); + + // extract k-th column into x + extract_column(z1, x, k); + + a = norm(x); + if (A[k * 4 + k] > 0) + a = -a; + + for (int i = 0; i < 4; i++) + e[i] = (i == k) ? 1 : 0; + + // e = x + a*e + vmadd(x, e, a, e); + + // e = e / ||e|| + rescale_unit(e); + + // qv[k] = I - 2 *e*e^T + compute_householder_factor(qv[k], e); + + // z = qv[k] * z1 + z = helpers::multiply(qv[k], z1); + } + + Q = qv[0]; + + // after this loop, we will obtain Q (up to a transpose operation) + for (int i = 1; i < 4 && i < 4 - 1; i++) { + + z1 = helpers::multiply(qv[i], Q); + Q = z1; + } + + R = helpers::multiply(Q, A); + Q = helpers::transpose(Q); +} + +// Function to perform self-adjoint eigenvalue decomposition (4x4 matrix) +static rmatrix4x4 // eigenvectors (4x4) +self_adjoint_evd(rmatrix4x4 A, // input symmetric matrix (4x4) + rdiagonal4x4& s // eigenvalues +) { + rmatrix4x4 U = {1, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 1}; // Start with identity + + auto isConverged = [](const rmatrix4x4& A, double tol = 1e-10) -> bool { + double sum = 0.0; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + if (i != j) + sum += A[i + 4 * j] * A[i + 4 * j]; + } + } + return std::sqrt(sum) < tol; + }; + + rmatrix4x4 Q{}; + rmatrix4x4 R{}; + + constexpr auto maxIters = 100; + for (int iter = 0; iter < maxIters; ++iter) { + qrDecomposition(A, Q, R); + + // A = R * Q + A = helpers::multiply(R, Q); + + // eigenVectors = eigenVectors * Q + U = helpers::multiply(U, Q); + + if (isConverged(A)) { + break; + } + } + + for (int i = 0; i < 4; ++i) { + s[i] = A[i * 4 + i]; + } + return U; +} + +} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/e.h b/mlir/lib/Dialect/MQTOpt/Transforms/e.h new file mode 100644 index 0000000000..d407422a0b --- /dev/null +++ b/mlir/lib/Dialect/MQTOpt/Transforms/e.h @@ -0,0 +1,976 @@ +// +// Analytical Eigen-decomposition for 4x4 and 3x3 matrices +// +// +// Robin Straebler - George Terzakis +// +// University of Portsmouth 2016 +// + +#ifndef __EIGENDECOMPOSE_H__ +#define __EIGENDECOMPOSE_H__ + +#include "Helpers.h" + +#include +#include +#include +#include +#include +#include + +// The 4x4 implies that the namespace contains algorithms +// for the eigen decomposition of 3x3 or 4x4 matrices. +// +namespace mqt::ir::opt { + +namespace PolySolvers { + +// see http://mathworld.wolfram.com/QuadraticFormula.html +template inline std::vector

SolveQuadratic(P a, P b, P c) { + P delta = b * b - 4 * a * c; + P inv_2a, sqrt_delta; + P x1, x0; + + if (delta < 0) + return std::vector

(); + if (a == 0) { + // solve first order system + x1 = 0; + if (b != 0) { + x0 = -c / b; + return std::vector

({x0}); + } + + x0 = 0; + return std::vector

(); + } + + inv_2a = 0.5 / a; + if (delta == 0) { + x0 = -b * inv_2a; + x1 = x0; + return std::vector

({x0}); + } + + sqrt_delta = sqrt(delta); + x0 = (-b + sqrt_delta) * inv_2a; + x1 = (-b - sqrt_delta) * inv_2a; + return std::vector

({x0, x1}); +} + +/* see http://mathworld.wolfram.com/CubicEquation.html */ +template inline std::vector

SolveCubic(P a, P b, P c, P d) { + P inv_a, b_a, b_a2, c_a, d_a; + P Q, R, Q3, D, b_a_3; + P AD, BD; + + P x0, x1, x2; + + if (a == 0) { + /* solve second order system */ + if (b == 0) { + /* solve first order system */ + if (c == 0) + return std::vector

(); + + x0 = -d / c; + return std::vector

({x0}); + } + + x2 = 0; + return SolveQuadratic

(b, c, d); + } + + /* calculate the normalized form x^3 + a2 * x^2 + a1 * x + a0 = 0 */ + inv_a = 1.0 / a; + b_a = inv_a * b; + b_a2 = b_a * b_a; + c_a = inv_a * c; + d_a = inv_a * d; + + /* solve the cubic equation */ + Q = (3 * c_a - b_a2) / 9; + R = (9 * b_a * c_a - 27 * d_a - 2 * b_a * b_a2) / 54; + Q3 = Q * Q * Q; + D = Q3 + R * R; + b_a_3 = (1.0 / 3.0) * b_a; + + if (Q == 0) { + if (R == 0) { + x0 = x1 = x2 = -b_a_3; + return std::vector

({x0, x1, x2}); + } else { + x0 = pow(2 * R, 1 / 3.0) - b_a_3; + return std::vector

({x0}); + } + } + + if (D <= 0) { + /* three real roots */ + P theta = acos(R / sqrt(-Q3)); + P sqrt_Q = sqrt(-Q); + x0 = 2 * sqrt_Q * cos(theta / 3.0) - b_a_3; + x1 = 2 * sqrt_Q * cos((theta + 2 * M_PI) / 3.0) - b_a_3; + x2 = 2 * sqrt_Q * cos((theta + 4 * M_PI) / 3.0) - b_a_3; + + return std::vector

({x0, x1, x2}); + } + + /* D > 0, only one real root */ + AD = pow(fabs(R) + sqrt(D), 1.0 / 3.0) * (R > 0 ? 1 : (R < 0 ? -1 : 0)); + BD = (AD == 0) ? 0 : -Q / AD; + + /* calculate the sole real root */ + x0 = AD + BD - b_a_3; + + return std::vector

({x0}); +} + +/* see http://mathworld.wolfram.com/QuarticEquation.html */ +template +inline std::vector

SolveQuartic(P a, P b, P c, P d, P e) { + P inv_a, b2, bc, b3, b_4; + P r0; + int nb_real_roots; + P R2, R_2, R, inv_R; + P D2, E2; + + P x0 = 0, x1 = 0, x2 = 0, x3 = 0; + + if (a == 0) { + x3 = 0; + return SolveCubic

(b, c, d, e); + } + + /* normalize coefficients */ + inv_a = 1.0 / a; + b *= inv_a; + c *= inv_a; + d *= inv_a; + e *= inv_a; + b2 = b * b; + bc = b * c; + b3 = b2 * b; + + /* solve resultant cubic */ + auto solution3 = + SolveCubic

(1, -c, d * b - 4 * e, 4 * c * e - d * d - b2 * e); + int n = (solution3.size() == 0) ? 0 : solution3.size(); + + if (n == 0) + return solution3; + + r0 = solution3[0]; + /* calculate R^2 */ + R2 = 0.25 * b2 - c + r0; + if (R2 < 0) + return std::vector

(); + + R = sqrt(R2); + inv_R = 1.0 / R; + + nb_real_roots = 0; + + /* calculate D^2 and E^2 */ + if (R < 10E-12) { + P temp = r0 * r0 - 4 * e; + if (temp < 0) { + D2 = E2 = -1; + } else { + P sqrt_temp = sqrt(temp); + D2 = 0.75 * b2 - 2 * c + 2 * sqrt_temp; + E2 = D2 - 4 * sqrt_temp; + } + } else { + P u = 0.75 * b2 - 2 * c - R2, v = 0.25 * inv_R * (4 * bc - 8 * d - b3); + D2 = u + v; + E2 = u - v; + } + + b_4 = 0.25 * b; + R_2 = 0.5 * R; + if (D2 >= 0) { + P D = sqrt(D2); + P D_2 = 0.5 * D; + nb_real_roots = 2; + x0 = R_2 + D_2 - b_4; + x1 = x0 - D; + } + + /* calculate E^2 */ + if (E2 >= 0) { + P E = sqrt(E2); + P E_2 = 0.5 * E; + if (nb_real_roots == 0) { + x0 = -R_2 + E_2 - b_4; + x1 = x0 - E; + nb_real_roots = 2; + } else { + x2 = -R_2 + E_2 - b_4; + x3 = x2 - E; + nb_real_roots = 4; + } + } + switch (nb_real_roots) { + // case 0: + // return new Tuple(0, null); + // break; // covered by the "default" case + case 1: + return std::vector

({x0}); + // break; + case 2: + return std::vector

({x0, x1}); + // break; + case 3: + return std::vector

({x0, x1, x2}); + // break; + case 4: + return std::vector

({x0, x1, x2, x3}); + // break; + default: + return std::vector

(); // just to shut the compiler up.... + // break; + } +} + +} // end namespace PolySolvers + +///

+/// This function performs two steps of Gauss-Jordan elmination in a 3x3 matrix. +/// NOTE: The function assumes that the matrix provided is RANK-2 and therefore +/// the solution +// will be the null space (vector). This way we obtain an eigen vector in +// two steps. +/// +/// is the coefficient matrix in flat +/// form std::vector

solutions +template std::vector

GaussJordan3x3(std::vector

flat_mat) { + // For the first step of the Gauss-Jordan Pivoting, we need the max of the + // coefficient from list0Pivot + // + auto absmax1it = std::max_element( + flat_mat.begin(), flat_mat.end(), + [](const int& a, const int& b) { return fabs(a) < fabs(b); }); + + P max1 = *absmax1it; + + // Get the index of max (in absolute value) + int index1 = std::distance(flat_mat.begin(), absmax1it); + + // If max1 = 0, we have a zero matrix and we need to return an empty list + if (max1 == 0) + return std::vector

(); + + // For the second step of the Gauss-Jordan Pivoting, we will require the + // maximum of the elements of the resulting matrix. + P max2; // The value of the absolute max in the next stage of the elimination + // (only need 2 stages for 3x3 matrices) + int index2; // the index of the absolute maximum in the next stage of the + // elimination + std::vector

flat_mat1; // and the next matrix + + // Variable which contain the coordinates of a vector + P x1 = 0, x2 = 0, x3 = 0; + + P* pX[3]; // using a pointer array to store permutations of x1, x2, x3 + P listFinalCoef[2]; + + // Taking cases according to where the maximum lies + if (index1 == 0) { + // List of the value after the first step of the Gauss-Jordan Pivoting + flat_mat1.push_back(flat_mat[4] - ((flat_mat[1] * flat_mat[3]) / max1)); + flat_mat1.push_back(flat_mat[5] - ((flat_mat[2] * flat_mat[3]) / max1)); + flat_mat1.push_back(flat_mat[7] - ((flat_mat[1] * flat_mat[6]) / max1)); + flat_mat1.push_back(flat_mat[8] - ((flat_mat[2] * flat_mat[6]) / max1)); + + // Get maximum and minimum elements (in order to get the maximum in absolute + // value) + auto absmax2it = std::max_element( + flat_mat1.begin(), flat_mat1.end(), + [](const int& a, const int& b) { return fabs(a) < fabs(b); }); + max2 = *absmax2it; + + // NOTE: Now max2it points to the absolute maximum ! + index2 = std::distance(flat_mat1.begin(), absmax2it); + + pX[0] = &x1; + pX[1] = &x2; + pX[2] = &x3; + + listFinalCoef[0] = flat_mat[1]; + listFinalCoef[1] = flat_mat[2]; + + } else if (index1 == 1) { + flat_mat1.push_back(flat_mat[3] - ((flat_mat[0] * flat_mat[4]) / max1)); + flat_mat1.push_back(flat_mat[5] - ((flat_mat[2] * flat_mat[4]) / max1)); + flat_mat1.push_back(flat_mat[6] - ((flat_mat[0] * flat_mat[7]) / max1)); + flat_mat1.push_back(flat_mat[8] - ((flat_mat[2] * flat_mat[7]) / max1)); + + // Get maximum and minimum elements (in order to get the maximum in absolute + // value) + auto absmax2it = std::max_element( + flat_mat1.begin(), flat_mat1.end(), + [](const int& a, const int& b) { return fabs(a) < fabs(b); }); + max2 = *absmax2it; + + // NOTE: Now max2it points to the absolute maximum ! + index2 = std::distance(flat_mat1.begin(), absmax2it); + + pX[0] = &x2; + pX[1] = &x1; + pX[2] = &x3; + + listFinalCoef[0] = flat_mat[0]; + listFinalCoef[1] = flat_mat[2]; + + } else if (index1 == 2) { + flat_mat1.push_back(flat_mat[3] - ((flat_mat[0] * flat_mat[5]) / max1)); + flat_mat1.push_back(flat_mat[4] - ((flat_mat[1] * flat_mat[5]) / max1)); + flat_mat1.push_back(flat_mat[6] - ((flat_mat[0] * flat_mat[8]) / max1)); + flat_mat1.push_back(flat_mat[7] - ((flat_mat[1] * flat_mat[8]) / max1)); + + // Get maximum and minimum elements (in order to get the maximum in absolute + // value) + auto absmax2it = std::max_element( + flat_mat1.begin(), flat_mat1.end(), + [](const int& a, const int& b) { return fabs(a) < fabs(b); }); + max2 = *absmax2it; + + // NOTE: Now max2it points to the absolute maximum ! + index2 = std::distance(flat_mat1.begin(), absmax2it); + + pX[0] = &x3; + pX[1] = &x1; + pX[2] = &x2; + + listFinalCoef[0] = flat_mat[0]; + listFinalCoef[1] = flat_mat[1]; + + } else if (index1 == 3) { + flat_mat1.push_back(flat_mat[1] - ((flat_mat[4] * flat_mat[0]) / max1)); + flat_mat1.push_back(flat_mat[2] - ((flat_mat[0] * flat_mat[5]) / max1)); + flat_mat1.push_back(flat_mat[7] - ((flat_mat[4] * flat_mat[6]) / max1)); + flat_mat1.push_back(flat_mat[8] - ((flat_mat[5] * flat_mat[6]) / max1)); + + auto max2it = std::max_element(flat_mat1.begin(), flat_mat1.end()); + auto min2it = std::min_element(flat_mat1.begin(), flat_mat1.end()); + max2 = *max2it; + + // Get maximum and minimum elements (in order to get the maximum in absolute + // value) + auto absmax2it = std::max_element( + flat_mat1.begin(), flat_mat1.end(), + [](const int& a, const int& b) { return fabs(a) < fabs(b); }); + max2 = *absmax2it; + + // NOTE: Now max2it points to the absolute maximum ! + index2 = std::distance(flat_mat1.begin(), absmax2it); + + pX[0] = &x1; + pX[1] = &x2; + pX[2] = &x3; + + listFinalCoef[0] = flat_mat[4]; + listFinalCoef[1] = flat_mat[5]; + + } else if (index1 == 4) { + flat_mat1.push_back(flat_mat[0] - ((flat_mat[1] * flat_mat[3]) / max1)); + flat_mat1.push_back(flat_mat[2] - ((flat_mat[5] * flat_mat[1]) / max1)); + flat_mat1.push_back(flat_mat[6] - ((flat_mat[3] * flat_mat[7]) / max1)); + flat_mat1.push_back(flat_mat[8] - ((flat_mat[5] * flat_mat[7]) / max1)); + + // Get maximum and minimum elements (in order to get the maximum in absolute + // value) + auto absmax2it = std::max_element( + flat_mat1.begin(), flat_mat1.end(), + [](const int& a, const int& b) { return fabs(a) < fabs(b); }); + max2 = *absmax2it; + + // NOTE: Now max2it points to the absolute maximum ! + index2 = std::distance(flat_mat1.begin(), absmax2it); + + pX[0] = &x2; + pX[1] = &x1; + pX[2] = &x3; + + listFinalCoef[0] = flat_mat[3]; + listFinalCoef[1] = flat_mat[5]; + + } else if (index1 == 5) { + flat_mat1.push_back(flat_mat[0] - ((flat_mat[2] * flat_mat[3]) / max1)); + flat_mat1.push_back(flat_mat[1] - ((flat_mat[2] * flat_mat[4]) / max1)); + flat_mat1.push_back(flat_mat[6] - ((flat_mat[8] * flat_mat[3]) / max1)); + flat_mat1.push_back(flat_mat[7] - ((flat_mat[8] * flat_mat[4]) / max1)); + + // Get maximum and minimum elements (in order to get the maximum in absolute + // value) + auto absmax2it = std::max_element( + flat_mat1.begin(), flat_mat1.end(), + [](const int& a, const int& b) { return fabs(a) < fabs(b); }); + max2 = *absmax2it; + + // NOTE: Now max2it points to the absolute maximum ! + index2 = std::distance(flat_mat1.begin(), absmax2it); + + pX[0] = &x3; + pX[1] = &x1; + pX[2] = &x2; + + listFinalCoef[0] = flat_mat[3]; + listFinalCoef[1] = flat_mat[4]; + + } else if (index1 == 6) { + flat_mat1.push_back(flat_mat[1] - ((flat_mat[0] * flat_mat[7]) / max1)); + flat_mat1.push_back(flat_mat[2] - ((flat_mat[0] * flat_mat[8]) / max1)); + flat_mat1.push_back(flat_mat[4] - ((flat_mat[3] * flat_mat[7]) / max1)); + flat_mat1.push_back(flat_mat[5] - ((flat_mat[3] * flat_mat[8]) / max1)); + + // Get maximum and minimum elements (in order to get the maximum in absolute + // value) + auto absmax2it = std::max_element( + flat_mat1.begin(), flat_mat1.end(), + [](const int& a, const int& b) { return fabs(a) < fabs(b); }); + max2 = *absmax2it; + + // NOTE: Now max2it points to the absolute maximum ! + index2 = std::distance(flat_mat1.begin(), absmax2it); + + pX[0] = &x1; + pX[1] = &x2; + pX[2] = &x3; + + listFinalCoef[0] = flat_mat[7]; + listFinalCoef[1] = flat_mat[8]; + + } else if (index1 == 7) { + flat_mat1.push_back(flat_mat[0] - ((flat_mat[1] * flat_mat[6]) / max1)); + flat_mat1.push_back(flat_mat[2] - ((flat_mat[1] * flat_mat[8]) / max1)); + flat_mat1.push_back(flat_mat[3] - ((flat_mat[4] * flat_mat[6]) / max1)); + flat_mat1.push_back(flat_mat[5] - ((flat_mat[4] * flat_mat[8]) / max1)); + + // Get maximum and minimum elements (in order to get the maximum in absolute + // value) + auto absmax2it = std::max_element( + flat_mat1.begin(), flat_mat1.end(), + [](const int& a, const int& b) { return fabs(a) < fabs(b); }); + max2 = *absmax2it; + + // NOTE: Now max2it points to the absolute maximum ! + index2 = std::distance(flat_mat1.begin(), absmax2it); + + pX[0] = &x2; + pX[1] = &x1; + pX[2] = &x3; + + listFinalCoef[0] = flat_mat[6]; + listFinalCoef[1] = flat_mat[8]; + + } else if (index1 == 8) { + flat_mat1.push_back(flat_mat[0] - ((flat_mat[2] * flat_mat[6]) / max1)); + flat_mat1.push_back(flat_mat[1] - ((flat_mat[2] * flat_mat[7]) / max1)); + flat_mat1.push_back(flat_mat[3] - ((flat_mat[5] * flat_mat[6]) / max1)); + flat_mat1.push_back(flat_mat[4] - ((flat_mat[5] * flat_mat[7]) / max1)); + + // Get maximum and minimum elements (in order to get the maximum in absolute + // value) + auto absmax2it = std::max_element( + flat_mat1.begin(), flat_mat1.end(), + [](const int& a, const int& b) { return fabs(a) < fabs(b); }); + max2 = *absmax2it; + + // NOTE: Now max2it points to the absolute maximum ! + index2 = std::distance(flat_mat1.begin(), absmax2it); + + pX[0] = &x3; + pX[1] = &x1; + pX[2] = &x2; + + listFinalCoef[0] = flat_mat[6]; + listFinalCoef[1] = flat_mat[7]; + } + + if (index2 == 0) { + *(pX[2]) = 1; + *(pX[1]) = -flat_mat1[1] / max2; + } else if (index2 == 1) { + *(pX[2]) = -flat_mat1[0] / max2; + *(pX[1]) = 1; + } else if (index2 == 2) { + *(pX[2]) = 1; + *(pX[1]) = -flat_mat1[3] / max2; + } else if (index2 == 3) { + *(pX[2]) = -flat_mat1[2] / max2; + *(pX[1]) = 1; + } + + *(pX[0]) = -((listFinalCoef[0] * *(pX[1])) / max1) - + ((listFinalCoef[1] * *(pX[2])) / max1); + + // normalize + P norm = sqrt(x1 * x1 + x2 * x2 + x3 * x3); + + return std::vector

({x1 / norm, x2 / norm, x3 / norm}); +} + +/// +/// This function eliminates the coefficients of the column "col" based on an +/// element at (row, col) +/// +/// +template +std::vector

GaussJordanFirstStep(std::vector

flat_mat, int row, int col) { + + // the result + std::vector

result; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + + if (i != row && j != col) { + result.push_back(flat_mat[i * 4 + j] - + ((flat_mat[row * 4 + j] * flat_mat[i * 4 + col]) / + flat_mat[row * 4 + col])); + } + } + } + + return result; +} + +/// +/// The Gauss-Jordan elimination for Rank-3 4x4 matrices +/// The steps are fixed and lead to a solution up to arbitrary scale. +/// This is how we solve for the eigenvectors of the a 4x4 marix. +/// +/// The input vector is a flat version of the 4x4 matrix +/// +template std::vector

GaussJordan4x4(std::vector

flat_mat) { + // Working out the the index of the max coefficient (absolute value). + auto absmax1it = std::max_element( + flat_mat.begin(), flat_mat.end(), + [](const int& a, const int& b) { return fabs(a) < fabs(b); }); + + P max1 = *absmax1it; + int index1 = std::distance(flat_mat.begin(), absmax1it); + + // Return empty list if the maximum is zero (zero matrix) + if (max1 == 0) + return std::vector

(); + + // Create new variable which are use after. + std::vector

flat_mat1; + + P x1 = 0, x2 = 0, x3 = 0, x4 = 0; + // Cache for the result of 3x3 Gauss Jordan elimination + std::vector

resultGaussJordan3x3; + + // go... + if (index1 == 0) { + /// We use the GaussJordan3x3 solver after the first step, which is the + /// elimination of coeffcieints along column 0. + flat_mat1 = GaussJordanFirstStep(flat_mat, 0, 0); + resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); + x2 = resultGaussJordan3x3[0]; + x3 = resultGaussJordan3x3[1]; + x4 = resultGaussJordan3x3[2]; + x1 = -(1 / flat_mat[0]) * + (flat_mat[1] * x2 + flat_mat[2] * x3 + flat_mat[3] * x4); + } else if (index1 == 1) { + /// We use the GaussJordan3x3 solver after the first step, which is the + /// elimination of coeffcieints along column 1. + flat_mat1 = GaussJordanFirstStep(flat_mat, 0, 1); + resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); + x1 = resultGaussJordan3x3[0]; + x3 = resultGaussJordan3x3[1]; + x4 = resultGaussJordan3x3[2]; + x2 = -(1 / flat_mat[1]) * + (flat_mat[0] * x1 + flat_mat[2] * x3 + flat_mat[3] * x4); + } else if (index1 == 2) { + flat_mat1 = GaussJordanFirstStep(flat_mat, 0, 2); + resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); + x1 = resultGaussJordan3x3[0]; + x2 = resultGaussJordan3x3[1]; + x4 = resultGaussJordan3x3[2]; + x3 = -(1 / flat_mat[2]) * + (flat_mat[0] * x1 + flat_mat[1] * x2 + flat_mat[3] * x4); + } else if (index1 == 3) { + flat_mat1 = GaussJordanFirstStep(flat_mat, 0, 3); + resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); + x1 = resultGaussJordan3x3[0]; + x2 = resultGaussJordan3x3[1]; + x3 = resultGaussJordan3x3[2]; + x4 = -(1 / flat_mat[3]) * + (flat_mat[0] * x1 + flat_mat[1] * x2 + flat_mat[2] * x3); + } else if (index1 == 4) { + flat_mat1 = GaussJordanFirstStep(flat_mat, 1, 0); + resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); + x2 = resultGaussJordan3x3[0]; + x3 = resultGaussJordan3x3[1]; + x4 = resultGaussJordan3x3[2]; + x1 = -(1 / flat_mat[4]) * + (flat_mat[5] * x2 + flat_mat[6] * x3 + flat_mat[7] * x4); + } else if (index1 == 5) { + flat_mat1 = GaussJordanFirstStep(flat_mat, 1, 1); + resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); + x1 = resultGaussJordan3x3[0]; + x3 = resultGaussJordan3x3[1]; + x4 = resultGaussJordan3x3[2]; + x2 = -(1 / flat_mat[5]) * + (flat_mat[4] * x1 + flat_mat[6] * x3 + flat_mat[7] * x4); + } else if (index1 == 6) { + flat_mat1 = GaussJordanFirstStep(flat_mat, 1, 2); + resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); + x1 = resultGaussJordan3x3[0]; + x2 = resultGaussJordan3x3[1]; + x4 = resultGaussJordan3x3[2]; + x3 = -(1 / flat_mat[6]) * + (flat_mat[4] * x1 + flat_mat[5] * x2 + flat_mat[7] * x4); + } else if (index1 == 7) { + flat_mat1 = GaussJordanFirstStep(flat_mat, 1, 3); + resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); + x1 = resultGaussJordan3x3[0]; + x2 = resultGaussJordan3x3[1]; + x3 = resultGaussJordan3x3[2]; + x4 = -(1 / flat_mat[7]) * + (flat_mat[4] * x1 + flat_mat[5] * x2 + flat_mat[6] * x3); + } else if (index1 == 8) { + flat_mat1 = GaussJordanFirstStep(flat_mat, 2, 0); + resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); + x2 = resultGaussJordan3x3[0]; + x3 = resultGaussJordan3x3[1]; + x4 = resultGaussJordan3x3[2]; + x1 = -(1 / flat_mat[8]) * + (flat_mat[9] * x2 + flat_mat[10] * x3 + flat_mat[11] * x4); + } else if (index1 == 9) { + flat_mat1 = GaussJordanFirstStep(flat_mat, 2, 1); + resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); + x1 = resultGaussJordan3x3[0]; + x3 = resultGaussJordan3x3[1]; + x4 = resultGaussJordan3x3[2]; + x2 = -(1 / flat_mat[9]) * + (flat_mat[8] * x1 + flat_mat[10] * x3 + flat_mat[11] * x4); + } else if (index1 == 10) { + flat_mat1 = GaussJordanFirstStep(flat_mat, 2, 2); + resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); + x1 = resultGaussJordan3x3[0]; + x2 = resultGaussJordan3x3[1]; + x4 = resultGaussJordan3x3[2]; + x3 = -(1 / flat_mat[10]) * + (flat_mat[8] * x1 + flat_mat[9] * x2 + flat_mat[11] * x4); + } else if (index1 == 11) { + flat_mat1 = GaussJordanFirstStep(flat_mat, 2, 3); + resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); + x1 = resultGaussJordan3x3[0]; + x2 = resultGaussJordan3x3[1]; + x3 = resultGaussJordan3x3[2]; + x4 = -(1 / flat_mat[11]) * + (flat_mat[8] * x1 + flat_mat[9] * x2 + flat_mat[10] * x3); + } else if (index1 == 12) { + flat_mat1 = GaussJordanFirstStep(flat_mat, 3, 0); + resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); + x2 = resultGaussJordan3x3[0]; + x3 = resultGaussJordan3x3[1]; + x4 = resultGaussJordan3x3[2]; + x1 = -(1 / flat_mat[12]) * + (flat_mat[13] * x2 + flat_mat[14] * x3 + flat_mat[15] * x4); + + } else if (index1 == 13) { + flat_mat1 = GaussJordanFirstStep(flat_mat, 3, 1); + resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); + x1 = resultGaussJordan3x3[0]; + x3 = resultGaussJordan3x3[1]; + x4 = resultGaussJordan3x3[2]; + x2 = -(1 / flat_mat[13]) * + (flat_mat[12] * x1 + flat_mat[14] * x3 + flat_mat[15] * x4); + } else if (index1 == 14) { + flat_mat1 = GaussJordanFirstStep(flat_mat, 3, 2); + resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); + x1 = resultGaussJordan3x3[0]; + x2 = resultGaussJordan3x3[1]; + x4 = resultGaussJordan3x3[2]; + x3 = -(1 / flat_mat[14]) * + (flat_mat[12] * x1 + flat_mat[13] * x2 + flat_mat[15] * x4); + } else if (index1 == 15) { + flat_mat1 = GaussJordanFirstStep(flat_mat, 3, 3); + resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); + x1 = resultGaussJordan3x3[0]; + x2 = resultGaussJordan3x3[1]; + x3 = resultGaussJordan3x3[2]; + x4 = -(1 / flat_mat[15]) * + (flat_mat[12] * x1 + flat_mat[13] * x2 + flat_mat[14] * x3); + } + + // normalize the solution + P norm = sqrt(x1 * x1 + x2 * x2 + x3 * x3 + x4 * x4); + + return std::vector

({x1 / norm, x2 / norm, x3 / norm, x4 / norm}); +} + +/// +/// Return the list of the eigenvalue of a 4x4 matrix. +/// Parameter is he argumen is a 1D array representing the 4x4 matrix +/// T +/// +template std::vector

EigenValues4x4(P* array) { + + P a11 = array[0 * 4 + 0], a12 = array[0 * 4 + 1], a13 = array[0 * 4 + 2], + a14 = array[0 * 4 + 3]; + P a21 = array[1 * 4 + 0], a22 = array[1 * 4 + 1], a23 = array[1 * 4 + 2], + a24 = array[1 * 4 + 3]; + P a31 = array[2 * 4 + 0], a32 = array[2 * 4 + 1], a33 = array[2 * 4 + 2], + a34 = array[2 * 4 + 3]; + P a41 = array[3 * 4 + 0], a42 = array[3 * 4 + 1], a43 = array[3 * 4 + 2], + a44 = array[3 * 4 + 3]; + + // 1. Obtaining the coefficients of the characteristics polynomial of A11 - + // λ*I where A11 is the (1, 1) submatrix of A: A11 = [a22-λ a23 a24; + // a32 a33-λ a34; + // a42 a43 a44 - λ] + + // The coefficients of the cubic polynomial det(A11-λI) are: + P C3 = -1, C2 = a22 + a33 + a44; + P C1 = a42 * a24 + a32 * a23 + a34 * a43 - a22 * a33 - a22 * a44 - a33 * a44; + P C0 = a22 * a33 * a44 + a23 * a42 * a34 + a24 * a32 * a43 - a22 * a34 * a43 - + a23 * a32 * a44 - a24 * a42 * a33; + + // 2. Now multiplying with a11 to get a quartic polynomial with coeffcients + // W4, W3, W2, W1, W0 as follows: + P W4 = -C3, W3 = a11 * C3 - C2, W2 = a11 * C2 - C1, W1 = a11 * C1 - C0, + W0 = a11 * C0; + + // 4. Now we obtain the coefficients of the 3 quadratics (from the algebraic + // complements A12, A13, A14) as follows: a. (A-λI)12 (-) + P Q1_2 = -a12 * a21, + Q1_1 = -a12 * (-a21 * (a33 + a44) + a23 * a31 + a24 * a41), + Q1_0 = + -a12 * (a24 * (a31 * a43 - a41 * a33) - a23 * (a31 * a44 - a41 * a34) + + a21 * (a33 * a44 - + a43 * a34)); // good all being well... + // multiplying with -a12 + // P1_2 *= -a12; P1_1 *= -a12; P1_0 *= -a12; + + // b. (A-λ)13 (+) + P Q2_2 = a13 * -a31, + Q2_1 = a13 * ((a31 * a44 - a41 * a34 + a22 * a31) - a21 * a32), + Q2_0 = + a13 * (a24 * (a31 * a42 - a41 * a32) + a22 * (a41 * a34 - a31 * a44) + + a21 * (a32 * a44 - a42 * a34)); + + // c. (A-λ)14 (-) + P Q3_2 = -a14 * a41, + Q3_1 = -a14 * ((a31 * a43 - a41 * a33 - a22 * a41) + a21 * a42), + Q3_0 = + -a14 * (a23 * (a31 * a42 - a41 * a32) - a22 * (a31 * a43 - a41 * a33) + + a21 * (a32 * a43 - a42 * a33)); + + // 5. Final coefficients + P A0 = Q3_0 + Q2_0 + Q1_0 + W0, A1 = Q3_1 + Q2_1 + Q1_1 + W1, + A2 = Q3_2 + Q2_2 + Q1_2 + W2, A3 = W3, A4 = W4; + + std::vector

solution = PolySolvers::SolveQuartic(A4, A3, A2, A1, A0); + + // Put the solutions in ascending order + std::sort(solution.begin(), solution.end()); + + return solution; +} + +/// +/// Return the list of the eigenvalues of a 3x3 matrix. +/// Parameter is he argumen is a 1D array representing the 4x4 matrix +/// T +/// +template std::vector

EigenValues3x3(const P* array) { + + P coef1 = -1; + P coef2 = (array[0 * 3 + 0] + array[1 * 3 + 1] + array[2 * 3 + 2]); + P coef3 = (array[2 * 3 + 0] * array[0 * 3 + 2]) + + (array[1 * 3 + 0] * array[0 * 3 + 1]) + + (array[1 * 3 + 2] * array[2 * 3 + 1]) - + (array[0 * 3 + 0] * array[1 * 3 + 1]) - + (array[0 * 3 + 0] * array[2 * 3 + 2]) - + (array[1 * 3 + 1] * array[2 * 3 + 2]); + + P coef4 = (array[0 * 3 + 0] * array[1 * 3 + 1] * array[2 * 3 + 2]) + + (array[0 * 3 + 1] * array[2 * 3 + 0] * array[1 * 3 + 2]) + + (array[0 * 3 + 2] * array[1 * 3 + 0] * array[2 * 3 + 1]) - + (array[0 * 3 + 0] * array[1 * 3 + 2] * array[2 * 3 + 1]) - + (array[0 * 3 + 1] * array[1 * 3 + 0] * array[2 * 3 + 2]) - + (array[0 * 3 + 2] * array[2 * 3 + 0] * array[1 * 3 + 1]); + + std::vector

solution = PolySolvers::SolveCubic(coef1, coef2, coef3, coef4); + + std::sort(solution.begin(), solution.end()); + + // for (int i = 0; i < solution.size(); i++) + // std::cout << "eigenvalue "< std::vector

PrincipalEigenvector4x4(P* M) { + // First obtain the eigenvalues of M + auto eigenvalues = EigenValues4x4(M); + if (eigenvalues.size() == 0) + return std::vector

({0, 0, 0, 0}); + // auto absmaxit = std::max_element( eigenvalues.begin(), + // eigenvalues.end() , + // [](const int& a, const int& b) { return fabs(a) < + // fabs(b);} + // ); + // P lambda = *absmaxit; + P lambda = eigenvalues[eigenvalues.size() - + 1]; // returning the largest eigenvalue (instead or + // trhe largest in absolute value) + if (lambda == 0) + return std::vector

({0, 0, 0, 0}); + // Now obtaining a vector containing the M-lambda * eye(3) + std::vector

A; + A.push_back(M[0] - lambda); + A.push_back(M[1]); + A.push_back(M[2]); + A.push_back(M[3]); + A.push_back(M[4]); + A.push_back(M[5] - lambda); + A.push_back(M[6]); + A.push_back(M[7]); + A.push_back(M[8]); + A.push_back(M[9]); + A.push_back(M[10] - lambda); + A.push_back(M[11]); + A.push_back(M[12]); + A.push_back(M[13]); + A.push_back(M[14]); + A.push_back(M[15] - lambda); + + // Now get the eigenvector using the Gauss-jordan steps + return GaussJordan4x4(A); +} + +// Get the eigenvector of the largest eigenvalue of a 4x4 matrix. +// NOTE: This function will return the zero vector even if no eigenvalues exist +template std::vector

PrincipalEigenvector3x3(P* M) { + // First obtain the eigenvalues of M + auto eigenvalues = EigenValues3x3(M); + if (eigenvalues.size() == 0) + return std::vector

({0, 0, 0}); + // auto maxit = std::max_element( eigenvalues.begin(), + // eigenvalues.end() , + // [](const int& a, const int& b) { return fabs(a) < + // fabs(b); } + // ); + + // P lambda = *maxit; + P lambda = eigenvalues[eigenvalues.size() - 1]; // get the largest eigenvalue + // if there are only zero eigenvalues, return the zero vector + if (lambda == 0) + return std::vector

({0, 0, 0}); // extra check + + // Now obtaining a vector containing the M-lambda * eye(3) + std::vector

A; + A.push_back(M[0] - lambda); + A.push_back(M[1]); + A.push_back(M[2]); + A.push_back(M[3]); + A.push_back(M[4] - lambda); + A.push_back(M[5]); + A.push_back(M[6]); + A.push_back(M[7]); + A.push_back(M[8] - lambda); + + // Now get the eigenvector using the Gauss-jordan steps + return GaussJordan3x3(A); +} + +// 3x3 Matrix eigen decomposition +template +std::pair, std::vector>> EigenDecompose3x3(P* M) { + // First obtain the eigenvalues of M + auto eigenvalues = EigenValues3x3(M); + // vector of eigenvectors + std::vector> eigenvectors; + // return an empty eigenvalue list and a single eigenvector withg zeros in it + if (eigenvalues.size() == 0) + return std::pair, std::vector>>(eigenvalues, + eigenvectors); + + std::vector

A({M[0], M[1], M[2], M[3], M[4], M[5], M[6], M[7], M[8]}); + + for (int i = 0; i < eigenvalues.size(); i++) { + // Now subtract the eigenvalue from the diagonal + A[0] -= eigenvalues[i]; + A[4] -= eigenvalues[i]; + A[8] -= eigenvalues[i]; + + // compute and add the eigen vector to the list + eigenvectors.push_back(GaussJordan3x3(A)); + + // add the eigenvalue back to the diagonal in order to undo the change in + // the elements of A + A[0] += eigenvalues[i]; + A[4] += eigenvalues[i]; + A[8] += eigenvalues[i]; + } + + // return the decomposition (in ascending eigenvalue order) + return std::pair, std::vector>>(eigenvalues, + eigenvectors); +} + +// 4x4 Matrix eigen decomposition +template +std::pair, std::vector>> EigenDecompose4x4(P* M) { + // First obtain the eigenvalues of M + auto eigenvalues = EigenValues4x4(M); + // vector of eigenvectors + std::vector> eigenvectors; + + // return an empty eigenvalue list and a single eigenvector withg zeros in it + if (eigenvalues.size() == 0) + return std::pair, std::vector>>(eigenvalues, + eigenvectors); + + std::vector

A({M[0], M[1], M[2], M[3], M[4], M[5], M[6], M[7], M[8], M[9], + M[10], M[11], M[12], M[13], M[14], M[15]}); + + for (int i = 0; i < eigenvalues.size(); i++) { + // Now subtract the eigenvalue from the diagonal + A[0] -= eigenvalues[i]; + A[5] -= eigenvalues[i]; + A[10] -= eigenvalues[i]; + A[15] -= eigenvalues[i]; + + // compute and add the eigen vector to the list + eigenvectors.push_back(GaussJordan4x4(A)); + + // add the eigenvalue back to the diagonal in order to undo the change in + // the elements of A + A[0] += eigenvalues[i]; + A[5] += eigenvalues[i]; + A[10] += eigenvalues[i]; + A[15] += eigenvalues[i]; + } + + // return the decomposition (in ascending eigenvalue order) + return std::pair, std::vector>>(eigenvalues, + eigenvectors); +} + +std::pair self_adjoint_evd(rmatrix4x4 A) { + rmatrix4x4 rvectors; // TODO: vectors result in nan because max2 is zero + rdiagonal4x4 rvalues; + auto [values, vectors] = EigenDecompose4x4(A.data()); + for (int i = 0; i < values.size(); ++i) { + for (int j = 0; j < vectors[i].size(); ++j) { + rvectors[j * 4 + i] = vectors[j][i]; + } + rvalues[i] = values[i]; + } + return {rvectors, rvalues}; +} +} // namespace mqt::ir::opt + +#endif diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/f.h b/mlir/lib/Dialect/MQTOpt/Transforms/f.h new file mode 100644 index 0000000000..4cbb4c86cb --- /dev/null +++ b/mlir/lib/Dialect/MQTOpt/Transforms/f.h @@ -0,0 +1,97 @@ +#pragma once + +#include "Helpers.h" + +namespace mqt::ir::opt { +inline bool jacobi_4x4(double * A, double * D, double * U) +{ + double B[4], Z[4]; + double Id[16] = {1., 0., 0., 0., + 0., 1., 0., 0., + 0., 0., 1., 0., + 0., 0., 0., 1.}; + + memcpy(U, Id, 16 * sizeof(double)); + + B[0] = A[0]; B[1] = A[5]; B[2] = A[10]; B[3] = A[15]; + memcpy(D, B, 4 * sizeof(double)); + memset(Z, 0, 4 * sizeof(double)); + + for(int iter = 0; iter < 50; iter++) { + double sum = fabs(A[1]) + fabs(A[2]) + fabs(A[3]) + fabs(A[6]) + fabs(A[7]) + fabs(A[11]); + + if (sum == 0.0) + return true; + + double tresh = (iter < 3) ? 0.2 * sum / 16. : 0.0; + for(int i = 0; i < 3; i++) { + double * pAij = A + 5 * i + 1; + for(int j = i + 1 ; j < 4; j++) { + double Aij = *pAij; + double eps_machine = 100.0 * fabs(Aij); + + if ( iter > 3 && fabs(D[i]) + eps_machine == fabs(D[i]) && fabs(D[j]) + eps_machine == fabs(D[j]) ) + *pAij = 0.0; + else if (fabs(Aij) > tresh) { + double hh = D[j] - D[i], t; + if (fabs(hh) + eps_machine == fabs(hh)) + t = Aij / hh; + else { + double theta = 0.5 * hh / Aij; + t = 1.0 / (fabs(theta) + sqrt(1.0 + theta * theta)); + if (theta < 0.0) t = -t; + } + + hh = t * Aij; + Z[i] -= hh; + Z[j] += hh; + D[i] -= hh; + D[j] += hh; + *pAij = 0.0; + + double c = 1.0 / sqrt(1 + t * t); + double s = t * c; + double tau = s / (1.0 + c); + for(int k = 0; k <= i - 1; k++) { + double g = A[k * 4 + i], h = A[k * 4 + j]; + A[k * 4 + i] = g - s * (h + g * tau); + A[k * 4 + j] = h + s * (g - h * tau); + } + for(int k = i + 1; k <= j - 1; k++) { + double g = A[i * 4 + k], h = A[k * 4 + j]; + A[i * 4 + k] = g - s * (h + g * tau); + A[k * 4 + j] = h + s * (g - h * tau); + } + for(int k = j + 1; k < 4; k++) { + double g = A[i * 4 + k], h = A[j * 4 + k]; + A[i * 4 + k] = g - s * (h + g * tau); + A[j * 4 + k] = h + s * (g - h * tau); + } + for(int k = 0; k < 4; k++) { + double g = U[k * 4 + i], h = U[k * 4 + j]; + U[k * 4 + i] = g - s * (h + g * tau); + U[k * 4 + j] = h + s * (g - h * tau); + } + } + pAij++; + } + } + + for(int i = 0; i < 4; i++) B[i] += Z[i]; + memcpy(D, B, 4 * sizeof(double)); + memset(Z, 0, 4 * sizeof(double)); + } + + return false; +} + + +std::pair self_adjoint_evd(rmatrix4x4 A) { + rmatrix4x4 rvectors{}; + rdiagonal4x4 rvalues{}; + if (!jacobi_4x4(A.data(), rvectors.data(), rvalues.data())) { + throw std::runtime_error{"bad luck..."}; + } + return {rvectors, rvalues}; +} +} diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/g.h b/mlir/lib/Dialect/MQTOpt/Transforms/g.h new file mode 100644 index 0000000000..d2927e943d --- /dev/null +++ b/mlir/lib/Dialect/MQTOpt/Transforms/g.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Helpers.h" +#include "eigen3/Eigen/Eigen" + +namespace mqt::ir::opt { + +auto self_adjoint_evd(rmatrix4x4 A) { + Eigen::Matrix4d a; + Eigen::SelfAdjointEigenSolver s; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + a(j, i) = A[j * 4 + i]; + } + } + std::cerr << "=EigIN==\n" << a << "\n========\n" << std::endl; + s.computeDirect(a); + auto vecs = s.eigenvectors(); + auto vals = s.eigenvalues(); + rmatrix4x4 rvecs; + rdiagonal4x4 rvals; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + rvecs[j * 4 + i] = vecs(j, i); + } + rvals[i] = vals(i); + } + std::cerr << "=Eigen==\n" << vecs << "\n========\n" << std::endl; + std::cerr << "=Eigen==\n" << vals << "\n========\n" << std::endl; + return std::make_pair(rvecs, rvals); +} +} // namespace mqt::ir::opt From 96b39ad9b86592f828c74289c2f8871eef6179f7 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 27 Oct 2025 12:36:28 +0100 Subject: [PATCH 023/237] work on applySeries() --- .../Transforms/GateDecompositionPattern.cpp | 68 +++++++++++++++++-- 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 27c4d43063..a0cb4629fb 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -43,7 +43,7 @@ struct GateDecompositionPattern final auto series = getTwoQubitSeries(op); llvm::errs() << "SERIES SIZE: " << series.size() << '\n'; - if (series.size() <= 3) { + if (series.size() < 3) { return mlir::failure(); } @@ -185,37 +185,91 @@ struct GateDecompositionPattern final llvm::SmallVector& series, const TwoQubitGateSequence& sequence) { auto location = series[0]->getLoc(); - auto inQubits = series[0].getAllInQubits(); + std::vector inQubits(2); + auto&& firstInQubist = series[0].getAllInQubits(); + if (firstInQubist.size() == 2) { + inQubits[0] = firstInQubist[0]; + inQubits[1] = firstInQubist[1]; + } else { + // TODO + } if (sequence.globalPhase != 0.0) { createOneParameterGate(rewriter, location, sequence.globalPhase, {}); } std::cerr << "GATE SEQUENCE!: " << std::flush; + UnitaryInterface lastGate = series[0]; for (auto&& gate : sequence.gates) { if (gate.type == qc::X) { mlir::SmallVector inCtrlQubits; if (gate.qubit_id.size() > 1) { inCtrlQubits.push_back(inQubits[gate.qubit_id[1]]); } - createGate(rewriter, location, {inQubits[0]}, inCtrlQubits, - gate.parameter); + lastGate = createGate(rewriter, location, {inQubits[0]}, + inCtrlQubits, gate.parameter); + if (gate.qubit_id.size() == 2) { + inQubits = lastGate.getAllOutQubits(); + } else if (gate.qubit_id.size() == 1) { + inQubits[gate.qubit_id[0]] = + lastGate.getAllOutQubits()[0]; + } else { + throw std::logic_error{"Invalid number of qubit IDs!"}; + } } if (gate.type == qc::RX) { mlir::SmallVector qubits; for (auto&& x : gate.qubit_id) { qubits.push_back(inQubits[x]); } - createGate(rewriter, location, qubits, {}, gate.parameter); + lastGate = + createGate(rewriter, location, qubits, {}, gate.parameter); + if (gate.qubit_id.size() == 2) { + inQubits = lastGate.getAllOutQubits(); + } else if (gate.qubit_id.size() == 1) { + inQubits[gate.qubit_id[0]] = + lastGate.getAllOutQubits()[0]; + } else { + throw std::logic_error{"Invalid number of qubit IDs!"}; + } } if (gate.type == qc::RY) { + mlir::SmallVector qubits; + for (auto&& x : gate.qubit_id) { + qubits.push_back(inQubits[x]); + } + lastGate = + createGate(rewriter, location, qubits, {}, gate.parameter); + if (gate.qubit_id.size() == 2) { + inQubits = lastGate.getAllOutQubits(); + } else if (gate.qubit_id.size() == 1) { + inQubits[gate.qubit_id[0]] = + lastGate.getAllOutQubits()[0]; + } else { + throw std::logic_error{"Invalid number of qubit IDs!"}; + } } if (gate.type == qc::RZ) { + mlir::SmallVector qubits; + for (auto&& x : gate.qubit_id) { + qubits.push_back(inQubits[x]); + } + lastGate = + createGate(rewriter, location, qubits, {}, gate.parameter); + if (gate.qubit_id.size() == 2) { + inQubits = lastGate.getAllOutQubits(); + } else if (gate.qubit_id.size() == 1) { + inQubits[gate.qubit_id[0]] = + lastGate.getAllOutQubits()[0]; + } else { + throw std::logic_error{"Invalid number of qubit IDs!"}; + } } } - for (auto&& op : series) { - rewriter.replaceOp(op, op.getAllInQubits()); + rewriter.replaceAllOpUsesWith(series.back(), inQubits); + for (auto&& op : llvm::reverse(series)) { + rewriter.eraseOp(op); } } From 394756631b5c2c6e4145b6d770234eb70adff268 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 27 Oct 2025 12:37:08 +0100 Subject: [PATCH 024/237] Revert "use laughing-umbrella implementation for decompose_two_qubit_product_gate" This reverts commit 461319afefb439ace9b91ed31a88618f03165640. --- .../Transforms/GateDecompositionPattern.cpp | 93 ++++++++----------- 1 file changed, 41 insertions(+), 52 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index a0cb4629fb..3464f599a5 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -356,65 +356,54 @@ struct GateDecompositionPattern final static std::tuple decompose_two_qubit_product_gate(matrix4x4 special_unitary) { - using helpers::determinant; - using helpers::dot; - using helpers::kroneckerProduct; - using helpers::transpose_conjugate; - helpers::print(special_unitary, "SPECIAL_UNITARY"); - - auto* it = llvm::max_element(special_unitary, [](auto&& a, auto&& b) { - return std::abs(a) < std::abs(b); - }); - if (it == special_unitary.end()) { + using helpers::dot; + using helpers::kroneckerProduct; + using helpers::transpose_conjugate; + using helpers::determinant; + helpers::print(special_unitary, "SPECIAL_UNITARY"); + // first quadrant + matrix2x2 r = {special_unitary[0 * 4 + 0], special_unitary[0 * 4 + 1], + special_unitary[1 * 4 + 0], special_unitary[1 * 4 + 1]}; + auto det_r = determinant(r); + if (std::abs(det_r) < 0.1) { + // third quadrant + r = {special_unitary[2 * 4 + 0], special_unitary[2 * 4 + 1], + special_unitary[3 * 4 + 0], special_unitary[3 * 4 + 1]}; + det_r = determinant(r); + } + if (std::abs(det_r) < 0.1) { throw std::runtime_error{ - "No maximal element in decompose_two_qubit_product_gate!"}; + "decompose_two_qubit_product_gate: unable to decompose: det_r < 0.1"}; } - auto pos = std::distance(special_unitary.begin(), it); - int i = pos % 4; - int j = pos / 4; - - auto u1_set = [](int i) { - if (i % 2 == 0) { - return std::make_pair(0, 2); - } - return std::make_pair(1, 3); - }; - - auto u2_set = [](int i) { - if (i < 2) { - return std::make_pair(0, 1); - } - return std::make_pair(2, 3); - }; - - auto to_su = [](auto&& u) { - return helpers::multiply(std::pow(qfp(determinant(u)), -0.25), u); - }; + llvm::transform(r, r.begin(), + [&](auto&& x) { return x / std::sqrt(det_r); }); + // transpose with complex conjugate of each element + matrix2x2 r_t_conj = transpose_conjugate(r); + + auto temp = kroneckerProduct(identityGate, r_t_conj); + temp = dot(special_unitary, temp); + + // [[a, b, c, d], + // [e, f, g, h], => [[a, c], + // [i, j, k, l], [i, k]] + // [m, n, o, p]] + matrix2x2 l = {temp[0 * 4 + 0], temp[0 * 4 + 2], temp[2 * 4 + 0], + temp[2 * 4 + 2]}; + auto det_l = determinant(l); + if (std::abs(det_l) < 0.9) { + throw std::runtime_error{ + "decompose_two_qubit_product_gate: unable to decompose: detL < 0.9"}; + } + llvm::transform(l, l.begin(), + [&](auto&& x) { return x / std::sqrt(det_l); }); + auto phase = std::arg(det_l) / 2.; - matrix2x2 u1; - u1[0 * 2 + 0] = special_unitary[u1_set(i).first * 4 + u1_set(j).first]; - u1[0 * 2 + 1] = special_unitary[u1_set(i).first * 4 + u1_set(j).second]; - u1[1 * 2 + 0] = special_unitary[u1_set(i).second * 4 + u1_set(j).first]; - u1[1 * 2 + 1] = special_unitary[u1_set(i).second * 4 + u1_set(j).second]; - matrix2x2 u2; - u1[0 * 2 + 0] = special_unitary[u2_set(i).first * 4 + u2_set(j).first]; - u1[0 * 2 + 1] = special_unitary[u2_set(i).first * 4 + u2_set(j).second]; - u1[1 * 2 + 0] = special_unitary[u2_set(i).second * 4 + u2_set(j).first]; - u1[1 * 2 + 1] = special_unitary[u2_set(i).second * 4 + u2_set(j).second]; - - u1 = to_su(u1); - u2 = to_su(u2); - - std::cerr << i << ',' << j << std::endl; - auto phase = special_unitary[i * 4 + j] / - (u1[(i / 2) * 2 + (j / 2)] * u2[(i % 2) * 2 + (j % 2)]); - - return {u1, u2, phase.real()}; // TODO: return only real part? + return {l, r, phase}; } static matrix4x4 magic_basis_transform(const matrix4x4& unitary, MagicBasisTransform direction) { - using helpers::dot; + using helpers::dot; constexpr matrix4x4 B_NON_NORMALIZED = { C_ONE, IM, C_ZERO, C_ZERO, C_ZERO, C_ZERO, IM, C_ONE, C_ZERO, C_ZERO, IM, C_M_ONE, C_ONE, M_IM, C_ZERO, C_ZERO, From ab188ae60dea26f932241d489c60051c0d984d39 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 27 Oct 2025 16:43:54 +0100 Subject: [PATCH 025/237] fix transpose_conjugate --- .../lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp | 2 +- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 3464f599a5..703b9acdd7 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -696,7 +696,7 @@ struct GateDecompositionPattern final auto tmp = remEuclid(x, qc::PI_2); return std::min(tmp, qc::PI_2 - tmp); }); - std::array order{0, 1, 2}; + std::array order{2, 1, 0}; // TODO: needs to be adjusted depending on eigenvector order in eigen decomposition algorithm? llvm::stable_sort(order, [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); helpers::print(order, "ORDER (1)"); diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index c323477eff..35d6f02d49 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -463,8 +463,7 @@ template auto conjugate(Container matrix) { template inline auto transpose_conjugate(Container&& matrix) { auto result = transpose(matrix); - result = conjugate(matrix); - return result; + return conjugate(result); } template From 410fadd7c142c873ff70a8eb862268472b67970c Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 30 Oct 2025 13:01:10 +0100 Subject: [PATCH 026/237] add test --- .../MQTOpt/Transforms/gate-decomposition.mlir | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir new file mode 100644 index 0000000000..b1a37228c5 --- /dev/null +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -0,0 +1,92 @@ +// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +// Copyright (c) 2025 Munich Quantum Software Company GmbH +// All rights reserved. +// +// SPDX-License-Identifier: MIT +// +// Licensed under the MIT License + +// RUN: quantum-opt %s -split-input-file --gate-decomposition | FileCheck %s + +// ----- +// This test checks if two-qubit consecutive controlled self-inverses are canceled correctly. + +module { + // CHECK-LABEL: func.func @testEvenNegationSeries + func.func @testEvenNegationSeries() { + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit + + // CHECK-NOT: %[[ANY:.*]] = mqtopt.x + + // CHECK: mqtopt.deallocQubit %[[Q0_0]] + // CHECK: mqtopt.deallocQubit %[[Q1_0]] + + %q0_0 = mqtopt.allocQubit + %q1_0 = mqtopt.allocQubit + + %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_2, %q1_2 = mqtopt.x() %q0_1 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_3, %q1_3 = mqtopt.x() %q0_2 ctrl %q1_2: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_4, %q1_4 = mqtopt.x() %q0_3 ctrl %q1_3: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_5, %q1_5 = mqtopt.x() %q0_4 ctrl %q1_4: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_6, %q1_6 = mqtopt.x() %q0_5 ctrl %q1_5: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_7, %q1_7 = mqtopt.x() %q0_6 ctrl %q1_6: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_8, %q1_8 = mqtopt.x() %q0_7 ctrl %q1_7: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_9, %q1_9 = mqtopt.x() %q0_8 ctrl %q1_8: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_10, %q1_10 = mqtopt.x() %q0_9 ctrl %q1_9: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_11, %q1_11 = mqtopt.x() %q0_10 ctrl %q1_10: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_12, %q1_12 = mqtopt.x() %q0_11 ctrl %q1_11: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_13, %q1_13 = mqtopt.x() %q0_12 ctrl %q1_12: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_14, %q1_14 = mqtopt.x() %q0_13 ctrl %q1_13: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_15, %q1_15 = mqtopt.x() %q0_14 ctrl %q1_14: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_16, %q1_16 = mqtopt.x() %q0_15 ctrl %q1_15: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_17, %q1_17 = mqtopt.x() %q0_16 ctrl %q1_16: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_18, %q1_18 = mqtopt.x() %q0_17 ctrl %q1_17: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_19, %q1_19 = mqtopt.x() %q0_18 ctrl %q1_18: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_20, %q1_20 = mqtopt.x() %q0_19 ctrl %q1_19: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_21, %q1_21 = mqtopt.x() %q0_20 ctrl %q1_20: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_22, %q1_22 = mqtopt.x() %q0_21 ctrl %q1_21: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_23, %q1_23 = mqtopt.x() %q0_22 ctrl %q1_22: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_24, %q1_24 = mqtopt.x() %q0_23 ctrl %q1_23: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_25, %q1_25 = mqtopt.x() %q0_24 ctrl %q1_24: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_27, %q1_27 = mqtopt.x() %q0_25 ctrl %q1_25: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_28, %q1_28 = mqtopt.x() %q0_27 ctrl %q1_27: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_29, %q1_29 = mqtopt.x() %q0_28 ctrl %q1_28: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_30, %q1_30 = mqtopt.x() %q0_29 ctrl %q1_29: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_31, %q1_31 = mqtopt.x() %q0_30 ctrl %q1_30: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_32, %q1_32 = mqtopt.x() %q0_31 ctrl %q1_31: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_33, %q1_33 = mqtopt.x() %q0_32 ctrl %q1_32: !mqtopt.Qubit ctrl !mqtopt.Qubit + + mqtopt.deallocQubit %q0_33 + mqtopt.deallocQubit %q1_33 + + return + } +} + +module { + // CHECK-LABEL: func.func @testOddNegationSeries + func.func @testOddNegationSeries() { + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit + + // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x %[[Q0_0]] ctrl %[[Q1_0]] + // CHECK-NOT: %[[ANY:.*]] = mqtopt.x + + // CHECK: mqtopt.deallocQubit %[[Q0_0]] + // CHECK: mqtopt.deallocQubit %[[Q1_0]] + + %q0_0 = mqtopt.allocQubit + %q1_0 = mqtopt.allocQubit + + %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_2, %q1_2 = mqtopt.x() %q0_1 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_3, %q1_3 = mqtopt.x() %q0_2 ctrl %q1_2: !mqtopt.Qubit ctrl !mqtopt.Qubit + + mqtopt.deallocQubit %q0_3 + mqtopt.deallocQubit %q1_3 + + return + } +} From 05885a10b3dcc841bbcf014f2acad3476a83af64 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 30 Oct 2025 20:43:13 +0100 Subject: [PATCH 027/237] actually kind of working snapshot? --- .../Transforms/GateDecompositionPattern.cpp | 479 +++++++++++------- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 24 +- .../MQTOpt/Transforms/gate-decomposition.mlir | 84 ++- 3 files changed, 391 insertions(+), 196 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 703b9acdd7..21060ae35b 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -40,19 +40,26 @@ struct GateDecompositionPattern final mlir::LogicalResult matchAndRewrite(UnitaryInterface op, mlir::PatternRewriter& rewriter) const override { - auto series = getTwoQubitSeries(op); - llvm::errs() << "SERIES SIZE: " << series.size() << '\n'; + auto series = TwoQubitSeries::getTwoQubitSeries(op); + llvm::errs() << "SERIES SIZE: " << series.gates.size() << '\n'; - if (series.size() < 3) { + if (series.gates.size() < 3) { + // too short + return mlir::failure(); + } + if (llvm::is_contained(series.inQubits, mlir::Value{}) || + llvm::is_contained(series.outQubits, mlir::Value{})) { + // only a single-qubit series return mlir::failure(); } matrix4x4 unitaryMatrix = helpers::kroneckerProduct(identityGate, identityGate); - for (auto&& gate : series) { - auto gateMatrix = getTwoQubitMatrix({.type = helpers::getQcType(gate), - .parameter = {/*TODO*/}, - .qubit_id = {0, 1}}); + for (auto&& gate : series.gates) { + auto gateMatrix = + getTwoQubitMatrix({.type = helpers::getQcType(gate.op), + .parameter = helpers::getParameters(gate.op), + .qubit_id = gate.qubit_id}); unitaryMatrix = helpers::multiply(unitaryMatrix, gateMatrix); } @@ -63,71 +70,132 @@ struct GateDecompositionPattern final llvm::errs() << "NO SEQUENCE GENERATED!\n"; return mlir::failure(); } + if (sequence->gates.size() >= series.gates.size()) { + // TODO: add more sophisticated metric to determine complexity of + // series/sequence + llvm::errs() << "SEQUENCE LONGER THAN INPUT!\n"; + return mlir::failure(); + } applySeries(rewriter, series, *sequence); return mlir::success(); } - [[nodiscard]] static llvm::SmallVector - getTwoQubitSeries(UnitaryInterface op) { - llvm::SmallVector qubits(2); - llvm::SmallVector result; + struct TwoQubitSeries { + std::array inQubits; + std::array outQubits; - if (helpers::isSingleQubitOperation(op)) { - qubits = {op->getResult(0), mlir::Value{}}; - } else if (helpers::isTwoQubitOperation(op)) { - qubits = op->getResults(); - } else { - return result; - } - result.push_back(op); + struct Gate { + UnitaryInterface op; + llvm::SmallVector qubit_id; + }; + llvm::SmallVector gates; + + [[nodiscard]] static TwoQubitSeries getTwoQubitSeries(UnitaryInterface op) { + TwoQubitSeries result(op); + + auto findNextInSeries = [&](mlir::Operation* user) { + auto userUnitary = mlir::dyn_cast(user); + if (!userUnitary) { + return false; + } - auto findNextInSeries = [&](mlir::Operation* user) { - auto userUnitary = mlir::dyn_cast(user); - if (!userUnitary) { + if (helpers::isSingleQubitOperation(userUnitary)) { + result.appendSingleQubitGate(userUnitary); + return true; + } + if (helpers::isTwoQubitOperation(userUnitary)) { + result.appendTwoQubitGate(userUnitary); + return true; + } return false; - } + }; - if (helpers::isSingleQubitOperation(userUnitary)) { - auto&& operand = userUnitary->getOperand(0); - auto* it = llvm::find(qubits, operand); - if (it == qubits.end()) { - throw std::logic_error{"Operand of single-qubit op and user of " - "qubit is not in qubits"}; + bool isFirstQubitOngoing = result.outQubits[0] != mlir::Value{}; + bool isSecondQubitOngoing = result.outQubits[1] != mlir::Value{}; + while (isFirstQubitOngoing || isSecondQubitOngoing) { + if (result.outQubits[0]) { + assert(result.outQubits[0].hasOneUse()); + isFirstQubitOngoing = + findNextInSeries(*result.outQubits[0].getUsers().begin()); } - *it = userUnitary->getResult(0); + if (result.outQubits[1]) { + assert(result.outQubits[1].hasOneUse()); + isSecondQubitOngoing = + findNextInSeries(*result.outQubits[1].getUsers().begin()); + } + } + return result; + } - result.push_back(userUnitary); - return true; + private: + TwoQubitSeries(UnitaryInterface initialOperation) { + auto&& in = initialOperation.getAllInQubits(); + auto&& out = initialOperation->getResults(); + if (helpers::isSingleQubitOperation(initialOperation)) { + inQubits = {in[0], mlir::Value{}}; + outQubits = {out[0], mlir::Value{}}; + gates.push_back({initialOperation, {0}}); + } else if (helpers::isTwoQubitOperation(initialOperation)) { + inQubits = {in[0], in[1]}; + outQubits = {out[0], out[1]}; + gates.push_back({initialOperation, {0, 1}}); } - if (helpers::isTwoQubitOperation(userUnitary)) { - auto&& firstOperand = userUnitary->getOperand(0); - auto&& secondOperand = userUnitary->getOperand(1); - auto* firstQubitIt = llvm::find(qubits, firstOperand); - auto* secondQubitIt = llvm::find(qubits, secondOperand); - if (firstQubitIt == qubits.end() || secondQubitIt == qubits.end()) { - return false; - } - *firstQubitIt = userUnitary->getResult(0); - *secondQubitIt = userUnitary->getResult(1); + } - result.push_back(userUnitary); - return true; + /** + * @return true if series continues, otherwise false + */ + bool appendSingleQubitGate(UnitaryInterface nextGate) { + auto operand = nextGate.getAllInQubits()[0]; + auto* it = llvm::find(outQubits, operand); + if (it == outQubits.end()) { + throw std::logic_error{"Operand of single-qubit op and user of " + "qubit is not in current outQubits"}; } - return false; - }; + std::uint8_t qubitId = std::distance(outQubits.begin(), it); + *it = nextGate->getResult(0); - while (true) { - assert(qubits[0].hasOneUse()); - assert(qubits[1].hasOneUse()); - bool isSeriesOngoing = findNextInSeries(*qubits[0].getUsers().begin()) || - findNextInSeries(*qubits[1].getUsers().begin()); - if (!isSeriesOngoing) { - return result; + gates.push_back({nextGate, {qubitId}}); + return true; + } + + /** + * @return true if series continues, otherwise false + */ + bool appendTwoQubitGate(UnitaryInterface nextGate) { + auto opInQubits = nextGate.getAllInQubits(); + auto&& firstOperand = opInQubits[0]; + auto&& secondOperand = opInQubits[1]; + auto* firstQubitIt = llvm::find(outQubits, firstOperand); + auto* secondQubitIt = llvm::find(outQubits, secondOperand); + if (firstQubitIt == outQubits.end() || secondQubitIt == outQubits.end()) { + // another qubit is involved, series is finished (except there only + // has been one qubit so far) + auto* it = llvm::find(outQubits, mlir::Value{}); + if (it == outQubits.end()) { + return false; + } + auto it2 = llvm::find(opInQubits, firstQubitIt != outQubits.end() + ? *firstQubitIt + : *secondQubitIt); + inQubits[std::distance(outQubits.begin(), it)] = + opInQubits[1 - std::distance(opInQubits.begin(), it2)]; + firstQubitIt = (firstQubitIt != outQubits.end()) ? firstQubitIt : it; + secondQubitIt = (secondQubitIt != outQubits.end()) ? secondQubitIt : it; } + *firstQubitIt = nextGate->getResult(0); + std::uint8_t firstQubitId = + std::distance(outQubits.begin(), firstQubitIt); + *secondQubitIt = nextGate->getResult(1); + std::uint8_t secondQubitId = + std::distance(outQubits.begin(), secondQubitIt); + + gates.push_back({nextGate, {firstQubitId, secondQubitId}}); + return true; } - } + }; /** * @brief Creates a new rotation gate with no controls. @@ -155,8 +223,8 @@ struct GateDecompositionPattern final static OpType createGate(mlir::PatternRewriter& rewriter, mlir::Location location, mlir::ValueRange inQubits, mlir::ValueRange ctrlQubits, - std::vector parameters) { - mlir::SmallVector parameterValues; + llvm::SmallVector parameters) { + mlir::SmallVector parameterValues; for (auto&& parameter : parameters) { auto parameterValue = rewriter.create( location, rewriter.getF64Type(), rewriter.getF64FloatAttr(parameter)); @@ -172,8 +240,8 @@ struct GateDecompositionPattern final struct QubitGateSequence { struct Gate { qc::OpType type{qc::I}; - std::vector parameter; - std::vector qubit_id = {0}; + llvm::SmallVector parameter; + llvm::SmallVector qubit_id = {0}; }; std::vector gates; fp globalPhase; @@ -182,94 +250,77 @@ struct GateDecompositionPattern final using TwoQubitGateSequence = QubitGateSequence; static void applySeries(mlir::PatternRewriter& rewriter, - llvm::SmallVector& series, + TwoQubitSeries& series, const TwoQubitGateSequence& sequence) { - auto location = series[0]->getLoc(); - std::vector inQubits(2); - auto&& firstInQubist = series[0].getAllInQubits(); - if (firstInQubist.size() == 2) { - inQubits[0] = firstInQubist[0]; - inQubits[1] = firstInQubist[1]; - } else { - // TODO - } + auto location = series.gates.back().op->getLoc(); if (sequence.globalPhase != 0.0) { createOneParameterGate(rewriter, location, sequence.globalPhase, {}); } + auto inQubits = series.inQubits; + auto updateInQubits = + [&inQubits](const TwoQubitGateSequence::Gate& gateDescription, + auto&& newGate) { + auto results = newGate.getAllOutQubits(); + if (gateDescription.qubit_id.size() == 2) { + inQubits[gateDescription.qubit_id[0]] = results[0]; + inQubits[gateDescription.qubit_id[1]] = results[1]; + } else if (gateDescription.qubit_id.size() == 1) { + inQubits[gateDescription.qubit_id[0]] = results[0]; + } else { + throw std::logic_error{"Invalid number of qubit IDs!"}; + } + }; + + std::cerr << "SERIES: "; + for (auto&& gate : series.gates) { + std::cerr << gate.op->getName().stripDialect().str() << ", "; + } + std::cerr << '\n'; std::cerr << "GATE SEQUENCE!: " << std::flush; - UnitaryInterface lastGate = series[0]; for (auto&& gate : sequence.gates) { + std::cerr << qc::toString(gate.type) << ", "; if (gate.type == qc::X) { mlir::SmallVector inCtrlQubits; if (gate.qubit_id.size() > 1) { inCtrlQubits.push_back(inQubits[gate.qubit_id[1]]); } - lastGate = createGate(rewriter, location, {inQubits[0]}, - inCtrlQubits, gate.parameter); - if (gate.qubit_id.size() == 2) { - inQubits = lastGate.getAllOutQubits(); - } else if (gate.qubit_id.size() == 1) { - inQubits[gate.qubit_id[0]] = - lastGate.getAllOutQubits()[0]; - } else { - throw std::logic_error{"Invalid number of qubit IDs!"}; - } - } - if (gate.type == qc::RX) { + auto newGate = createGate(rewriter, location, {inQubits[0]}, + inCtrlQubits, gate.parameter); + updateInQubits(gate, newGate); + } else if (gate.type == qc::RX) { mlir::SmallVector qubits; for (auto&& x : gate.qubit_id) { qubits.push_back(inQubits[x]); } - lastGate = + auto newGate = createGate(rewriter, location, qubits, {}, gate.parameter); - if (gate.qubit_id.size() == 2) { - inQubits = lastGate.getAllOutQubits(); - } else if (gate.qubit_id.size() == 1) { - inQubits[gate.qubit_id[0]] = - lastGate.getAllOutQubits()[0]; - } else { - throw std::logic_error{"Invalid number of qubit IDs!"}; - } - } - if (gate.type == qc::RY) { + updateInQubits(gate, newGate); + } else if (gate.type == qc::RY) { mlir::SmallVector qubits; for (auto&& x : gate.qubit_id) { qubits.push_back(inQubits[x]); } - lastGate = + auto newGate = createGate(rewriter, location, qubits, {}, gate.parameter); - if (gate.qubit_id.size() == 2) { - inQubits = lastGate.getAllOutQubits(); - } else if (gate.qubit_id.size() == 1) { - inQubits[gate.qubit_id[0]] = - lastGate.getAllOutQubits()[0]; - } else { - throw std::logic_error{"Invalid number of qubit IDs!"}; - } - } - if (gate.type == qc::RZ) { + updateInQubits(gate, newGate); + } else if (gate.type == qc::RZ) { mlir::SmallVector qubits; for (auto&& x : gate.qubit_id) { qubits.push_back(inQubits[x]); } - lastGate = + auto newGate = createGate(rewriter, location, qubits, {}, gate.parameter); - if (gate.qubit_id.size() == 2) { - inQubits = lastGate.getAllOutQubits(); - } else if (gate.qubit_id.size() == 1) { - inQubits[gate.qubit_id[0]] = - lastGate.getAllOutQubits()[0]; - } else { - throw std::logic_error{"Invalid number of qubit IDs!"}; - } + updateInQubits(gate, newGate); + } else { + throw std::runtime_error{"Unknown gate type!"}; } } - rewriter.replaceAllOpUsesWith(series.back(), inQubits); - for (auto&& op : llvm::reverse(series)) { - rewriter.eraseOp(op); + rewriter.replaceAllUsesWith(series.outQubits, inQubits); + for (auto&& gate : llvm::reverse(series.gates)) { + rewriter.eraseOp(gate.op); } } @@ -344,11 +395,13 @@ struct GateDecompositionPattern final auto [U, S] = self_adjoint_evd(A); // TODO: not in original code - if (helpers::determinant(U) + 1.0 < std::numeric_limits::epsilon()) { - // if determinant of eigenvector matrix is -1.0, multiply first eigenvector by -1.0 - for (int i = 0; i < 4; ++i) { - U[i * 4 + 0] *= -1.0; - } + if (helpers::determinant(U) + 1.0 < 1e-5) { + std::cerr << "CORRECTION!\n"; + // if determinant of eigenvector matrix is -1.0, multiply first + // eigenvector by -1.0 + for (int i = 0; i < 4; ++i) { + U[i * 4 + 0] *= -1.0; + } } return std::make_pair(U, S); @@ -356,11 +409,11 @@ struct GateDecompositionPattern final static std::tuple decompose_two_qubit_product_gate(matrix4x4 special_unitary) { - using helpers::dot; - using helpers::kroneckerProduct; - using helpers::transpose_conjugate; - using helpers::determinant; - helpers::print(special_unitary, "SPECIAL_UNITARY"); + using helpers::determinant; + using helpers::dot; + using helpers::kroneckerProduct; + using helpers::transpose_conjugate; + helpers::print(special_unitary, "SPECIAL_UNITARY"); // first quadrant matrix2x2 r = {special_unitary[0 * 4 + 0], special_unitary[0 * 4 + 1], special_unitary[1 * 4 + 0], special_unitary[1 * 4 + 1]}; @@ -371,17 +424,21 @@ struct GateDecompositionPattern final special_unitary[3 * 4 + 0], special_unitary[3 * 4 + 1]}; det_r = determinant(r); } + std::cerr << "DET_R: " << det_r << '\n'; if (std::abs(det_r) < 0.1) { throw std::runtime_error{ "decompose_two_qubit_product_gate: unable to decompose: det_r < 0.1"}; } llvm::transform(r, r.begin(), [&](auto&& x) { return x / std::sqrt(det_r); }); + helpers::print(r, "R"); // transpose with complex conjugate of each element matrix2x2 r_t_conj = transpose_conjugate(r); auto temp = kroneckerProduct(identityGate, r_t_conj); + helpers::print(temp, "TEMP (decompose_two_qubit_product_gate, 1)"); temp = dot(special_unitary, temp); + helpers::print(temp, "TEMP (decompose_two_qubit_product_gate, 2)"); // [[a, b, c, d], // [e, f, g, h], => [[a, c], @@ -403,7 +460,7 @@ struct GateDecompositionPattern final static matrix4x4 magic_basis_transform(const matrix4x4& unitary, MagicBasisTransform direction) { - using helpers::dot; + using helpers::dot; constexpr matrix4x4 B_NON_NORMALIZED = { C_ONE, IM, C_ZERO, C_ZERO, C_ZERO, C_ZERO, IM, C_ONE, C_ZERO, C_ZERO, IM, C_M_ONE, C_ONE, M_IM, C_ZERO, C_ZERO, @@ -456,6 +513,38 @@ struct GateDecompositionPattern final return {std::exp(-ilam2), C_ZERO, C_ZERO, std::exp(ilam2)}; } + static matrix4x4 rxxMatrix(const fp theta) { + const auto cosTheta = std::cos(theta / 2.); + const auto sinTheta = std::sin(theta / 2.); + + return {{cosTheta, + C_ZERO, + C_ZERO, + {0., -sinTheta}, + C_ZERO, + cosTheta, + {0., -sinTheta}, + C_ZERO, + C_ZERO, + {0., -sinTheta}, + cosTheta, + C_ZERO, + {0., -sinTheta}, + C_ZERO, + C_ZERO, + cosTheta}}; + } + + static matrix4x4 rzzMatrix(const fp theta) { + const auto cosTheta = std::cos(theta / 2.); + const auto sinTheta = std::sin(theta / 2.); + + return {qfp{cosTheta, -sinTheta}, C_ZERO, C_ZERO, C_ZERO, C_ZERO, + {cosTheta, sinTheta}, C_ZERO, C_ZERO, C_ZERO, C_ZERO, + {cosTheta, sinTheta}, C_ZERO, C_ZERO, C_ZERO, C_ZERO, + {cosTheta, -sinTheta}}; + } + static std::array angles_from_unitary(const matrix2x2& matrix, EulerBasis basis) { if (basis == EulerBasis::XYX) { @@ -524,7 +613,7 @@ struct GateDecompositionPattern final return {qfp{0.5, 0.5}, qfp{0.5, -0.5}, qfp{0.5, -0.5}, qfp{0.5, 0.5}}; } if (gate.type == qc::RZ) { - return rz_matrix(gate.parameter.at(0)); + return rz_matrix(gate.parameter[0]); } if (gate.type == qc::X) { return {0, 1, 1, 0}; @@ -546,19 +635,28 @@ struct GateDecompositionPattern final if (gate.qubit_id[0] == 1) { return kroneckerProduct(getSingleQubitMatrix(gate), identityGate); } - throw std::logic_error{"Invalid qubit ID in compute_unitary"}; + throw std::logic_error{"Invalid qubit ID in getTwoQubitMatrix"}; } if (gate.qubit_id.size() == 2) { if (gate.type == qc::X) { // controlled X (CX) - if (gate.qubit_id == std::vector{0, 1}) { + if (gate.qubit_id == llvm::SmallVector{0, 1}) { return {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0}; } - if (gate.qubit_id == std::vector{1, 0}) { + if (gate.qubit_id == llvm::SmallVector{1, 0}) { return {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}; } } - throw std::invalid_argument{"unsupported gate type for two qubit matrix"}; + if (gate.type == qc::RZ) { + // TODO: check qubit order + return rzzMatrix(gate.parameter[0]); + } + if (gate.type == qc::RX) { + // TODO: check qubit order + return rxxMatrix(gate.parameter[0]); + } + throw std::invalid_argument{ + "unsupported gate type for two qubit matrix "}; } throw std::logic_error{"Invalid number of qubit IDs in compute_unitary"}; } @@ -601,27 +699,28 @@ struct GateDecompositionPattern final helpers::print(m2); // arma::Mat U(4, 4); - // for (int i = 0; i < 4; ++i) { - // for (int j = 0; j < 4; ++j) { + // for (int i = 0; i < 4; ++i) { + // for (int j = 0; j < 4; ++j) { // U.at(j, i) = u_p[j * 4 + i]; - // } - // } + // } + // } // auto x = U.st() * U; - // std::cerr << "ARMA\n" << U.t() << "\n\n" << U << "\n\n" << x << std::endl; + // std::cerr << "ARMA\n" << U.t() << "\n\n" << U << "\n\n" << x << + // std::endl; // M2 is a symmetric complex matrix. We need to decompose it as M2 = P D // P^T where P ∈ SO(4), D is diagonal with unit-magnitude elements. // - // We can't use raw `eig` directly because it isn't guaranteed to give us - // real or orthogonal eigenvectors. Instead, since `M2` is + // We can't use raw `eig` directly because it isn't guaranteed to give + // us real or orthogonal eigenvectors. Instead, since `M2` is // complex-symmetric, // M2 = A + iB // for real-symmetric `A` and `B`, and as // M2^+ @ M2 = A^2 + B^2 + i [A, B] = 1 // we must have `A` and `B` commute, and consequently they are // simultaneously diagonalizable. Mixing them together _should_ account - // for any degeneracy problems, but it's not guaranteed, so we repeat it a - // little bit. The fixed seed is to make failures deterministic; the + // for any degeneracy problems, but it's not guaranteed, so we repeat it + // a little bit. The fixed seed is to make failures deterministic; the // value is not important. auto state = std::mt19937{2023}; std::normal_distribution dist; @@ -654,10 +753,8 @@ struct GateDecompositionPattern final [](auto&& x) { return qfp(x, 0.0); }); auto d_inner = diagonal(dot(dot(transpose(p_inner), m2), p_inner)); - llvm::errs() << "===== D_INNER =====\n"; - helpers::print(d_inner); - llvm::errs() << "===== P_INNER =====\n"; - helpers::print(p_inner); + helpers::print(d_inner, "D_INNER"); + helpers::print(p_inner, "P_INNER"); matrix4x4 diag_d{}; // zero initialization diag_d[0 * 4 + 0] = d_inner[0]; diag_d[1 * 4 + 1] = d_inner[1]; @@ -665,8 +762,7 @@ struct GateDecompositionPattern final diag_d[3 * 4 + 3] = d_inner[3]; auto compare = dot(dot(p_inner, diag_d), transpose(p_inner)); - llvm::errs() << "===== COMPARE =====\n"; - helpers::print(compare); + helpers::print(compare, "COMPARE"); found = llvm::all_of_zip(compare, m2, [](auto&& a, auto&& b) { return std::abs(a - b) <= 1.0e-13; }); @@ -696,7 +792,9 @@ struct GateDecompositionPattern final auto tmp = remEuclid(x, qc::PI_2); return std::min(tmp, qc::PI_2 - tmp); }); - std::array order{2, 1, 0}; // TODO: needs to be adjusted depending on eigenvector order in eigen decomposition algorithm? + std::array order{ + 2, 1, 0}; // TODO: needs to be adjusted depending on eigenvector + // order in eigen decomposition algorithm? llvm::stable_sort(order, [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); helpers::print(order, "ORDER (1)"); @@ -950,8 +1048,9 @@ struct GateDecompositionPattern final // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4) \sim // \text{SWAP}^\alpha` // - // (a non-equivalent root of SWAP from the TwoQubitWeylPartialSWAPEquiv - // similar to how :math:`x = (\pm \sqrt(x))^2`) + // (a non-equivalent root of SWAP from the + // TwoQubitWeylPartialSWAPEquiv similar to how :math:`x = (\pm + // \sqrt(x))^2`) // // This gate binds 3 parameters, we make it canonical by setting: // @@ -1158,7 +1257,7 @@ struct GateDecompositionPattern final struct TwoQubitBasisDecomposer { qc::OpType gate; - std::vector gate_params; + llvm::SmallVector gate_params; fp basis_fidelity; EulerBasis euler_basis; std::optional pulse_optimize; @@ -1185,13 +1284,14 @@ struct GateDecompositionPattern final matrix2x2 q2r; public: - static TwoQubitBasisDecomposer - new_inner(qc::OpType gate = qc::X, // CX - const std::vector& gate_params = {}, - matrix4x4 gate_matrix = {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, - 0, 0}, // CX matrix - fp basis_fidelity = 1.0, EulerBasis euler_basis = EulerBasis::ZYZ, - std::optional pulse_optimize = std::nullopt) { + static TwoQubitBasisDecomposer new_inner( + qc::OpType gate = qc::X, // CX + const llvm::SmallVector& gate_params = {}, + // matrix4x4 gate_matrix = {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, + matrix4x4 gate_matrix = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, + 0}, // CX matrix + fp basis_fidelity = 1.0, EulerBasis euler_basis = EulerBasis::ZYZ, + std::optional pulse_optimize = std::nullopt) { using helpers::dot; using helpers::transpose_conjugate; @@ -1296,7 +1396,8 @@ struct GateDecompositionPattern final auto k1rd = transpose_conjugate(basis_decomposer.K1r); auto k2ld = transpose_conjugate(basis_decomposer.K2l); auto k2rd = transpose_conjugate(basis_decomposer.K2r); - // Pre-build the fixed parts of the matrices used in 3-part decomposition + // Pre-build the fixed parts of the matrices used in 3-part + // decomposition auto u0l = dot(k31l, k1ld); auto u0r = dot(k31r, k1rd); auto u1l = dot(dot(k2ld, k32l_k21l), k1ld); @@ -1364,11 +1465,11 @@ struct GateDecompositionPattern final unitaryMatrix, DEFAULT_FIDELITY, std::nullopt); auto traces = this->traces(target_decomposed); auto get_default_nbasis = [&]() { - auto minValue = std::numeric_limits::max(); + auto minValue = std::numeric_limits::min(); auto minIndex = -1; for (std::size_t i = 0; i < traces.size(); ++i) { auto value = trace_to_fid(traces[i]) * std::pow(basis_fidelity, i); - if (value < minValue) { + if (value > minValue) { minIndex = i; minValue = value; } @@ -1392,11 +1493,23 @@ struct GateDecompositionPattern final throw std::logic_error{"Invalid basis to use"}; }; auto decomposition = choose_decomposition(); + std::cerr << "NBasis: " << (int)best_nbasis + << "; basis_fid: " << basis_fidelity + << "; Traces: " << traces[0] << ", " << traces[1] << ", " + << traces[2] << ", " << traces[3]; + std::cerr << "\nDecomposition:\n"; + for (auto x : decomposition) { + helpers::print(x, "", true); + } if (pulse_optimize.value_or(true)) { - return pulse_optimal_chooser(best_nbasis, decomposition, - target_decomposed); + if (auto result = pulse_optimal_chooser(best_nbasis, decomposition, + target_decomposed)) { + return result; + } } - std::vector target_1q_basis_list; + std::vector + target_1q_basis_list; // TODO: simplify because list only has one + // element? target_1q_basis_list.push_back(euler_basis); llvm::SmallVector, 8> euler_decompositions; @@ -1406,12 +1519,12 @@ struct GateDecompositionPattern final euler_decompositions.push_back(euler_decomp); } TwoQubitGateSequence gates{.globalPhase = target_decomposed.global_phase}; - // Worst case length is 5x 1q gates for each 1q decomposition + 1x 2q gate - // We might overallocate a bit if the euler basis is different but + // Worst case length is 5x 1q gates for each 1q decomposition + 1x 2q + // gate We might overallocate a bit if the euler basis is different but // the worst case is just 16 extra elements with just a String and 2 - // smallvecs each. This is only transient though as the circuit sequences - // aren't long lived and are just used to create a QuantumCircuit or - // DAGCircuit when we return to Python space. + // smallvecs each. This is only transient though as the circuit + // sequences aren't long lived and are just used to create a + // QuantumCircuit or DAGCircuit when we return to Python space. constexpr auto TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY = 21; gates.gates.reserve(TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY); gates.globalPhase -= best_nbasis * basis_decomposer.global_phase; @@ -1515,8 +1628,8 @@ struct GateDecompositionPattern final if (gate != qc::X) { // CX if (pulse_optimize.has_value()) { - throw std::runtime_error{ - "pulse_optimizer currently only works with CNOT entangling gate"}; + throw std::runtime_error{"pulse_optimizer currently only works " + "with CNOT entangling gate"}; } return std::nullopt; } @@ -1542,8 +1655,8 @@ struct GateDecompositionPattern final /// This first decomposes each unitary from the KAK decomposition into ZXZ /// on the source qubit of the CNOTs and XZX on the targets in order to /// commute operators to beginning and end of decomposition. The beginning - /// and ending single qubit gates are then collapsed and re-decomposed with - /// the single qubit decomposer. This last step could be avoided if + /// and ending single qubit gates are then collapsed and re-decomposed + /// with the single qubit decomposer. This last step could be avoided if /// performance is a concern. TwoQubitGateSequence get_sx_vz_2cx_efficient_euler( const std::vector& decomposition, @@ -1604,12 +1717,13 @@ struct GateDecompositionPattern final /// gates assuming three CNOT gates are needed. /// /// This first decomposes each unitary from the KAK decomposition into ZXZ - /// on the source qubit of the CNOTs and XZX on the targets in order commute - /// operators to beginning and end of decomposition. Inserting Hadamards - /// reverses the direction of the CNOTs and transforms a variable Rx -> - /// variable virtual Rz. The beginning and ending single qubit gates are - /// then collapsed and re-decomposed with the single qubit decomposer. This - /// last step could be avoided if performance is a concern. + /// on the source qubit of the CNOTs and XZX on the targets in order + /// commute operators to beginning and end of decomposition. Inserting + /// Hadamards reverses the direction of the CNOTs and transforms a + /// variable Rx -> variable virtual Rz. The beginning and ending single + /// qubit gates are then collapsed and re-decomposed with the single qubit + /// decomposer. This last step could be avoided if performance is a + /// concern. std::optional get_sx_vz_3cx_efficient_euler( const std::vector& decomposition, const TwoQubitWeylDecomposition& target_decomposed) { @@ -1814,7 +1928,8 @@ struct GateDecompositionPattern final matrix2x2 unitary_mat, const std::vector& target_basis_list, std::size_t qubit, const std::vector>& - error_map, // TODO: remove error_map+qubit for platform independence + error_map, // TODO: remove error_map+qubit for platform + // independence bool simplify, std::optional atol) { auto calculateError = [](const OneQubitGateSequence& sequence) { return sequence.gates.size(); @@ -1845,8 +1960,8 @@ struct GateDecompositionPattern final * directory of this source tree or at * http://www.apache.org/licenses/LICENSE-2.0. * - * Any modifications or derivative works of this code must retain this - * copyright notice, and modified files need to carry a notice + * Any modifications or derivative works of this code must retain + * this copyright notice, and modified files need to carry a notice * indicating that they have been altered from the originals. */ [[nodiscard]] static OneQubitGateSequence @@ -1854,7 +1969,7 @@ struct GateDecompositionPattern final qc::OpType kGate, qc::OpType aGate, bool simplify, std::optional atol) { fp angleZeroEpsilon = atol.value_or(1e-12); - if (simplify) { + if (!simplify) { angleZeroEpsilon = -1.0; } diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index 35d6f02d49..632a8fd62d 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -40,42 +40,48 @@ constexpr qfp M_IM{0., -1.}; namespace mqt::ir::opt::helpers { // TODO: remove -template void print(std::array, N> matrix, std::string s = "") { +template void print(std::array, N> matrix, std::string s = "", bool force = false) { + if (!force) return; int i{}; + int n = std::sqrt(N); if (!s.empty()) { llvm::errs() << "=== " << s << " ===\n"; } for (auto&& a : matrix) { std::cerr << std::setprecision(17) << a.real() << 'i' << a.imag() << ' '; - if (++i % 4 == 0) { + if (++i % n == 0) { llvm::errs() << '\n'; } } llvm::errs() << '\n'; } -template void print(std::array matrix, std::string s = "") { +template void print(std::array matrix, std::string s = "", bool force = false) { + if (!force) return; int i{}; + int n = std::sqrt(N); if (!s.empty()) { llvm::errs() << "=== " << s << " ===\n"; } for (auto&& a : matrix) { std::cerr << std::setprecision(17) << a << ' '; - if (++i % 4 == 0) { + if (++i % n == 0) { llvm::errs() << '\n'; } } llvm::errs() << '\n'; } -template void print(std::array matrix, std::string s = "") { +template void print(std::array matrix, std::string s = "", bool force = false) { + if (!force) return; int i{}; + int n = std::sqrt(N); if (!s.empty()) { llvm::errs() << "=== " << s << " ===\n"; } for (auto&& a : matrix) { std::cerr << a << ' '; - if (++i % 4 == 0) { + if (++i % n == 0) { llvm::errs() << '\n'; } } @@ -83,7 +89,7 @@ template void print(std::array matrix, std::stri } inline auto flatten(const dd::TwoQubitGateMatrix& matrix) { - std::array, 16> result; + std::array result; for (std::size_t i = 0; i < result.size(); ++i) { result[i] = matrix[i / 4][i % 4]; } @@ -172,8 +178,8 @@ inline std::optional mlirValueToFp(mlir::Value value) { return std::nullopt; } -[[nodiscard]] inline std::vector getParameters(UnitaryInterface op) { - std::vector parameters; +[[nodiscard]] inline llvm::SmallVector getParameters(UnitaryInterface op) { + llvm::SmallVector parameters; for (auto&& param : op.getParams()) { if (auto value = helpers::mlirValueToFp(param)) { parameters.push_back(*value); diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index b1a37228c5..1771267811 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -17,7 +17,7 @@ module { // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK-NOT: %[[ANY:.*]] = mqtopt.x + // CHECK-NOT: mqtopt.[[ANY:.*]] // CHECK: mqtopt.deallocQubit %[[Q0_0]] // CHECK: mqtopt.deallocQubit %[[Q1_0]] @@ -71,11 +71,10 @@ module { // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x %[[Q0_0]] ctrl %[[Q1_0]] - // CHECK-NOT: %[[ANY:.*]] = mqtopt.x + // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] - // CHECK: mqtopt.deallocQubit %[[Q0_0]] - // CHECK: mqtopt.deallocQubit %[[Q1_0]] + // CHECK: mqtopt.deallocQubit %[[Q0_1]] + // CHECK: mqtopt.deallocQubit %[[Q1_1]] %q0_0 = mqtopt.allocQubit %q1_0 = mqtopt.allocQubit @@ -90,3 +89,78 @@ module { return } } + +module { + // CHECK-LABEL: func.func @testSeriesOneQubitOpInbetween + func.func @testSeriesOneQubitOpInbetween() { + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit + + // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] + + // CHECK: mqtopt.deallocQubit %[[Q0_1]] + // CHECK: mqtopt.deallocQubit %[[Q1_1]] + + %q0_0 = mqtopt.allocQubit + %q1_0 = mqtopt.allocQubit + + %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_2 = mqtopt.x() %q0_1: !mqtopt.Qubit + %q0_3, %q1_2 = mqtopt.x() %q0_2 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit + + mqtopt.deallocQubit %q0_3 + mqtopt.deallocQubit %q1_2 + + return + } +} + +module { + // CHECK-LABEL: func.func @testSeriesStartingOneQubitOp + func.func @testSeriesStartingOneQubitOp() { + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit + + // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] + + // CHECK: mqtopt.deallocQubit %[[Q0_1]] + // CHECK: mqtopt.deallocQubit %[[Q1_1]] + + %q0_0 = mqtopt.allocQubit + %q1_0 = mqtopt.allocQubit + + %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_2, %q1_2 = mqtopt.x() %q0_1 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_3 = mqtopt.x() %q0_1: !mqtopt.Qubit + + mqtopt.deallocQubit %q0_3 + mqtopt.deallocQubit %q1_2 + + return + } +} + +module { + // CHECK-LABEL: func.func @testSeriesEndingOneQubitOp + func.func @testSeriesEndingOneQubitOp() { + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit + + // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] + + // CHECK: mqtopt.deallocQubit %[[Q0_1]] + // CHECK: mqtopt.deallocQubit %[[Q1_1]] + + %q0_0 = mqtopt.allocQubit + %q1_0 = mqtopt.allocQubit + + %q0_1 = mqtopt.x() %q0_0: !mqtopt.Qubit + %q0_2, %q1_1 = mqtopt.x() %q0_1 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_3, %q1_2 = mqtopt.x() %q0_2 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit + + mqtopt.deallocQubit %q0_3 + mqtopt.deallocQubit %q1_2 + + return + } +} From c87b02afd8a404c3c16900ad5bfc5ac2eb89285a Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 30 Oct 2025 21:18:29 +0100 Subject: [PATCH 028/237] fix test case --- .../MQTOpt/Transforms/gate-decomposition.mlir | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index 1771267811..490cffd6f6 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -129,9 +129,9 @@ module { %q0_0 = mqtopt.allocQubit %q1_0 = mqtopt.allocQubit - %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_2, %q1_2 = mqtopt.x() %q0_1 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3 = mqtopt.x() %q0_1: !mqtopt.Qubit + %q0_1 = mqtopt.x() %q0_0: !mqtopt.Qubit + %q0_2, %q1_1 = mqtopt.x() %q0_1 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_3, %q1_2 = mqtopt.x() %q0_2 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit mqtopt.deallocQubit %q0_3 mqtopt.deallocQubit %q1_2 @@ -154,9 +154,9 @@ module { %q0_0 = mqtopt.allocQubit %q1_0 = mqtopt.allocQubit - %q0_1 = mqtopt.x() %q0_0: !mqtopt.Qubit - %q0_2, %q1_1 = mqtopt.x() %q0_1 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3, %q1_2 = mqtopt.x() %q0_2 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_2, %q1_2 = mqtopt.x() %q0_1 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_3 = mqtopt.x() %q0_2: !mqtopt.Qubit mqtopt.deallocQubit %q0_3 mqtopt.deallocQubit %q1_2 From 619d0d695244a74680b53df65258e47ba03bf5ef Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 30 Oct 2025 21:18:42 +0100 Subject: [PATCH 029/237] minor fix --- .../MQTOpt/Transforms/GateDecompositionPattern.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 21060ae35b..52457e92c0 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -59,7 +59,7 @@ struct GateDecompositionPattern final auto gateMatrix = getTwoQubitMatrix({.type = helpers::getQcType(gate.op), .parameter = helpers::getParameters(gate.op), - .qubit_id = gate.qubit_id}); + .qubit_id = gate.qubitIds}); unitaryMatrix = helpers::multiply(unitaryMatrix, gateMatrix); } @@ -88,7 +88,7 @@ struct GateDecompositionPattern final struct Gate { UnitaryInterface op; - llvm::SmallVector qubit_id; + llvm::SmallVector qubitIds; }; llvm::SmallVector gates; @@ -102,12 +102,10 @@ struct GateDecompositionPattern final } if (helpers::isSingleQubitOperation(userUnitary)) { - result.appendSingleQubitGate(userUnitary); - return true; + return result.appendSingleQubitGate(userUnitary); } if (helpers::isTwoQubitOperation(userUnitary)) { - result.appendTwoQubitGate(userUnitary); - return true; + return result.appendTwoQubitGate(userUnitary); } return false; }; @@ -130,7 +128,7 @@ struct GateDecompositionPattern final } private: - TwoQubitSeries(UnitaryInterface initialOperation) { + explicit TwoQubitSeries(UnitaryInterface initialOperation) { auto&& in = initialOperation.getAllInQubits(); auto&& out = initialOperation->getResults(); if (helpers::isSingleQubitOperation(initialOperation)) { @@ -303,7 +301,7 @@ struct GateDecompositionPattern final qubits.push_back(inQubits[x]); } auto newGate = - createGate(rewriter, location, qubits, {}, gate.parameter); + createGate(rewriter, location, qubits, {}, gate.parameter); updateInQubits(gate, newGate); } else if (gate.type == qc::RZ) { mlir::SmallVector qubits; From a998ef7c3cf8b003bf56bf129ed825c808633d1e Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 30 Oct 2025 23:14:25 +0100 Subject: [PATCH 030/237] fix test case --- .../Dialect/MQTOpt/Transforms/gate-decomposition.mlir | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index 490cffd6f6..166936cfb7 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -96,10 +96,12 @@ module { // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] + // CHECK: mqtopt.gphase(%[[C0:.*]]) + // CHECK: %[[Q0_1:.*]] = mqtopt.ry(%[[C1:.*]]) %[[Q0_0]] + // CHECK: %[[Q0_2:.*]] = mqtopt.rz(%[[C1]]) %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q1_1]] + // CHECK: mqtopt.deallocQubit %[[Q0_2]] + // CHECK: mqtopt.deallocQubit %[[Q1_0]] %q0_0 = mqtopt.allocQubit %q1_0 = mqtopt.allocQubit From 71420a281ef249248bb10f643cea1410270f247a Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 30 Oct 2025 23:28:22 +0100 Subject: [PATCH 031/237] add new test, fix tests --- .../MQTOpt/Transforms/gate-decomposition.mlir | 72 +++++++++++++++++-- 1 file changed, 65 insertions(+), 7 deletions(-) diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index 166936cfb7..298a9c57fa 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -9,7 +9,7 @@ // RUN: quantum-opt %s -split-input-file --gate-decomposition | FileCheck %s // ----- -// This test checks if two-qubit consecutive controlled self-inverses are canceled correctly. +// This test checks if an even number of CNOT gates will be cancelled out. module { // CHECK-LABEL: func.func @testEvenNegationSeries @@ -65,6 +65,9 @@ module { } } +// ----- +// This test checks if an odd number of CNOT gates will be reduced to a single CNOT. + module { // CHECK-LABEL: func.func @testOddNegationSeries func.func @testOddNegationSeries() { @@ -90,6 +93,9 @@ module { } } +// ----- +// This test checks if a two-qubit series containing a single-qubit gate is decomposed correctly. + module { // CHECK-LABEL: func.func @testSeriesOneQubitOpInbetween func.func @testSeriesOneQubitOpInbetween() { @@ -117,16 +123,21 @@ module { } } +// ----- +// This test checks if a two-qubit series starting on a single-qubit gate is decomposed correctly. + module { // CHECK-LABEL: func.func @testSeriesStartingOneQubitOp func.func @testSeriesStartingOneQubitOp() { // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] + // CHECK: mqtopt.gphase(%[[C0:.*]]) + // CHECK: %[[Q0_1:.*]] = mqtopt.ry(%[[C1:.*]]) %[[Q0_0]] + // CHECK: %[[Q0_2:.*]] = mqtopt.rz(%[[C1]]) %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q1_1]] + // CHECK: mqtopt.deallocQubit %[[Q0_2]] + // CHECK: mqtopt.deallocQubit %[[Q1_0]] %q0_0 = mqtopt.allocQubit %q1_0 = mqtopt.allocQubit @@ -142,16 +153,21 @@ module { } } +// ----- +// This test checks if a two-qubit series ending on a single-qubit gate is decomposed correctly. + module { // CHECK-LABEL: func.func @testSeriesEndingOneQubitOp func.func @testSeriesEndingOneQubitOp() { // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] + // CHECK: mqtopt.gphase(%[[C0:.*]]) + // CHECK: %[[Q0_1:.*]] = mqtopt.ry(%[[C1:.*]]) %[[Q0_0]] + // CHECK: %[[Q0_2:.*]] = mqtopt.rz(%[[C1]]) %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q1_1]] + // CHECK: mqtopt.deallocQubit %[[Q0_2]] + // CHECK: mqtopt.deallocQubit %[[Q1_0]] %q0_0 = mqtopt.allocQubit %q1_0 = mqtopt.allocQubit @@ -166,3 +182,45 @@ module { return } } + +// ----- +// This test checks if an interrupted series is ignored correctly. + +module { + // CHECK-LABEL: func.func @testInterruptedSeries + func.func @testInterruptedSeries() { + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit + + // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] + // CHECK: %[[Q1_2:.*]], %[[Q0_2:.*]] = mqtopt.x() %[[Q1_1]] ctrl %[[Q0_1]] + // CHECK: %[[Q0_3:.*]], %[[Q2_1:.*]] = mqtopt.x() %[[Q0_2]] ctrl %[[Q2_0]] + // CHECK: %[[Q1_3:.*]], %[[Q0_4:.*]] = mqtopt.x() %[[Q1_2]] ctrl %[[Q0_3]] + // CHECK: %[[Q0_5:.*]], %[[Q1_4:.*]] = mqtopt.x() %[[Q0_4]] ctrl %[[Q1_3]] + + // CHECK-NOT: mqtopt.ry + // CHECK-NOT: mqtopt.rz + // CHECK-NOT: mqtopt.gphase(%[[ANY:.*]]) + + // CHECK: mqtopt.deallocQubit %[[Q0_5]] + // CHECK: mqtopt.deallocQubit %[[Q1_4]] + // CHECK: mqtopt.deallocQubit %[[Q2_1]] + + %q0_0 = mqtopt.allocQubit + %q1_0 = mqtopt.allocQubit + %q2_0 = mqtopt.allocQubit + + %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q1_2, %q0_2 = mqtopt.x() %q1_1 ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_3, %q2_1 = mqtopt.x() %q0_2 ctrl %q2_0: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q1_3, %q0_4 = mqtopt.x() %q1_2 ctrl %q0_3: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_5, %q1_4 = mqtopt.x() %q0_4 ctrl %q1_3: !mqtopt.Qubit ctrl !mqtopt.Qubit + + mqtopt.deallocQubit %q0_5 + mqtopt.deallocQubit %q1_4 + mqtopt.deallocQubit %q2_1 + + return + } +} From bd8cdbdb3baeee826b85f71331cca10a004ff284 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 4 Nov 2025 16:29:29 +0100 Subject: [PATCH 032/237] iniital complexSeries test --- .../MQTOpt/Transforms/gate-decomposition.mlir | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index 298a9c57fa..1ea8f72353 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -224,3 +224,39 @@ module { return } } + +module { + // CHECK-LABEL: func.func @testComplexSeries + func.func @testComplexSeries() { + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit + + // CHECK-NOT: mqtopt.gphase(%[[ANY:.*]]) + + // CHECK: mqtopt.deallocQubit %[[Q0_5]] + // CHECK: mqtopt.deallocQubit %[[Q1_4]] + // CHECK: mqtopt.deallocQubit %[[Q2_1]] + + %cst0 = arith.constant 2.5 : f64 + %cst1 = arith.constant 1.2 : f64 + + %q0_0 = mqtopt.allocQubit + %q1_0 = mqtopt.allocQubit + %q2_0 = mqtopt.allocQubit + + %q0_1 = mqtopt.h() %q0_0: !mqtopt.Qubit + %q1_1, %q0_2 = mqtopt.x() %q1_0 ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_3, %q1_2 = mqtopt.rzz(%cst0) %q0_2, %q1_1: !mqtopt.Qubit, !mqtopt.Qubit + %q1_3 = mqtopt.ry(%cst1) %q1_2: !mqtopt.Qubit + %q0_4 = mqtopt.rx(%cst1) %q0_3: !mqtopt.Qubit + %q0_5, %q1_4 = mqtopt.x() %q0_4 ctrl %q1_3: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_6 = mqtopt.rz(%cst0) %q0_5: !mqtopt.Qubit + + mqtopt.deallocQubit %q0_6 + mqtopt.deallocQubit %q1_4 + mqtopt.deallocQubit %q2_0 + + return + } +} From 85b03c2afb523a92cdaa30daf7360ceaa0d833d5 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 4 Nov 2025 17:31:25 +0100 Subject: [PATCH 033/237] use Eigen for multiply() and determinant() --- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 92 +++++++------------- 1 file changed, 31 insertions(+), 61 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index 632a8fd62d..b52cf42fe1 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -16,6 +16,7 @@ #include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" #include +#include #include #include @@ -271,15 +272,31 @@ template template [[nodiscard]] inline auto multiply(const std::array& lhs, const std::array& rhs) { + const int n = std::sqrt(N); + Eigen::Matrix lhsEigen; + Eigen::Matrix rhsEigen; + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + lhsEigen(j, i) = lhs[j * n + i]; + rhsEigen(j, i) = rhs[j * n + i]; + } + } + auto r = lhsEigen * rhsEigen; std::array result{}; - const int n = std::sqrt(lhs.size()); - for (int i = 0; i < n; i++) { - for (int j = 0; j < n; j++) { - for (int k = 0; k < n; k++) { - result[i * n + j] += lhs[i * n + k] * rhs[k * n + j]; - } + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + result[j * n + i] = r(j, i); } } + // std::array result{}; + // const int n = std::sqrt(N); + // for (int i = 0; i < n; i++) { + // for (int j = 0; j < n; j++) { + // for (int k = 0; k < n; k++) { + // result[i * n + j] += lhs[i * n + k] * rhs[k * n + j]; + // } + // } + // } return result; } @@ -472,63 +489,16 @@ inline auto transpose_conjugate(Container&& matrix) { return conjugate(result); } -template -inline T determinant(const std::array& mat) { - return mat[0] * mat[3] - mat[1] * mat[2]; -} - -template -inline T determinant(const std::array& mat) { - return mat[0] * (mat[4] * mat[8] - mat[5] * mat[7]) - - mat[1] * (mat[3] * mat[8] - mat[5] * mat[6]) + - mat[2] * (mat[3] * mat[7] - mat[4] * mat[6]); -} - -inline std::array get3x3Submatrix(const matrix4x4& mat, - int rowToBeRemoved, - int columnToBeRemoved) { - std::array, 9> result; - int subIndex = 0; - for (int i = 0; i < 4; ++i) { - if (i != rowToBeRemoved) { - for (int j = 0; j < 4; ++j) { - if (j != columnToBeRemoved) { - result[subIndex++] = mat[i * 4 + j]; - } - } +template +inline T determinant(const std::array& mat) { + const int n = std::sqrt(N); + Eigen::Matrix matEigen; + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + matEigen(j, i) = mat[j * n + i]; } } - return result; -} - -template -inline T determinant(const std::array& mat) { - auto [l, u, rowPermutations] = helpers::LUdecomposition(mat); - T det = 1.0; - for (int i = 0; i < 4; ++i) { - det *= l[i * 4 + i]; - } - - if (rowPermutations % 2 != 0) { - det = -det; - } - return det; - - // auto det = -C_ZERO; - // for (int column = 0; column < 4; ++column) { - // auto submatrix = get3x3Submatrix(mat, 0, column); - // auto subDet = determinant(submatrix); - // auto tmp = mat[0 * 4 + column] * subDet; - // if (column % 2 == 0 && - // tmp != - // C_ZERO) { // TODO: better way to get negative 0.0 in - // determinant? - // det += tmp; - // } else if (tmp != -C_ZERO) { - // det -= tmp; - // } - // } - // return det; + return matEigen.determinant(); } template From 0de0b43565be0c3fad638318ecc5efb68966ea1f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 4 Nov 2025 23:06:06 +0100 Subject: [PATCH 034/237] fix gate matrix multiplication --- mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 52457e92c0..a85bf6c7cc 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -60,7 +60,7 @@ struct GateDecompositionPattern final getTwoQubitMatrix({.type = helpers::getQcType(gate.op), .parameter = helpers::getParameters(gate.op), .qubit_id = gate.qubitIds}); - unitaryMatrix = helpers::multiply(unitaryMatrix, gateMatrix); + unitaryMatrix = helpers::multiply(gateMatrix, unitaryMatrix); } auto decomposer = TwoQubitBasisDecomposer::new_inner(); From cc9f1be83fabb874da6467e5a5e8a8d4f718a39f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 4 Nov 2025 23:06:33 +0100 Subject: [PATCH 035/237] add simple (too simple) series complexity --- .../Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index a85bf6c7cc..e3e382bab6 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -70,7 +70,7 @@ struct GateDecompositionPattern final llvm::errs() << "NO SEQUENCE GENERATED!\n"; return mlir::failure(); } - if (sequence->gates.size() >= series.gates.size()) { + if (sequence->gates.size() >= series.complexity) { // TODO: add more sophisticated metric to determine complexity of // series/sequence llvm::errs() << "SEQUENCE LONGER THAN INPUT!\n"; @@ -83,6 +83,7 @@ struct GateDecompositionPattern final } struct TwoQubitSeries { + std::size_t complexity{0}; std::array inQubits; std::array outQubits; @@ -135,10 +136,12 @@ struct GateDecompositionPattern final inQubits = {in[0], mlir::Value{}}; outQubits = {out[0], mlir::Value{}}; gates.push_back({initialOperation, {0}}); + complexity += 1; } else if (helpers::isTwoQubitOperation(initialOperation)) { inQubits = {in[0], in[1]}; outQubits = {out[0], out[1]}; gates.push_back({initialOperation, {0, 1}}); + complexity += 2; } } @@ -156,6 +159,7 @@ struct GateDecompositionPattern final *it = nextGate->getResult(0); gates.push_back({nextGate, {qubitId}}); + complexity += 1; return true; } @@ -191,6 +195,7 @@ struct GateDecompositionPattern final std::distance(outQubits.begin(), secondQubitIt); gates.push_back({nextGate, {firstQubitId, secondQubitId}}); + complexity += 2; return true; } }; From 13721df192666ee63f06e0ee6deaad8cbd5dfdab Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 4 Nov 2025 23:07:22 +0100 Subject: [PATCH 036/237] fix insertion when operations which are not part of the series are involved --- .../Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index e3e382bab6..3d6959430d 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -255,7 +255,10 @@ struct GateDecompositionPattern final static void applySeries(mlir::PatternRewriter& rewriter, TwoQubitSeries& series, const TwoQubitGateSequence& sequence) { - auto location = series.gates.back().op->getLoc(); + auto& lastSeriesOp = series.gates.back().op; + auto location = lastSeriesOp->getLoc(); + rewriter.setInsertionPointAfter(lastSeriesOp); + if (sequence.globalPhase != 0.0) { createOneParameterGate(rewriter, location, sequence.globalPhase, {}); From 63056040749f52274666deb316768523d09eba39 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 4 Nov 2025 23:08:13 +0100 Subject: [PATCH 037/237] add gate matrices --- .../Transforms/GateDecompositionPattern.cpp | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 3d6959430d..9cb918fb51 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -618,14 +618,29 @@ struct GateDecompositionPattern final if (gate.type == qc::SX) { return {qfp{0.5, 0.5}, qfp{0.5, -0.5}, qfp{0.5, -0.5}, qfp{0.5, 0.5}}; } + if (gate.type == qc::RX) { + return rx_matrix(gate.parameter[0]); + } + if (gate.type == qc::RY) { + return ry_matrix(gate.parameter[0]); + } if (gate.type == qc::RZ) { return rz_matrix(gate.parameter[0]); } if (gate.type == qc::X) { return {0, 1, 1, 0}; } + if (gate.type == qc::I) { + return identityGate; + } + if (gate.type == qc::H) { + static constexpr fp SQRT2_2 = static_cast( + 0.707106781186547524400844362104849039284835937688474036588L); + return {SQRT2_2, SQRT2_2, SQRT2_2, -SQRT2_2}; + } throw std::invalid_argument{ - "unsupported gate type for single qubit matrix"}; + "unsupported gate type for single qubit matrix (" + + qc::toString(gate.type) + ")"}; } static matrix4x4 getTwoQubitMatrix(const QubitGateSequence::Gate& gate) { @@ -654,13 +669,19 @@ struct GateDecompositionPattern final } } if (gate.type == qc::RZ) { + throw std::invalid_argument{"RZ for two-qubit gate matrix"}; // TODO: check qubit order return rzzMatrix(gate.parameter[0]); } if (gate.type == qc::RX) { + throw std::invalid_argument{"RX for two-qubit gate matrix"}; // TODO: check qubit order return rxxMatrix(gate.parameter[0]); } + if (gate.type == qc::RZZ) { + // TODO: check qubit order + return rzzMatrix(gate.parameter[0]); + } throw std::invalid_argument{ "unsupported gate type for two qubit matrix "}; } @@ -1123,7 +1144,7 @@ struct GateDecompositionPattern final angles_from_unitary(general.K2l, EulerBasis::ZYZ); auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = angles_from_unitary(general.K2r, EulerBasis::ZYZ); - TwoQubitWeylDecomposition{ + return TwoQubitWeylDecomposition{ qc::PI_4, qc::PI_4, c, From dd130b985943bba962404eef365d7601b163adcf Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 6 Nov 2025 14:10:06 +0100 Subject: [PATCH 038/237] sequence complexity() --- .../Transforms/GateDecompositionPattern.cpp | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 9cb918fb51..64b5b8071a 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -15,9 +15,7 @@ #include #include -#include #include -#include #include #include #include @@ -70,7 +68,7 @@ struct GateDecompositionPattern final llvm::errs() << "NO SEQUENCE GENERATED!\n"; return mlir::failure(); } - if (sequence->gates.size() >= series.complexity) { + if (sequence->complexity() >= series.complexity) { // TODO: add more sophisticated metric to determine complexity of // series/sequence llvm::errs() << "SEQUENCE LONGER THAN INPUT!\n"; @@ -247,6 +245,13 @@ struct GateDecompositionPattern final llvm::SmallVector qubit_id = {0}; }; std::vector gates; + std::size_t complexity() { + std::size_t c{}; + for (auto&& gate : gates) { + c += gate.qubit_id.size(); + } + return c; + } fp globalPhase; }; using OneQubitGateSequence = QubitGateSequence; @@ -401,7 +406,7 @@ struct GateDecompositionPattern final auto [U, S] = self_adjoint_evd(A); // TODO: not in original code - if (helpers::determinant(U) + 1.0 < 1e-5) { + if (std::abs(helpers::determinant(U) + 1.0) < 1e-5) { std::cerr << "CORRECTION!\n"; // if determinant of eigenvector matrix is -1.0, multiply first // eigenvector by -1.0 @@ -515,8 +520,12 @@ struct GateDecompositionPattern final } static matrix2x2 rz_matrix(fp theta) { - auto ilam2 = qfp(0., 0.5 * theta); - return {std::exp(-ilam2), C_ZERO, C_ZERO, std::exp(ilam2)}; + return {qfp{std::cos(theta / 2.), -std::sin(theta / 2.)}, + 0, + 0, + {std::cos(theta / 2.), std::sin(theta / 2.)}}; + // auto ilam2 = qfp(0., 0.5 * theta); + // return {std::exp(-ilam2), C_ZERO, C_ZERO, std::exp(ilam2)}; } static matrix4x4 rxxMatrix(const fp theta) { @@ -1284,6 +1293,7 @@ struct GateDecompositionPattern final struct TwoQubitBasisDecomposer { qc::OpType gate; + std::array gate_qubit_ids; llvm::SmallVector gate_params; fp basis_fidelity; EulerBasis euler_basis; @@ -1313,6 +1323,7 @@ struct GateDecompositionPattern final public: static TwoQubitBasisDecomposer new_inner( qc::OpType gate = qc::X, // CX + std::array gate_qubit_ids = {1, 0}, const llvm::SmallVector& gate_params = {}, // matrix4x4 gate_matrix = {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, matrix4x4 gate_matrix = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, @@ -1449,6 +1460,7 @@ struct GateDecompositionPattern final return TwoQubitBasisDecomposer{ gate, + gate_qubit_ids, gate_params, basis_fidelity, euler_basis, From fb742d8cd916eaa7c787534dff78945ee1d33c57 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 6 Nov 2025 14:10:26 +0100 Subject: [PATCH 039/237] print debugging :) --- .../Transforms/GateDecompositionPattern.cpp | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 64b5b8071a..da5b2cc5be 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -40,6 +40,10 @@ struct GateDecompositionPattern final mlir::PatternRewriter& rewriter) const override { auto series = TwoQubitSeries::getTwoQubitSeries(op); llvm::errs() << "SERIES SIZE: " << series.gates.size() << '\n'; + for (auto&& gate : series.gates) { + std::cerr << gate.op->getName().stripDialect().str() << ", "; + } + std::cerr << '\n'; if (series.gates.size() < 3) { // too short @@ -53,13 +57,16 @@ struct GateDecompositionPattern final matrix4x4 unitaryMatrix = helpers::kroneckerProduct(identityGate, identityGate); + int i{}; for (auto&& gate : series.gates) { auto gateMatrix = getTwoQubitMatrix({.type = helpers::getQcType(gate.op), .parameter = helpers::getParameters(gate.op), .qubit_id = gate.qubitIds}); unitaryMatrix = helpers::multiply(gateMatrix, unitaryMatrix); + helpers::print(gateMatrix, "GATE MATRIX " + std::to_string(i++), true); } + helpers::print(unitaryMatrix, "UNITARY MATRIX", true); auto decomposer = TwoQubitBasisDecomposer::new_inner(); auto sequence = decomposer.twoQubitDecompose( @@ -71,7 +78,8 @@ struct GateDecompositionPattern final if (sequence->complexity() >= series.complexity) { // TODO: add more sophisticated metric to determine complexity of // series/sequence - llvm::errs() << "SEQUENCE LONGER THAN INPUT!\n"; + llvm::errs() << "SEQUENCE LONGER THAN INPUT (" << sequence->gates.size() + << ")\n"; return mlir::failure(); } @@ -721,28 +729,18 @@ struct GateDecompositionPattern final using helpers::transpose; auto& u = unitary_matrix; auto det_u = determinant(u); + std::cerr << "DET_U: " << det_u << '\n'; auto det_pow = std::pow(det_u, static_cast(-0.25)); llvm::transform(u, u.begin(), [&](auto&& x) { return x * det_pow; }); - llvm::errs() << "===== U =====\n"; - helpers::print(u); + helpers::print(u, "U", true); auto global_phase = std::arg(det_u) / 4.; auto u_p = magic_basis_transform(u, MagicBasisTransform::OutOf); - llvm::errs() << "===== U_P =====\n"; - helpers::print(u_p); + helpers::print(u_p, "U_P", true); auto m2 = dot(transpose(u_p), u_p); auto default_euler_basis = EulerBasis::ZYZ; - llvm::errs() << "===== M2 =====\n"; - helpers::print(m2); - - // arma::Mat U(4, 4); - // for (int i = 0; i < 4; ++i) { - // for (int j = 0; j < 4; ++j) { - // U.at(j, i) = u_p[j * 4 + i]; - // } - // } - // auto x = U.st() * U; - // std::cerr << "ARMA\n" << U.t() << "\n\n" << U << "\n\n" << x << - // std::endl; + helpers::print(m2, "M2", true); + + std::cerr << "DET_U after division: " << determinant(u) << '\n'; // M2 is a symmetric complex matrix. We need to decompose it as M2 = P D // P^T where P ∈ SO(4), D is diagonal with unit-magnitude elements. @@ -789,8 +787,8 @@ struct GateDecompositionPattern final [](auto&& x) { return qfp(x, 0.0); }); auto d_inner = diagonal(dot(dot(transpose(p_inner), m2), p_inner)); - helpers::print(d_inner, "D_INNER"); - helpers::print(p_inner, "P_INNER"); + helpers::print(d_inner, "D_INNER", true); + helpers::print(p_inner, "P_INNER", true); matrix4x4 diag_d{}; // zero initialization diag_d[0 * 4 + 0] = d_inner[0]; diag_d[1 * 4 + 1] = d_inner[1]; From e6840d4c566791677277756018cdc1e8aeaed279 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 6 Nov 2025 14:10:52 +0100 Subject: [PATCH 040/237] longer complex test to pass sequence complexity check --- .../MQTOpt/Transforms/gate-decomposition.mlir | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index 1ea8f72353..63b676b13a 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -240,6 +240,7 @@ module { %cst0 = arith.constant 2.5 : f64 %cst1 = arith.constant 1.2 : f64 + %cst2 = arith.constant 0.5 : f64 %q0_0 = mqtopt.allocQubit %q1_0 = mqtopt.allocQubit @@ -251,10 +252,19 @@ module { %q1_3 = mqtopt.ry(%cst1) %q1_2: !mqtopt.Qubit %q0_4 = mqtopt.rx(%cst1) %q0_3: !mqtopt.Qubit %q0_5, %q1_4 = mqtopt.x() %q0_4 ctrl %q1_3: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_6 = mqtopt.rz(%cst0) %q0_5: !mqtopt.Qubit - - mqtopt.deallocQubit %q0_6 - mqtopt.deallocQubit %q1_4 + %q0_6 = mqtopt.rz(%cst2) %q0_5: !mqtopt.Qubit + // make series longer to enforce decomposition + %q0_7 = mqtopt.i() %q0_6: !mqtopt.Qubit + %q0_8 = mqtopt.i() %q0_7: !mqtopt.Qubit + %q0_9 = mqtopt.i() %q0_8: !mqtopt.Qubit + %q1_5 = mqtopt.i() %q1_4: !mqtopt.Qubit + %q1_6 = mqtopt.i() %q1_5: !mqtopt.Qubit + %q1_7 = mqtopt.i() %q1_6: !mqtopt.Qubit + %q1_8 = mqtopt.i() %q1_7: !mqtopt.Qubit + %q1_9 = mqtopt.i() %q1_8: !mqtopt.Qubit + + mqtopt.deallocQubit %q0_9 + mqtopt.deallocQubit %q1_9 mqtopt.deallocQubit %q2_0 return From 08e199163f0b56ce8065ab8dd08f624164f3784f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 10 Nov 2025 15:13:22 +0100 Subject: [PATCH 041/237] use CXGate() definition for decomposer --- .../MQTOpt/Transforms/GateDecompositionPattern.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index da5b2cc5be..6b6ce298e9 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -120,6 +120,8 @@ struct GateDecompositionPattern final bool isFirstQubitOngoing = result.outQubits[0] != mlir::Value{}; bool isSecondQubitOngoing = result.outQubits[1] != mlir::Value{}; while (isFirstQubitOngoing || isSecondQubitOngoing) { + // TODO: can cause issues; instead: per iteration, collect all + // single-qubit operations, then take one two-qubit operation; repeat if (result.outQubits[0]) { assert(result.outQubits[0].hasOneUse()); isFirstQubitOngoing = @@ -1321,10 +1323,11 @@ struct GateDecompositionPattern final public: static TwoQubitBasisDecomposer new_inner( qc::OpType gate = qc::X, // CX - std::array gate_qubit_ids = {1, 0}, + std::array gate_qubit_ids = {0, 1}, const llvm::SmallVector& gate_params = {}, - // matrix4x4 gate_matrix = {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, - matrix4x4 gate_matrix = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, + matrix4x4 gate_matrix = {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, + // matrix4x4 gate_matrix = {1, 0, 0, 0, 0, 1, + // 0, 0, 0, 0, 0, 1, 0, 0, 1, 0}, // CX matrix fp basis_fidelity = 1.0, EulerBasis euler_basis = EulerBasis::ZYZ, std::optional pulse_optimize = std::nullopt) { From 0e6ff8eabd1cef19b75ea5554f89337a549d4862 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 10 Nov 2025 15:13:44 +0100 Subject: [PATCH 042/237] add cnot test other direction --- .../MQTOpt/Transforms/gate-decomposition.mlir | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index 63b676b13a..402c791057 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -270,3 +270,50 @@ module { return } } + +module { + // CHECK-LABEL: func.func @testCNotOtherDirection + func.func @testCNotOtherDirection() { + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit + + // CHECK-NOT: mqtopt.gphase(%[[ANY:.*]]) + + // CHECK: mqtopt.deallocQubit %[[Q0_5]] + // CHECK: mqtopt.deallocQubit %[[Q1_4]] + // CHECK: mqtopt.deallocQubit %[[Q2_1]] + + %cst0 = arith.constant 2.5 : f64 + %cst1 = arith.constant 1.2 : f64 + %cst2 = arith.constant 0.5 : f64 + + %q0_0 = mqtopt.allocQubit + %q1_0 = mqtopt.allocQubit + %q2_0 = mqtopt.allocQubit + + %q0_1 = mqtopt.i() %q0_0: !mqtopt.Qubit + %q1_1 = mqtopt.i() %q1_0: !mqtopt.Qubit + %q0_2 = mqtopt.i() %q0_1: !mqtopt.Qubit + %q0_3, %q1_2 = mqtopt.x() %q0_2 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q1_3 = mqtopt.i() %q1_2: !mqtopt.Qubit + %q0_4 = mqtopt.i() %q0_3: !mqtopt.Qubit + %q1_4 = mqtopt.i() %q1_3: !mqtopt.Qubit + %q0_5 = mqtopt.i() %q0_4: !mqtopt.Qubit + %q0_6 = mqtopt.i() %q0_5: !mqtopt.Qubit + %q0_7 = mqtopt.i() %q0_6: !mqtopt.Qubit + %q0_8 = mqtopt.i() %q0_7: !mqtopt.Qubit + %q0_9 = mqtopt.i() %q0_8: !mqtopt.Qubit + %q1_5 = mqtopt.i() %q1_4: !mqtopt.Qubit + %q1_6 = mqtopt.i() %q1_5: !mqtopt.Qubit + %q1_7 = mqtopt.i() %q1_6: !mqtopt.Qubit + %q1_8 = mqtopt.i() %q1_7: !mqtopt.Qubit + %q1_9 = mqtopt.i() %q1_8: !mqtopt.Qubit + + mqtopt.deallocQubit %q0_9 + mqtopt.deallocQubit %q1_9 + mqtopt.deallocQubit %q2_0 + + return + } +} From 5814ade77e6f0436358805689da97e19125428f8 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 10 Nov 2025 15:13:56 +0100 Subject: [PATCH 043/237] tmp (to be deleted) --- .../Transforms/GateDecompositionPattern.cpp | 15 +++++++++------ mlir/lib/Dialect/MQTOpt/Transforms/g.h | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 6b6ce298e9..8f765901f1 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -44,6 +44,10 @@ struct GateDecompositionPattern final std::cerr << gate.op->getName().stripDialect().str() << ", "; } std::cerr << '\n'; + static int a{}; + if (a++ > 0) { + return mlir::failure(); + } if (series.gates.size() < 3) { // too short @@ -815,14 +819,14 @@ struct GateDecompositionPattern final std::array d_real; llvm::transform(d, d_real.begin(), [](auto&& x) { return -std::arg(x) / 2.0; }); - helpers::print(d_real, "D_REAL"); + helpers::print(d_real, "D_REAL", true); d_real[3] = -d_real[0] - d_real[1] - d_real[2]; std::array cs; for (std::size_t i = 0; i < cs.size(); ++i) { assert(i < d_real.size()); cs[i] = remEuclid((d_real[i] + d_real[3]) / 2.0, qc::TAU); } - helpers::print(cs, "CS"); + helpers::print(cs, "CS", true); decltype(cs) cstemp; llvm::transform(cs, cstemp.begin(), [](auto&& x) { auto tmp = remEuclid(x, qc::PI_2); @@ -833,15 +837,14 @@ struct GateDecompositionPattern final // order in eigen decomposition algorithm? llvm::stable_sort(order, [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); - helpers::print(order, "ORDER (1)"); std::tie(order[0], order[1], order[2]) = std::tuple{order[1], order[2], order[0]}; - helpers::print(order, "ORDER (2)"); + helpers::print(order, "ORDER", true); std::tie(cs[0], cs[1], cs[2]) = std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; std::tie(d_real[0], d_real[1], d_real[2]) = std::tuple{d_real[order[0]], d_real[order[1]], d_real[order[2]]}; - helpers::print(d_real, "D_REAL (sorted)"); + helpers::print(d_real, "D_REAL (sorted)", true); // swap columns of p according to order constexpr auto P_ROW_LENGTH = 4; @@ -867,7 +870,7 @@ struct GateDecompositionPattern final temp[2 * 4 + 2] = std::exp(IM * d_real[2]); temp[3 * 4 + 3] = std::exp(IM * d_real[3]); helpers::print(temp, "TEMP"); - helpers::print(p, "P"); + helpers::print(p, "P", true); auto k1 = magic_basis_transform(dot(dot(u_p, p), temp), MagicBasisTransform::Into); auto k2 = magic_basis_transform(transpose(p), MagicBasisTransform::Into); diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/g.h b/mlir/lib/Dialect/MQTOpt/Transforms/g.h index d2927e943d..8eb6c40540 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/g.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/g.h @@ -14,7 +14,7 @@ auto self_adjoint_evd(rmatrix4x4 A) { } } std::cerr << "=EigIN==\n" << a << "\n========\n" << std::endl; - s.computeDirect(a); + s.compute(a); // TODO: computeDirect is faster auto vecs = s.eigenvectors(); auto vals = s.eigenvalues(); rmatrix4x4 rvecs; From 70d604e961e5123106c6e7008f88ee9c8a1dbc68 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 10 Nov 2025 21:31:36 +0100 Subject: [PATCH 044/237] re-write using Eigen --- .../Transforms/GateDecompositionPattern.cpp | 659 ++++++------ mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 400 +------ mlir/lib/Dialect/MQTOpt/Transforms/a.h | 82 -- mlir/lib/Dialect/MQTOpt/Transforms/b.h | 469 --------- mlir/lib/Dialect/MQTOpt/Transforms/c.h | 142 --- mlir/lib/Dialect/MQTOpt/Transforms/d.h | 31 - mlir/lib/Dialect/MQTOpt/Transforms/e.h | 976 ------------------ mlir/lib/Dialect/MQTOpt/Transforms/f.h | 97 -- mlir/lib/Dialect/MQTOpt/Transforms/g.h | 32 - 9 files changed, 337 insertions(+), 2551 deletions(-) delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/a.h delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/b.h delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/c.h delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/d.h delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/e.h delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/f.h delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/g.h diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 8f765901f1..69f5f1a259 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -9,12 +9,12 @@ */ #include "Helpers.h" -#include "g.h" #include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" #include "mlir/Dialect/MQTOpt/Transforms/Passes.h" #include #include +#include #include #include #include @@ -67,7 +67,7 @@ struct GateDecompositionPattern final getTwoQubitMatrix({.type = helpers::getQcType(gate.op), .parameter = helpers::getParameters(gate.op), .qubit_id = gate.qubitIds}); - unitaryMatrix = helpers::multiply(gateMatrix, unitaryMatrix); + unitaryMatrix = gateMatrix * unitaryMatrix; helpers::print(gateMatrix, "GATE MATRIX " + std::to_string(i++), true); } helpers::print(unitaryMatrix, "UNITARY MATRIX", true); @@ -99,7 +99,7 @@ struct GateDecompositionPattern final struct Gate { UnitaryInterface op; - llvm::SmallVector qubitIds; + llvm::SmallVector qubitIds; }; llvm::SmallVector gates; @@ -167,7 +167,7 @@ struct GateDecompositionPattern final throw std::logic_error{"Operand of single-qubit op and user of " "qubit is not in current outQubits"}; } - std::uint8_t qubitId = std::distance(outQubits.begin(), it); + std::size_t qubitId = std::distance(outQubits.begin(), it); *it = nextGate->getResult(0); gates.push_back({nextGate, {qubitId}}); @@ -200,10 +200,9 @@ struct GateDecompositionPattern final secondQubitIt = (secondQubitIt != outQubits.end()) ? secondQubitIt : it; } *firstQubitIt = nextGate->getResult(0); - std::uint8_t firstQubitId = - std::distance(outQubits.begin(), firstQubitIt); + std::size_t firstQubitId = std::distance(outQubits.begin(), firstQubitIt); *secondQubitIt = nextGate->getResult(1); - std::uint8_t secondQubitId = + std::size_t secondQubitId = std::distance(outQubits.begin(), secondQubitIt); gates.push_back({nextGate, {firstQubitId, secondQubitId}}); @@ -256,7 +255,7 @@ struct GateDecompositionPattern final struct Gate { qc::OpType type{qc::I}; llvm::SmallVector parameter; - llvm::SmallVector qubit_id = {0}; + llvm::SmallVector qubit_id = {0}; }; std::vector gates; std::size_t complexity() { @@ -385,9 +384,8 @@ struct GateDecompositionPattern final }; static constexpr auto sqrt2 = static_cast(1.4142135623730950488L); - static constexpr matrix2x2 identityGate = {1, 0, 0, 1}; - static constexpr matrix2x2 hGate = {1.0 / sqrt2, 1.0 / sqrt2, 1.0 / sqrt2, - -1.0 / sqrt2}; + static const matrix2x2 identityGate; + static const matrix2x2 hGate; static fp remEuclid(fp a, fp b) { auto r = std::fmod(a, b); @@ -417,15 +415,15 @@ struct GateDecompositionPattern final // rmatrix4x4 U; // jacobi_eigen_decomposition(A, U, S); - auto [U, S] = self_adjoint_evd(A); + auto [U, S] = helpers::self_adjoint_evd(A); // TODO: not in original code - if (std::abs(helpers::determinant(U) + 1.0) < 1e-5) { + if (std::abs(U.determinant() + 1.0) < 1e-5) { std::cerr << "CORRECTION!\n"; // if determinant of eigenvector matrix is -1.0, multiply first // eigenvector by -1.0 for (int i = 0; i < 4; ++i) { - U[i * 4 + 0] *= -1.0; + U(i, 0) *= -1.0; } } @@ -434,50 +432,43 @@ struct GateDecompositionPattern final static std::tuple decompose_two_qubit_product_gate(matrix4x4 special_unitary) { - using helpers::determinant; - using helpers::dot; - using helpers::kroneckerProduct; - using helpers::transpose_conjugate; helpers::print(special_unitary, "SPECIAL_UNITARY"); // first quadrant - matrix2x2 r = {special_unitary[0 * 4 + 0], special_unitary[0 * 4 + 1], - special_unitary[1 * 4 + 0], special_unitary[1 * 4 + 1]}; - auto det_r = determinant(r); + matrix2x2 r{{special_unitary(0, 0), special_unitary(0, 1)}, + {special_unitary(1, 0), special_unitary(1, 1)}}; + auto det_r = r.determinant(); if (std::abs(det_r) < 0.1) { // third quadrant - r = {special_unitary[2 * 4 + 0], special_unitary[2 * 4 + 1], - special_unitary[3 * 4 + 0], special_unitary[3 * 4 + 1]}; - det_r = determinant(r); + r = matrix2x2{{special_unitary(2, 0), special_unitary(2, 1)}, + {special_unitary(3, 0), special_unitary(3, 1)}}; + det_r = r.determinant(); } std::cerr << "DET_R: " << det_r << '\n'; if (std::abs(det_r) < 0.1) { throw std::runtime_error{ "decompose_two_qubit_product_gate: unable to decompose: det_r < 0.1"}; } - llvm::transform(r, r.begin(), - [&](auto&& x) { return x / std::sqrt(det_r); }); + r /= std::sqrt(det_r); helpers::print(r, "R"); // transpose with complex conjugate of each element - matrix2x2 r_t_conj = transpose_conjugate(r); + matrix2x2 r_t_conj = r.transpose().conjugate(); - auto temp = kroneckerProduct(identityGate, r_t_conj); + auto temp = helpers::kroneckerProduct(identityGate, r_t_conj); helpers::print(temp, "TEMP (decompose_two_qubit_product_gate, 1)"); - temp = dot(special_unitary, temp); + temp = special_unitary * temp; helpers::print(temp, "TEMP (decompose_two_qubit_product_gate, 2)"); // [[a, b, c, d], // [e, f, g, h], => [[a, c], // [i, j, k, l], [i, k]] // [m, n, o, p]] - matrix2x2 l = {temp[0 * 4 + 0], temp[0 * 4 + 2], temp[2 * 4 + 0], - temp[2 * 4 + 2]}; - auto det_l = determinant(l); + matrix2x2 l{{temp(0, 0), temp(0, 2)}, {temp(2, 0), temp(2, 2)}}; + auto det_l = l.determinant(); if (std::abs(det_l) < 0.9) { throw std::runtime_error{ "decompose_two_qubit_product_gate: unable to decompose: detL < 0.9"}; } - llvm::transform(l, l.begin(), - [&](auto&& x) { return x / std::sqrt(det_l); }); + l /= std::sqrt(det_l); auto phase = std::arg(det_l) / 2.; return {l, r, phase}; @@ -485,24 +476,25 @@ struct GateDecompositionPattern final static matrix4x4 magic_basis_transform(const matrix4x4& unitary, MagicBasisTransform direction) { - using helpers::dot; - constexpr matrix4x4 B_NON_NORMALIZED = { - C_ONE, IM, C_ZERO, C_ZERO, C_ZERO, C_ZERO, IM, C_ONE, - C_ZERO, C_ZERO, IM, C_M_ONE, C_ONE, M_IM, C_ZERO, C_ZERO, + const matrix4x4 B_NON_NORMALIZED{ + {C_ONE, IM, C_ZERO, C_ZERO}, + {C_ZERO, C_ZERO, IM, C_ONE}, + {C_ZERO, C_ZERO, IM, C_M_ONE}, + {C_ONE, M_IM, C_ZERO, C_ZERO}, }; - constexpr matrix4x4 B_NON_NORMALIZED_DAGGER = { - qfp(0.5, 0.), C_ZERO, C_ZERO, qfp(0.5, 0.), - qfp(0., -0.5), C_ZERO, C_ZERO, qfp(0., 0.5), - C_ZERO, qfp(0., -0.5), qfp(0., -0.5), C_ZERO, - C_ZERO, qfp(0.5, 0.), qfp(-0.5, 0.), C_ZERO, + const matrix4x4 B_NON_NORMALIZED_DAGGER{ + {qfp(0.5, 0.), C_ZERO, C_ZERO, qfp(0.5, 0.)}, + {qfp(0., -0.5), C_ZERO, C_ZERO, qfp(0., 0.5)}, + {C_ZERO, qfp(0., -0.5), qfp(0., -0.5), C_ZERO}, + {C_ZERO, qfp(0.5, 0.), qfp(-0.5, 0.), C_ZERO}, }; helpers::print(unitary, "UNITARY in MAGIC BASIS TRANSFORM"); if (direction == MagicBasisTransform::OutOf) { - return dot(dot(B_NON_NORMALIZED_DAGGER, unitary), B_NON_NORMALIZED); + return B_NON_NORMALIZED_DAGGER * unitary * B_NON_NORMALIZED; } if (direction == MagicBasisTransform::Into) { - return dot(dot(B_NON_NORMALIZED, unitary), B_NON_NORMALIZED_DAGGER); + return B_NON_NORMALIZED * unitary * B_NON_NORMALIZED_DAGGER; } throw std::logic_error{"Unknown MagicBasisTransform direction!"}; } @@ -523,21 +515,19 @@ struct GateDecompositionPattern final auto half_theta = theta / 2.; auto cos = qfp(std::cos(half_theta), 0.); auto isin = qfp(0., -std::sin(half_theta)); - return {cos, isin, isin, cos}; + return matrix2x2{{cos, isin}, {isin, cos}}; } static matrix2x2 ry_matrix(fp theta) { auto half_theta = theta / 2.; auto cos = qfp(std::cos(half_theta), 0.); auto sin = qfp(std::sin(half_theta), 0.); - return {cos, -sin, sin, cos}; + return matrix2x2{{cos, -sin}, {sin, cos}}; } static matrix2x2 rz_matrix(fp theta) { - return {qfp{std::cos(theta / 2.), -std::sin(theta / 2.)}, - 0, - 0, - {std::cos(theta / 2.), std::sin(theta / 2.)}}; + return matrix2x2{{qfp{std::cos(theta / 2.), -std::sin(theta / 2.)}, 0}, + {0, qfp{std::cos(theta / 2.), std::sin(theta / 2.)}}}; // auto ilam2 = qfp(0., 0.5 * theta); // return {std::exp(-ilam2), C_ZERO, C_ZERO, std::exp(ilam2)}; } @@ -546,32 +536,20 @@ struct GateDecompositionPattern final const auto cosTheta = std::cos(theta / 2.); const auto sinTheta = std::sin(theta / 2.); - return {{cosTheta, - C_ZERO, - C_ZERO, - {0., -sinTheta}, - C_ZERO, - cosTheta, - {0., -sinTheta}, - C_ZERO, - C_ZERO, - {0., -sinTheta}, - cosTheta, - C_ZERO, - {0., -sinTheta}, - C_ZERO, - C_ZERO, - cosTheta}}; + return matrix4x4{{cosTheta, C_ZERO, C_ZERO, {0., -sinTheta}}, + {C_ZERO, cosTheta, {0., -sinTheta}, C_ZERO}, + {C_ZERO, {0., -sinTheta}, cosTheta, C_ZERO}, + {{0., -sinTheta}, C_ZERO, C_ZERO, cosTheta}}; } static matrix4x4 rzzMatrix(const fp theta) { const auto cosTheta = std::cos(theta / 2.); const auto sinTheta = std::sin(theta / 2.); - return {qfp{cosTheta, -sinTheta}, C_ZERO, C_ZERO, C_ZERO, C_ZERO, - {cosTheta, sinTheta}, C_ZERO, C_ZERO, C_ZERO, C_ZERO, - {cosTheta, sinTheta}, C_ZERO, C_ZERO, C_ZERO, C_ZERO, - {cosTheta, -sinTheta}}; + return matrix4x4{{qfp{cosTheta, -sinTheta}, C_ZERO, C_ZERO, C_ZERO}, + {C_ZERO, {cosTheta, sinTheta}, C_ZERO, C_ZERO}, + {C_ZERO, C_ZERO, {cosTheta, sinTheta}, C_ZERO}, + {C_ZERO, C_ZERO, C_ZERO, {cosTheta, -sinTheta}}}; } static std::array angles_from_unitary(const matrix2x2& matrix, @@ -589,18 +567,12 @@ struct GateDecompositionPattern final } static std::array params_zyz_inner(const matrix2x2& matrix) { - auto getIndex = [](auto x, auto y) { return (y * 2) + x; }; - auto determinant = [getIndex](auto&& matrix) { - return (matrix.at(getIndex(0, 0)) * matrix.at(getIndex(1, 1))) - - (matrix.at(getIndex(1, 0)) * matrix.at(getIndex(0, 1))); - }; - - auto detArg = std::arg(determinant(matrix)); + auto detArg = std::arg(matrix.determinant()); auto phase = 0.5 * detArg; - auto theta = 2. * std::atan2(std::abs(matrix.at(getIndex(1, 0))), - std::abs(matrix.at(getIndex(0, 0)))); - auto ang1 = std::arg(matrix.at(getIndex(1, 1))); - auto ang2 = std::arg(matrix.at(getIndex(1, 0))); + auto theta = + 2. * std::atan2(std::abs(matrix(1, 0)), std::abs(matrix(0, 0))); + auto ang1 = std::arg(matrix(1, 1)); + auto ang2 = std::arg(matrix(1, 0)); auto phi = ang1 + ang2 - detArg; auto lam = ang1 - ang2; return {theta, phi, lam, phase}; @@ -612,15 +584,15 @@ struct GateDecompositionPattern final } static std::array params_xyx_inner(const matrix2x2& matrix) { - auto mat_zyz = std::array{ - static_cast(0.5) * (matrix.at(0 * 2 + 0) + matrix.at(0 * 2 + 1) + - matrix.at(1 * 2 + 0) + matrix.at(1 * 2 + 1)), - static_cast(0.5) * (matrix.at(0 * 2 + 0) - matrix.at(0 * 2 + 1) + - matrix.at(1 * 2 + 0) - matrix.at(1 * 2 + 1)), - static_cast(0.5) * (matrix.at(0 * 2 + 0) + matrix.at(0 * 2 + 1) - - matrix.at(1 * 2 + 0) - matrix.at(1 * 2 + 1)), - static_cast(0.5) * (matrix.at(0 * 2 + 0) - matrix.at(0 * 2 + 1) - - matrix.at(1 * 2 + 0) + matrix.at(1 * 2 + 1)), + auto mat_zyz = matrix2x2{ + {static_cast(0.5) * + (matrix(0, 0) + matrix(0, 1) + matrix(1, 0) + matrix(1, 1)), + static_cast(0.5) * + (matrix(0, 0) - matrix(0, 1) + matrix(1, 0) - matrix(1, 1))}, + {static_cast(0.5) * + (matrix(0, 0) + matrix(0, 1) - matrix(1, 0) - matrix(1, 1)), + static_cast(0.5) * + (matrix(0, 0) - matrix(0, 1) - matrix(1, 0) + matrix(1, 1))}, }; auto [theta, phi, lam, phase] = params_zyz_inner(mat_zyz); auto new_phi = mod2pi(phi + qc::PI, 0.); @@ -633,13 +605,14 @@ struct GateDecompositionPattern final }; } - static constexpr std::array IPZ = {IM, C_ZERO, C_ZERO, M_IM}; - static constexpr std::array IPY = {C_ZERO, C_ONE, C_M_ONE, C_ZERO}; - static constexpr std::array IPX = {C_ZERO, IM, IM, C_ZERO}; + static const matrix2x2 IPZ; + static const matrix2x2 IPY; + static const matrix2x2 IPX; static matrix2x2 getSingleQubitMatrix(const QubitGateSequence::Gate& gate) { if (gate.type == qc::SX) { - return {qfp{0.5, 0.5}, qfp{0.5, -0.5}, qfp{0.5, -0.5}, qfp{0.5, 0.5}}; + return matrix2x2{{qfp{0.5, 0.5}, qfp{0.5, -0.5}}, + {qfp{0.5, -0.5}, qfp{0.5, 0.5}}}; } if (gate.type == qc::RX) { return rx_matrix(gate.parameter[0]); @@ -651,7 +624,7 @@ struct GateDecompositionPattern final return rz_matrix(gate.parameter[0]); } if (gate.type == qc::X) { - return {0, 1, 1, 0}; + return matrix2x2{{0, 1}, {1, 0}}; } if (gate.type == qc::I) { return identityGate; @@ -659,7 +632,7 @@ struct GateDecompositionPattern final if (gate.type == qc::H) { static constexpr fp SQRT2_2 = static_cast( 0.707106781186547524400844362104849039284835937688474036588L); - return {SQRT2_2, SQRT2_2, SQRT2_2, -SQRT2_2}; + return matrix2x2{{SQRT2_2, SQRT2_2}, {SQRT2_2, -SQRT2_2}}; } throw std::invalid_argument{ "unsupported gate type for single qubit matrix (" + @@ -684,11 +657,13 @@ struct GateDecompositionPattern final if (gate.qubit_id.size() == 2) { if (gate.type == qc::X) { // controlled X (CX) - if (gate.qubit_id == llvm::SmallVector{0, 1}) { - return {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0}; + if (gate.qubit_id == llvm::SmallVector{1, 0}) { + return matrix4x4{ + {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}}; } - if (gate.qubit_id == llvm::SmallVector{1, 0}) { - return {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}; + if (gate.qubit_id == llvm::SmallVector{0, 1}) { + return matrix4x4{ + {1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}, {0, 1, 0, 0}}; } } if (gate.type == qc::RZ) { @@ -716,10 +691,10 @@ struct GateDecompositionPattern final fp b; fp c; fp global_phase; - std::array K1l; - std::array K2l; - std::array K1r; - std::array K2r; + matrix2x2 K1l; + matrix2x2 K2l; + matrix2x2 K1r; + matrix2x2 K2r; Specialization specialization; EulerBasis default_euler_basis; std::optional requested_fidelity; @@ -729,24 +704,20 @@ struct GateDecompositionPattern final static TwoQubitWeylDecomposition new_inner(matrix4x4 unitary_matrix, std::optional fidelity, std::optional _specialization) { - using helpers::determinant; - using helpers::diagonal; - using helpers::dot; - using helpers::transpose; auto& u = unitary_matrix; - auto det_u = determinant(u); + auto det_u = u.determinant(); std::cerr << "DET_U: " << det_u << '\n'; auto det_pow = std::pow(det_u, static_cast(-0.25)); - llvm::transform(u, u.begin(), [&](auto&& x) { return x * det_pow; }); + u *= det_pow; helpers::print(u, "U", true); auto global_phase = std::arg(det_u) / 4.; auto u_p = magic_basis_transform(u, MagicBasisTransform::OutOf); helpers::print(u_p, "U_P", true); - auto m2 = dot(transpose(u_p), u_p); + matrix4x4 m2 = u_p.transpose() * u_p; auto default_euler_basis = EulerBasis::ZYZ; helpers::print(m2, "M2", true); - std::cerr << "DET_U after division: " << determinant(u) << '\n'; + std::cerr << "DET_U after division: " << u.determinant() << '\n'; // M2 is a symmetric complex matrix. We need to decompose it as M2 = P D // P^T where P ∈ SO(4), D is diagonal with unit-magnitude elements. @@ -765,8 +736,8 @@ struct GateDecompositionPattern final auto state = std::mt19937{2023}; std::normal_distribution dist; auto found = false; - diagonal4x4 d{{C_ZERO}}; - matrix4x4 p{{C_ZERO}}; + diagonal4x4 d = diagonal4x4::Zero(); + matrix4x4 p = matrix4x4::Zero(); for (int i = 0; i < 100; ++i) { fp rand_a; @@ -783,29 +754,18 @@ struct GateDecompositionPattern final rand_a = dist(state); rand_b = dist(state); } - std::array m2_real; - llvm::transform(m2, m2_real.begin(), [&](const qfp& val) { - return rand_a * val.real() + rand_b * val.imag(); - }); + rmatrix4x4 m2_real = rand_a * m2.real() + rand_b * m2.imag(); rmatrix4x4 p_inner_real = self_adjoint_eigen_lower(m2_real).first; - matrix4x4 p_inner; - llvm::transform(p_inner_real, p_inner.begin(), - [](auto&& x) { return qfp(x, 0.0); }); - auto d_inner = diagonal(dot(dot(transpose(p_inner), m2), p_inner)); + matrix4x4 p_inner = p_inner_real; + diagonal4x4 d_inner = (p_inner.transpose() * m2 * p_inner).diagonal(); helpers::print(d_inner, "D_INNER", true); helpers::print(p_inner, "P_INNER", true); - matrix4x4 diag_d{}; // zero initialization - diag_d[0 * 4 + 0] = d_inner[0]; - diag_d[1 * 4 + 1] = d_inner[1]; - diag_d[2 * 4 + 2] = d_inner[2]; - diag_d[3 * 4 + 3] = d_inner[3]; + matrix4x4 diag_d = d_inner.asDiagonal(); - auto compare = dot(dot(p_inner, diag_d), transpose(p_inner)); + matrix4x4 compare = p_inner * diag_d * p_inner.transpose(); helpers::print(compare, "COMPARE"); - found = llvm::all_of_zip(compare, m2, [](auto&& a, auto&& b) { - return std::abs(a - b) <= 1.0e-13; - }); + found = (compare - m2).cwiseAbs().cwiseLessOrEqual(1.0e-13).all(); if (found) { p = p_inner; d = d_inner; @@ -816,15 +776,13 @@ struct GateDecompositionPattern final throw std::runtime_error{ "TwoQubitWeylDecomposition: failed to diagonalize M2."}; } - std::array d_real; - llvm::transform(d, d_real.begin(), - [](auto&& x) { return -std::arg(x) / 2.0; }); + rdiagonal4x4 d_real = -1.0 * d.cwiseArg() / 2.0; helpers::print(d_real, "D_REAL", true); - d_real[3] = -d_real[0] - d_real[1] - d_real[2]; + d_real(3) = -d_real(0) - d_real(1) - d_real(2); std::array cs; for (std::size_t i = 0; i < cs.size(); ++i) { assert(i < d_real.size()); - cs[i] = remEuclid((d_real[i] + d_real[3]) / 2.0, qc::TAU); + cs[i] = remEuclid((d_real(i) + d_real(3)) / 2.0, qc::TAU); } helpers::print(cs, "CS", true); decltype(cs) cstemp; @@ -842,38 +800,29 @@ struct GateDecompositionPattern final helpers::print(order, "ORDER", true); std::tie(cs[0], cs[1], cs[2]) = std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; - std::tie(d_real[0], d_real[1], d_real[2]) = - std::tuple{d_real[order[0]], d_real[order[1]], d_real[order[2]]}; + std::tie(d_real(0), d_real(1), d_real(2)) = + std::tuple{d_real(order[0]), d_real(order[1]), d_real(order[2])}; helpers::print(d_real, "D_REAL (sorted)", true); // swap columns of p according to order - constexpr auto P_ROW_LENGTH = 4; auto p_orig = p; for (std::size_t i = 0; i < order.size(); ++i) { - for (std::size_t row = 0; row < P_ROW_LENGTH; ++row) { - std::swap(p[row * P_ROW_LENGTH + i], - p_orig[row * P_ROW_LENGTH + order[i]]); - } + p.col(i) = p_orig.col(order[i]); } - if (determinant(p).real() < 0.0) { - // negate last column - for (int i = 0; i < P_ROW_LENGTH; ++i) { - auto& x = p[i * P_ROW_LENGTH + P_ROW_LENGTH - 1]; - x = -x; - } + if (p.determinant().real() < 0.0) { + auto lastColumnIndex = p.cols() - 1; + p.col(lastColumnIndex) = -p.col(lastColumnIndex); } - matrix4x4 temp{}; - temp[0 * 4 + 0] = std::exp(IM * d_real[0]); - temp[1 * 4 + 1] = std::exp(IM * d_real[1]); - temp[2 * 4 + 2] = std::exp(IM * d_real[2]); - temp[3 * 4 + 3] = std::exp(IM * d_real[3]); + matrix4x4 temp = d_real.asDiagonal(); + temp *= IM; + temp = temp.exp(); helpers::print(temp, "TEMP"); helpers::print(p, "P", true); - auto k1 = magic_basis_transform(dot(dot(u_p, p), temp), - MagicBasisTransform::Into); - auto k2 = magic_basis_transform(transpose(p), MagicBasisTransform::Into); + auto k1 = + magic_basis_transform(u_p * p * temp, MagicBasisTransform::Into); + auto k2 = magic_basis_transform(p.transpose(), MagicBasisTransform::Into); auto [K1l, K1r, phase_l] = decompose_two_qubit_product_gate(k1); auto [K2l, K2r, phase_r] = decompose_two_qubit_product_gate(k2); @@ -882,28 +831,28 @@ struct GateDecompositionPattern final // Flip into Weyl chamber if (cs[0] > qc::PI_2) { cs[0] -= 3.0 * qc::PI_2; - K1l = dot(K1l, IPY); - K1r = dot(K1r, IPY); + K1l = K1l * IPY; + K1r = K1r * IPY; global_phase += qc::PI_2; } if (cs[1] > qc::PI_2) { cs[1] -= 3.0 * qc::PI_2; - K1l = dot(K1l, IPX); - K1r = dot(K1r, IPX); + K1l = K1l * IPX; + K1r = K1r * IPX; global_phase += qc::PI_2; } auto conjs = 0; if (cs[0] > qc::PI_4) { cs[0] = qc::PI_2 - cs[0]; - K1l = dot(K1l, IPY); - K2r = dot(IPY, K2r); + K1l = K1l * IPY; + K2r = IPY * K2r; conjs += 1; global_phase -= qc::PI_2; } if (cs[1] > qc::PI_4) { cs[1] = qc::PI_2 - cs[1]; - K1l = dot(K1l, IPX); - K2r = dot(IPX, K2r); + K1l = K1l * IPX; + K2r = IPX * K2r; conjs += 1; global_phase += qc::PI_2; if (conjs == 1) { @@ -912,8 +861,8 @@ struct GateDecompositionPattern final } if (cs[2] > qc::PI_2) { cs[2] -= 3.0 * qc::PI_2; - K1l = dot(K1l, IPZ); - K1r = dot(K1r, IPZ); + K1l = K1l * IPZ; + K1r = K1r * IPZ; global_phase += qc::PI_2; if (conjs == 1) { global_phase -= qc::PI; @@ -921,14 +870,14 @@ struct GateDecompositionPattern final } if (conjs == 1) { cs[2] = qc::PI_2 - cs[2]; - K1l = dot(K1l, IPZ); - K2r = dot(IPZ, K2r); + K1l = K1l * IPZ; + K2r = IPZ * K2r; global_phase += qc::PI_2; } if (cs[2] > qc::PI_4) { cs[2] -= qc::PI_2; - K1l = dot(K1l, IPZ); - K1r = dot(K1r, IPZ); + K1l = K1l * IPZ; + K1r = K1r * IPZ; global_phase -= qc::PI_2; } auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); @@ -1003,9 +952,9 @@ struct GateDecompositionPattern final 0., 0., general.global_phase, - dot(general.K1l, general.K2l), + general.K1l * general.K2l, identityGate, - dot(general.K1r, general.K2r), + general.K1r * general.K2r, identityGate, specialization, general.default_euler_basis, @@ -1027,9 +976,9 @@ struct GateDecompositionPattern final qc::PI_4, qc::PI_4, general.global_phase, - dot(general.K1l, general.K2r), + general.K1l * general.K2r, identityGate, - dot(general.K1r, general.K2l), + general.K1r * general.K2l, identityGate, specialization, general.default_euler_basis, @@ -1044,9 +993,9 @@ struct GateDecompositionPattern final qc::PI_4, qc::PI_4, global_phase + qc::PI_2, - dot(dot(general.K1l, IPZ), general.K2r), + general.K1l * IPZ * general.K2r, identityGate, - dot(dot(general.K1r, IPZ), general.K2l), + general.K1r * IPZ * general.K2l, identityGate, specialization, general.default_euler_basis, @@ -1064,19 +1013,17 @@ struct GateDecompositionPattern final // :math:`K2_l = Id`. if (specialization == Specialization::PartialSWAPEquiv) { auto closest = closest_partial_swap(a, b, c); - auto k2l_dag = transpose(general.K2l); - llvm::transform(k2l_dag, k2l_dag.begin(), - [](auto&& x) { return std::conj(x); }); + auto k2l_dag = general.K2l.transpose().conjugate(); return TwoQubitWeylDecomposition{ closest, closest, closest, general.global_phase, - dot(general.K1l, general.K2l), + general.K1l * general.K2l, identityGate, - dot(general.K1r, general.K2l), - dot(k2l_dag, general.K2r), + general.K1r * general.K2l, + k2l_dag * general.K2r, specialization, general.default_euler_basis, general.requested_fidelity, @@ -1096,19 +1043,17 @@ struct GateDecompositionPattern final // :math:`K2_l = Id` if (specialization == Specialization::PartialSWAPFlipEquiv) { auto closest = closest_partial_swap(a, b, -c); - auto k2l_dag = transpose(general.K2l); - llvm::transform(k2l_dag, k2l_dag.begin(), - [](auto&& x) { return std::conj(x); }); + auto k2l_dag = general.K2l.transpose().conjugate(); return TwoQubitWeylDecomposition{ closest, closest, -closest, general.global_phase, - dot(general.K1l, general.K2l), + general.K1l * general.K2l, identityGate, - dot(dot(dot(general.K1r, IPZ), general.K2l), IPZ), - dot(dot(dot(IPZ, k2l_dag), IPZ), general.K2r), + general.K1r * IPZ * general.K2l * IPZ, + IPZ * k2l_dag * IPZ * general.K2r, specialization, general.default_euler_basis, general.requested_fidelity, @@ -1133,10 +1078,10 @@ struct GateDecompositionPattern final 0., 0., global_phase + k2lphase + k2rphase, - dot(general.K1l, rx_matrix(k2lphi)), - dot(ry_matrix(k2ltheta), rx_matrix(k2llambda)), - dot(general.K1r, rx_matrix(k2rphi)), - dot(ry_matrix(k2rtheta), rx_matrix(k2rlambda)), + general.K1l * rx_matrix(k2lphi), + ry_matrix(k2ltheta) * rx_matrix(k2llambda), + general.K1r * rx_matrix(k2rphi), + ry_matrix(k2rtheta) * rx_matrix(k2rlambda), specialization, euler_basis, general.requested_fidelity, @@ -1161,10 +1106,10 @@ struct GateDecompositionPattern final qc::PI_4, c, global_phase + k2lphase + k2rphase, - dot(general.K1l, rz_matrix(k2rphi)), - dot(ry_matrix(k2ltheta), rz_matrix(k2llambda)), - dot(general.K1r, rz_matrix(k2lphi)), - dot(ry_matrix(k2rtheta), rz_matrix(k2rlambda)), + general.K1l * rz_matrix(k2rphi), + ry_matrix(k2ltheta) * rz_matrix(k2llambda), + general.K1r * rz_matrix(k2lphi), + ry_matrix(k2rtheta) * rz_matrix(k2rlambda), specialization, general.default_euler_basis, general.requested_fidelity, @@ -1185,10 +1130,10 @@ struct GateDecompositionPattern final (a + b) / 2., c, global_phase + k2lphase, - dot(general.K1l, rz_matrix(k2lphi)), - dot(ry_matrix(k2ltheta), rz_matrix(k2llambda)), - dot(general.K1r, rz_matrix(k2lphi)), - dot(rz_matrix(-k2lphi), general.K2r), + general.K1l * rz_matrix(k2lphi), + ry_matrix(k2ltheta) * rz_matrix(k2llambda), + general.K1r * rz_matrix(k2lphi), + rz_matrix(-k2lphi) * general.K2r, specialization, general.default_euler_basis, general.requested_fidelity, @@ -1210,10 +1155,10 @@ struct GateDecompositionPattern final (b + c) / 2., (b + c) / 2., global_phase + k2lphase, - dot(general.K1l, rx_matrix(k2lphi)), - dot(ry_matrix(k2ltheta), rx_matrix(k2llambda)), - dot(general.K1r, rx_matrix(k2lphi)), - dot(rx_matrix(-k2lphi), general.K2r), + general.K1l * rx_matrix(k2lphi), + ry_matrix(k2ltheta) * rx_matrix(k2llambda), + general.K1r * rx_matrix(k2lphi), + rx_matrix(-k2lphi) * general.K2r, specialization, euler_basis, general.requested_fidelity, @@ -1235,10 +1180,10 @@ struct GateDecompositionPattern final (b - c) / 2., -((b - c) / 2.), global_phase + k2lphase, - dot(general.K1l, rx_matrix(k2lphi)), - dot(ry_matrix(k2ltheta), rx_matrix(k2llambda)), - dot(dot(dot(general.K1r, IPZ), rx_matrix(k2lphi)), IPZ), - dot(dot(dot(IPZ, rx_matrix(-k2lphi)), IPZ), general.K2r), + general.K1l * rx_matrix(k2lphi), + ry_matrix(k2ltheta) * rx_matrix(k2llambda), + general.K1r * IPZ * rx_matrix(k2lphi) * IPZ, + IPZ * rx_matrix(-k2lphi) * IPZ * general.K2r, specialization, euler_basis, general.requested_fidelity, @@ -1295,9 +1240,7 @@ struct GateDecompositionPattern final static constexpr auto DEFAULT_FIDELITY = 1.0 - 1.0e-9; struct TwoQubitBasisDecomposer { - qc::OpType gate; - std::array gate_qubit_ids; - llvm::SmallVector gate_params; + QubitGateSequence::Gate basis_gate; fp basis_fidelity; EulerBasis euler_basis; std::optional pulse_optimize; @@ -1324,19 +1267,12 @@ struct GateDecompositionPattern final matrix2x2 q2r; public: - static TwoQubitBasisDecomposer new_inner( - qc::OpType gate = qc::X, // CX - std::array gate_qubit_ids = {0, 1}, - const llvm::SmallVector& gate_params = {}, - matrix4x4 gate_matrix = {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, - // matrix4x4 gate_matrix = {1, 0, 0, 0, 0, 1, - // 0, 0, 0, 0, 0, 1, 0, 0, 1, - 0}, // CX matrix - fp basis_fidelity = 1.0, EulerBasis euler_basis = EulerBasis::ZYZ, - std::optional pulse_optimize = std::nullopt) { - using helpers::dot; - using helpers::transpose_conjugate; - + static TwoQubitBasisDecomposer + new_inner(OneQubitGateSequence::Gate basis_gate = {.type = qc::X, + .parameter = {}, + .qubit_id = {0, 1}}, + fp basis_fidelity = 1.0, EulerBasis euler_basis = EulerBasis::ZYZ, + std::optional pulse_optimize = std::nullopt) { auto relative_eq = [](auto&& lhs, auto&& rhs, auto&& epsilon, auto&& max_relative) { // Handle same infinities @@ -1365,21 +1301,17 @@ struct GateDecompositionPattern final }; constexpr auto FRAC_1_SQRT_2 = static_cast(0.707106781186547524400844362104849039); - constexpr auto K12R_ARR = std::array{ - qfp(0., FRAC_1_SQRT_2), - qfp(FRAC_1_SQRT_2, 0.), - qfp(-FRAC_1_SQRT_2, 0.), - qfp(0., -FRAC_1_SQRT_2), + const auto K12R_ARR = matrix2x2{ + {qfp(0., FRAC_1_SQRT_2), qfp(FRAC_1_SQRT_2, 0.)}, + {qfp(-FRAC_1_SQRT_2, 0.), qfp(0., -FRAC_1_SQRT_2)}, }; - constexpr auto K12L_ARR = std::array{ - qfp(0.5, 0.5), - qfp(0.5, 0.5), - qfp(-0.5, 0.5), - qfp(0.5, -0.5), + const auto K12L_ARR = matrix2x2{ + {qfp(0.5, 0.5), qfp(0.5, 0.5)}, + {qfp(-0.5, 0.5), qfp(0.5, -0.5)}, }; auto basis_decomposer = TwoQubitWeylDecomposition::new_inner( - gate_matrix, DEFAULT_FIDELITY, std::nullopt); + getTwoQubitMatrix(basis_gate), DEFAULT_FIDELITY, std::nullopt); auto super_controlled = relative_eq(basis_decomposer.a, qc::PI_4, std::numeric_limits::epsilon(), 1e-09) && @@ -1390,82 +1322,76 @@ struct GateDecompositionPattern final // expand as Ui = Ki1.Ubasis.Ki2 auto b = basis_decomposer.b; auto temp = qfp(0.5, -0.5); - auto k11l = std::array{ - temp * (M_IM * std::exp(qfp(0., -b))), temp * std::exp(qfp(0., -b)), - temp * (M_IM * std::exp(qfp(0., b))), temp * -(std::exp(qfp(0., b)))}; - auto k11r = std::array{FRAC_1_SQRT_2 * std::exp((IM * qfp(0., -b))), - FRAC_1_SQRT_2 * -std::exp(qfp(0., -b)), - FRAC_1_SQRT_2 * std::exp(qfp(0., b)), - FRAC_1_SQRT_2 * (M_IM * std::exp(qfp(0., b)))}; - auto k32l_k21l = std::array{FRAC_1_SQRT_2 * std::cos(qfp(1., (2. * b))), - FRAC_1_SQRT_2 * (IM * std::sin((2. * b))), - FRAC_1_SQRT_2 * (IM * std::sin(2. * b)), - FRAC_1_SQRT_2 * qfp(1., -std::cos(2. * b))}; + auto k11l = matrix2x2{ + {temp * (M_IM * std::exp(qfp(0., -b))), temp * std::exp(qfp(0., -b))}, + {temp * (M_IM * std::exp(qfp(0., b))), + temp * -(std::exp(qfp(0., b)))}}; + auto k11r = matrix2x2{{FRAC_1_SQRT_2 * std::exp((IM * qfp(0., -b))), + FRAC_1_SQRT_2 * -std::exp(qfp(0., -b))}, + {FRAC_1_SQRT_2 * std::exp(qfp(0., b)), + FRAC_1_SQRT_2 * (M_IM * std::exp(qfp(0., b)))}}; + auto k32l_k21l = matrix2x2{{FRAC_1_SQRT_2 * std::cos(qfp(1., (2. * b))), + FRAC_1_SQRT_2 * (IM * std::sin((2. * b)))}, + {FRAC_1_SQRT_2 * (IM * std::sin(2. * b)), + FRAC_1_SQRT_2 * qfp(1., -std::cos(2. * b))}}; temp = qfp(0.5, 0.5); - auto k21r = std::array{ - temp * (M_IM * std::exp(qfp(0., -2. * b))), - temp * std::exp(qfp(0., -2. * b)), - temp * (IM * std::exp(qfp(0., 2. * b))), - temp * std::exp(qfp(0., 2. * b)), + auto k21r = matrix2x2{ + {temp * (M_IM * std::exp(qfp(0., -2. * b))), + temp * std::exp(qfp(0., -2. * b))}, + {temp * (IM * std::exp(qfp(0., 2. * b))), + temp * std::exp(qfp(0., 2. * b))}, }; - constexpr auto K22L_ARR = std::array{ - qfp(FRAC_1_SQRT_2, 0.), - qfp(-FRAC_1_SQRT_2, 0.), - qfp(FRAC_1_SQRT_2, 0.), - qfp(FRAC_1_SQRT_2, 0.), + const auto K22L_ARR = matrix2x2{ + {qfp(FRAC_1_SQRT_2, 0.), qfp(-FRAC_1_SQRT_2, 0.)}, + {qfp(FRAC_1_SQRT_2, 0.), qfp(FRAC_1_SQRT_2, 0.)}, }; - constexpr auto K22R_ARR = std::array{C_ZERO, C_ONE, C_M_ONE, C_ZERO}; - auto k31l = std::array{ - FRAC_1_SQRT_2 * std::exp(qfp(0., -b)), - FRAC_1_SQRT_2 * std::exp(qfp(0., -b)), - FRAC_1_SQRT_2 * -std::exp(qfp(0., b)), - FRAC_1_SQRT_2 * std::exp(qfp(0., b)), + const auto K22R_ARR = matrix2x2{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; + auto k31l = matrix2x2{ + {FRAC_1_SQRT_2 * std::exp(qfp(0., -b)), + FRAC_1_SQRT_2 * std::exp(qfp(0., -b))}, + {FRAC_1_SQRT_2 * -std::exp(qfp(0., b)), + FRAC_1_SQRT_2 * std::exp(qfp(0., b))}, }; - auto k31r = std::array{ - IM * std::exp(qfp(0., b)), - C_ZERO, - C_ZERO, - M_IM * std::exp(qfp(0., -b)), + auto k31r = matrix2x2{ + {IM * std::exp(qfp(0., b)), C_ZERO}, + {C_ZERO, M_IM * std::exp(qfp(0., -b))}, }; temp = qfp(0.5, 0.5); - auto k32r = std::array{ - temp * std::exp(qfp(0., b)), - temp * -std::exp(qfp(0., -b)), - temp * (M_IM * std::exp(qfp(0., b))), - temp * (M_IM * std::exp(qfp(0., -b))), + auto k32r = matrix2x2{ + {temp * std::exp(qfp(0., b)), temp * -std::exp(qfp(0., -b))}, + {temp * (M_IM * std::exp(qfp(0., b))), + temp * (M_IM * std::exp(qfp(0., -b)))}, }; - auto k1ld = transpose_conjugate(basis_decomposer.K1l); - auto k1rd = transpose_conjugate(basis_decomposer.K1r); - auto k2ld = transpose_conjugate(basis_decomposer.K2l); - auto k2rd = transpose_conjugate(basis_decomposer.K2r); + auto k1ld = basis_decomposer.K1l.transpose().conjugate(); + auto k1rd = basis_decomposer.K1r.transpose().conjugate(); + auto k2ld = basis_decomposer.K2l.transpose().conjugate(); + auto k2rd = basis_decomposer.K2r.transpose().conjugate(); // Pre-build the fixed parts of the matrices used in 3-part // decomposition - auto u0l = dot(k31l, k1ld); - auto u0r = dot(k31r, k1rd); - auto u1l = dot(dot(k2ld, k32l_k21l), k1ld); - auto u1ra = dot(k2rd, k32r); - auto u1rb = dot(k21r, k1rd); - auto u2la = dot(k2ld, K22L_ARR); - auto u2lb = dot(k11l, k1ld); - auto u2ra = dot(k2rd, K22R_ARR); - auto u2rb = dot(k11r, k1rd); - auto u3l = dot(k2ld, K12L_ARR); - auto u3r = dot(k2rd, K12R_ARR); + auto u0l = k31l * k1ld; + auto u0r = k31r * k1rd; + auto u1l = k2ld * k32l_k21l * k1ld; + auto u1ra = k2rd * k32r; + auto u1rb = k21r * k1rd; + auto u2la = k2ld * K22L_ARR; + auto u2lb = k11l * k1ld; + auto u2ra = k2rd * K22R_ARR; + auto u2rb = k11r * k1rd; + auto u3l = k2ld * K12L_ARR; + auto u3r = k2rd * K12R_ARR; // Pre-build the fixed parts of the matrices used in the 2-part // decomposition - auto q0l = dot(transpose_conjugate(K12L_ARR), k1ld); - auto q0r = dot(dot(transpose_conjugate(K12R_ARR), IPZ), k1rd); - auto q1la = dot(k2ld, transpose_conjugate(k11l)); - auto q1lb = dot(k11l, k1ld); - auto q1ra = dot(dot(k2rd, IPZ), transpose_conjugate(k11r)); - auto q1rb = dot(k11r, k1rd); - auto q2l = dot(k2ld, K12L_ARR); - auto q2r = dot(k2rd, K12R_ARR); + auto q0l = K12L_ARR.transpose().conjugate() * k1ld; + auto q0r = K12R_ARR.transpose().conjugate() * IPZ * k1rd; + auto q1la = k2ld * k11l.transpose().conjugate(); + auto q1lb = k11l * k1ld; + auto q1ra = k2rd * IPZ * k11r.transpose().conjugate(); + auto q1rb = k11r * k1rd; + auto q2l = k2ld * K12L_ARR; + auto q2r = k2rd * K12R_ARR; return TwoQubitBasisDecomposer{ - gate, - gate_qubit_ids, - gate_params, + basis_gate, basis_fidelity, euler_basis, pulse_optimize, @@ -1591,8 +1517,7 @@ struct GateDecompositionPattern final add_euler_decomposition(2 * i, 0); add_euler_decomposition(2 * i + 1, 1); - gates.gates.push_back( - {.type = gate, .parameter = gate_params, .qubit_id = {0, 1}}); + gates.gates.push_back(basis_gate); } add_euler_decomposition(2 * best_nbasis, 0); @@ -1604,52 +1529,47 @@ struct GateDecompositionPattern final private: [[nodiscard]] std::vector decomp0_inner(const TwoQubitWeylDecomposition& target) const { - using helpers::dot; return { - dot(target.K1r, target.K2r), - dot(target.K1l, target.K2l), + target.K1r * target.K2r, + target.K1l * target.K2l, }; } [[nodiscard]] std::vector decomp1_inner(const TwoQubitWeylDecomposition& target) const { - using helpers::dot; - using helpers::transpose_conjugate; // FIXME: fix for z!=0 and c!=0 using closest reflection (not always in // the Weyl chamber) return { - dot(transpose_conjugate(basis_decomposer.K2r), target.K2r), - dot(transpose_conjugate(basis_decomposer.K2l), target.K2l), - dot(target.K1r, transpose_conjugate(basis_decomposer.K1r)), - dot(target.K1l, transpose_conjugate(basis_decomposer.K1l)), + basis_decomposer.K2r.transpose().conjugate() * target.K2r, + basis_decomposer.K2l.transpose().conjugate() * target.K2l, + target.K1r * basis_decomposer.K1r.transpose().conjugate(), + target.K1l * basis_decomposer.K1l.transpose().conjugate(), }; } [[nodiscard]] std::vector decomp2_supercontrolled_inner( const TwoQubitWeylDecomposition& target) const { - using helpers::dot; return { - dot(q2r, target.K2r), - dot(q2l, target.K2l), - dot(dot(q1ra, rz_matrix(2. * target.b)), q1rb), - dot(dot(q1la, rz_matrix(-2. * target.a)), q1lb), - dot(target.K1r, q0r), - dot(target.K1l, q0l), + q2r * target.K2r, + q2l * target.K2l, + q1ra * rz_matrix(2. * target.b) * q1rb, + q1la * rz_matrix(-2. * target.a) * q1lb, + target.K1r * q0r, + target.K1l * q0l, }; } [[nodiscard]] std::vector decomp3_supercontrolled_inner( const TwoQubitWeylDecomposition& target) const { - using helpers::dot; return { - dot(u3r, target.K2r), - dot(u3l, target.K2l), - dot(dot(u2ra, rz_matrix(2. * target.b)), u2rb), - dot(dot(u2la, rz_matrix(-2. * target.a)), u2lb), - dot(dot(u1ra, rz_matrix(-2. * target.c)), u1rb), + u3r * target.K2r, + u3l * target.K2l, + u2ra * rz_matrix(2. * target.b) * u2rb, + u2la * rz_matrix(-2. * target.a) * u2lb, + u1ra * rz_matrix(-2. * target.c) * u1rb, u1l, - dot(target.K1r, u0r), - dot(target.K1l, u0l), + target.K1r * u0r, + target.K1l * u0l, }; } @@ -1669,7 +1589,8 @@ struct GateDecompositionPattern final return std::nullopt; } - if (gate != qc::X) { // CX + if (basis_gate.type != qc::X || + basis_gate.qubit_id.size() != 2) { // != CX if (pulse_optimize.has_value()) { throw std::runtime_error{"pulse_optimizer currently only works " "with CNOT entangling gate"}; @@ -1704,7 +1625,6 @@ struct GateDecompositionPattern final TwoQubitGateSequence get_sx_vz_2cx_efficient_euler( const std::vector& decomposition, const TwoQubitWeylDecomposition& target_decomposed) { - using helpers::dot; TwoQubitGateSequence gates{.globalPhase = target_decomposed.global_phase}; gates.globalPhase -= 2. * basis_decomposer.global_phase; @@ -1721,16 +1641,15 @@ struct GateDecompositionPattern final auto euler_q0 = get_euler_angles(0, EulerBasis::ZXZ); auto euler_q1 = get_euler_angles(1, EulerBasis::XZX); - auto euler_matrix_q0 = - dot(rx_matrix(euler_q0[0][1]), rz_matrix(euler_q0[0][0])); - euler_matrix_q0 = - dot(rz_matrix(euler_q0[0][2] + euler_q0[1][0] + qc::PI_2), - euler_matrix_q0); + matrix2x2 euler_matrix_q0 = + rx_matrix(euler_q0[0][1]) * rz_matrix(euler_q0[0][0]); + euler_matrix_q0 = rz_matrix(euler_q0[0][2] + euler_q0[1][0] + qc::PI_2) * + euler_matrix_q0; append_1q_sequence(gates, euler_matrix_q0, 0); - auto euler_matrix_q1 = - dot(rz_matrix(euler_q1[0][1]), rx_matrix(euler_q1[0][0])); + matrix2x2 euler_matrix_q1 = + rz_matrix(euler_q1[0][1]) * rx_matrix(euler_q1[0][0]); euler_matrix_q1 = - dot(rx_matrix(euler_q1[0][2] + euler_q1[1][0]), euler_matrix_q1); + rx_matrix(euler_q1[0][2] + euler_q1[1][0]) * euler_matrix_q1; append_1q_sequence(gates, euler_matrix_q1, 1); gates.gates.push_back( {.type = qc::X, .qubit_id = {0, 1}}); // TODO: mark CX somehow? @@ -1744,14 +1663,13 @@ struct GateDecompositionPattern final gates.globalPhase += qc::PI_2; gates.gates.push_back( {.type = qc::X, .qubit_id = {0, 1}}); // TODO: mark CX somehow? - euler_matrix_q0 = - dot(rx_matrix(euler_q0[2][1]), - rz_matrix(euler_q0[1][2] + euler_q0[2][0] + qc::PI_2)); - euler_matrix_q0 = dot(rz_matrix(euler_q0[2][2]), euler_matrix_q0); + euler_matrix_q0 = rx_matrix(euler_q0[2][1]) * + rz_matrix(euler_q0[1][2] + euler_q0[2][0] + qc::PI_2); + euler_matrix_q0 = rz_matrix(euler_q0[2][2]) * euler_matrix_q0; append_1q_sequence(gates, euler_matrix_q0, 0); - euler_matrix_q1 = dot(rz_matrix(euler_q1[2][1]), - rx_matrix(euler_q1[1][2] + euler_q1[2][0])); - euler_matrix_q1 = dot(rx_matrix(euler_q1[2][2]), euler_matrix_q1); + euler_matrix_q1 = rz_matrix(euler_q1[2][1]) * + rx_matrix(euler_q1[1][2] + euler_q1[2][0]); + euler_matrix_q1 = rx_matrix(euler_q1[2][2]) * euler_matrix_q1; append_1q_sequence(gates, euler_matrix_q1, 1); return gates; } @@ -1770,7 +1688,6 @@ struct GateDecompositionPattern final std::optional get_sx_vz_3cx_efficient_euler( const std::vector& decomposition, const TwoQubitWeylDecomposition& target_decomposed) { - using helpers::dot; TwoQubitGateSequence gates{.globalPhase = target_decomposed.global_phase}; gates.globalPhase -= 3. * basis_decomposer.global_phase; gates.globalPhase = remEuclid(gates.globalPhase, qc::TAU); @@ -1802,24 +1719,23 @@ struct GateDecompositionPattern final auto x02_add = x12 - euler_q0[1][0]; auto x12_is_half_pi = std::abs(x12 - qc::PI_2) < atol; - auto euler_matrix_q0 = - dot(rx_matrix(euler_q0[0][1]), rz_matrix(euler_q0[0][0])); + matrix2x2 euler_matrix_q0 = + rx_matrix(euler_q0[0][1]) * rz_matrix(euler_q0[0][0]); if (x12_is_non_zero && x12_is_pi_mult) { - euler_matrix_q0 = - dot(rz_matrix(euler_q0[0][2] - x02_add), euler_matrix_q0); + euler_matrix_q0 = rz_matrix(euler_q0[0][2] - x02_add) * euler_matrix_q0; } else { euler_matrix_q0 = - dot(rz_matrix(euler_q0[0][2] + euler_q0[1][0]), euler_matrix_q0); + rz_matrix(euler_q0[0][2] + euler_q0[1][0]) * euler_matrix_q0; } - euler_matrix_q0 = dot(hGate, euler_matrix_q0); + euler_matrix_q0 = hGate * euler_matrix_q0; append_1q_sequence(gates, euler_matrix_q0, 0); auto rx_0 = rx_matrix(euler_q1[0][0]); auto rz = rz_matrix(euler_q1[0][1]); auto rx_1 = rx_matrix(euler_q1[0][2] + euler_q1[1][0]); - auto euler_matrix_q1 = dot(rz, rx_0); - euler_matrix_q1 = dot(rx_1, euler_matrix_q1); - euler_matrix_q1 = dot(hGate, euler_matrix_q1); + matrix2x2 euler_matrix_q1 = rz * rx_0; + euler_matrix_q1 = rx_1 * euler_matrix_q1; + euler_matrix_q1 = hGate * euler_matrix_q1; append_1q_sequence(gates, euler_matrix_q1, 1); gates.gates.push_back({.type = qc::X, .qubit_id = {1, 0}}); @@ -1872,20 +1788,21 @@ struct GateDecompositionPattern final return std::nullopt; } gates.gates.push_back({.type = qc::X, .qubit_id = {1, 0}}); - auto eulerMatrix = dot(rz_matrix(euler_q0[2][2] + euler_q0[3][0]), hGate); - eulerMatrix = dot(rx_matrix(euler_q0[3][1]), eulerMatrix); - eulerMatrix = dot(rz_matrix(euler_q0[3][2]), eulerMatrix); + matrix2x2 eulerMatrix = + rz_matrix(euler_q0[2][2] + euler_q0[3][0]) * hGate; + eulerMatrix = rx_matrix(euler_q0[3][1]) * eulerMatrix; + eulerMatrix = rz_matrix(euler_q0[3][2]) * eulerMatrix; append_1q_sequence(gates, eulerMatrix, 0); - eulerMatrix = dot(rx_matrix(euler_q1[2][2] + euler_q1[3][0]), hGate); - eulerMatrix = dot(rz_matrix(euler_q1[3][1]), eulerMatrix); - eulerMatrix = dot(rx_matrix(euler_q1[3][2]), eulerMatrix); + eulerMatrix = rx_matrix(euler_q1[2][2] + euler_q1[3][0]) * hGate; + eulerMatrix = rz_matrix(euler_q1[3][1]) * eulerMatrix; + eulerMatrix = rx_matrix(euler_q1[3][2]) * eulerMatrix; append_1q_sequence(gates, eulerMatrix, 1); auto out_unitary = compute_unitary(gates, gates.globalPhase); // TODO: fix the sign problem to avoid correction here - if (std::abs(target_decomposed.unitary_matrix.at(0 * 4 + 0) - - (-out_unitary.at(0 * 4 + 0))) < atol) { + if (std::abs(target_decomposed.unitary_matrix(0, 0) - + (-out_unitary(0, 0))) < atol) { gates.globalPhase += qc::PI; } return gates; @@ -1893,24 +1810,20 @@ struct GateDecompositionPattern final matrix4x4 compute_unitary(const TwoQubitGateSequence& sequence, fp global_phase) { - using helpers::dot; auto phase = std::exp(std::complex{0, global_phase}); - matrix4x4 matrix; - matrix[0 * 4 + 0] = phase; - matrix[1 * 4 + 1] = phase; - matrix[2 * 4 + 2] = phase; - matrix[3 * 4 + 3] = phase; + matrix4x4 matrix{}; + matrix.diagonal().setConstant(phase); for (auto&& gate : sequence.gates) { matrix4x4 gate_matrix = getTwoQubitMatrix(gate); - matrix = dot(gate_matrix, matrix); + matrix = gate_matrix * matrix; } return matrix; } void append_1q_sequence(TwoQubitGateSequence& twoQubitSequence, - matrix2x2 unitary, std::uint8_t qubit) { + matrix2x2 unitary, std::size_t qubit) { std::vector target_1q_basis_list; target_1q_basis_list.push_back(euler_basis); auto sequence = unitary_to_gate_sequence_inner( @@ -2056,6 +1969,14 @@ struct GateDecompositionPattern final }; // namespace mqt::ir::opt }; +const matrix2x2 GateDecompositionPattern::identityGate = matrix2x2::Identity(); +const matrix2x2 GateDecompositionPattern::hGate{{1.0 / sqrt2, 1.0 / sqrt2}, + {1.0 / sqrt2, -1.0 / sqrt2}}; +const matrix2x2 GateDecompositionPattern::IPZ{{IM, C_ZERO}, {C_ZERO, M_IM}}; +const matrix2x2 GateDecompositionPattern::IPY{{C_ZERO, C_ONE}, + {C_M_ONE, C_ZERO}}; +const matrix2x2 GateDecompositionPattern::IPX{{C_ZERO, IM}, {IM, C_ZERO}}; + /** * @brief Populates the given pattern set with patterns for gate * decomposition. diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index b52cf42fe1..3a153248e9 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -17,18 +17,20 @@ #include #include +#include +#include #include #include namespace mqt::ir::opt { using fp = double; using qfp = std::complex; -using diagonal4x4 = std::array; -using rdiagonal4x4 = std::array; -using vector2d = std::vector; -using matrix2x2 = std::array; -using matrix4x4 = std::array; -using rmatrix4x4 = std::array; +using matrix2x2 = Eigen::Matrix2; +using matrix4x4 = Eigen::Matrix4; +using rmatrix4x4 = Eigen::Matrix4; +using diagonal4x4 = Eigen::Vector; +using rdiagonal4x4 = Eigen::Vector; +; constexpr qfp C_ZERO{0., 0.}; constexpr qfp C_ONE{1., 0.}; @@ -40,63 +42,47 @@ constexpr qfp M_IM{0., -1.}; namespace mqt::ir::opt::helpers { -// TODO: remove -template void print(std::array, N> matrix, std::string s = "", bool force = false) { - if (!force) return; - int i{}; - int n = std::sqrt(N); - if (!s.empty()) { - llvm::errs() << "=== " << s << " ===\n"; - } - for (auto&& a : matrix) { - std::cerr << std::setprecision(17) << a.real() << 'i' << a.imag() << ' '; - if (++i % n == 0) { - llvm::errs() << '\n'; - } - } - llvm::errs() << '\n'; +void print(std::size_t x) { std::cerr << x; } +void print(fp x) { std::cerr << x; } + +void print(qfp x) { + std::cerr << std::setprecision(17) << x.real() << 'i' << x.imag(); } -template void print(std::array matrix, std::string s = "", bool force = false) { - if (!force) return; - int i{}; - int n = std::sqrt(N); +// TODO: remove +template +void print(Eigen::Matrix matrix, std::string s = "", + bool force = false) { + if (!force) + return; if (!s.empty()) { llvm::errs() << "=== " << s << " ===\n"; } - for (auto&& a : matrix) { - std::cerr << std::setprecision(17) << a << ' '; - if (++i % n == 0) { - llvm::errs() << '\n'; + for (int i = 0; i < matrix.cols(); ++i) { + for (int j = 0; j < matrix.rows(); ++j) { + print(matrix(j, i)); + std::cerr << ' '; } + llvm::errs() << '\n'; } llvm::errs() << '\n'; } -template void print(std::array matrix, std::string s = "", bool force = false) { - if (!force) return; - int i{}; - int n = std::sqrt(N); +template +void print(T&& matrix, std::string s = "", bool force = false) { + if (!force) + return; if (!s.empty()) { llvm::errs() << "=== " << s << " ===\n"; } + for (auto&& a : matrix) { - std::cerr << a << ' '; - if (++i % n == 0) { - llvm::errs() << '\n'; - } + print(a); + std::cerr << ' '; } llvm::errs() << '\n'; } -inline auto flatten(const dd::TwoQubitGateMatrix& matrix) { - std::array result; - for (std::size_t i = 0; i < result.size(); ++i) { - result[i] = matrix[i / 4][i % 4]; - } - return result; -} - std::optional mlirValueToFp(mlir::Value value); template @@ -179,7 +165,8 @@ inline std::optional mlirValueToFp(mlir::Value value) { return std::nullopt; } -[[nodiscard]] inline llvm::SmallVector getParameters(UnitaryInterface op) { +[[nodiscard]] inline llvm::SmallVector +getParameters(UnitaryInterface op) { llvm::SmallVector parameters; for (auto&& param : op.getParams()) { if (auto value = helpers::mlirValueToFp(param)) { @@ -221,317 +208,24 @@ inline std::optional mlirValueToFp(mlir::Value value) { return isTwoQubitOp; } -template -T kahanSum(const std::array& values) { - auto sum = T{}; - auto c = T{}; // Compensation for lost low-order bits - - for (auto&& value : values) { - auto y = value - c; // Correct for error so far - auto t = sum + y; // Add the value to the running sum - c = (t - sum) - y; // Recompute the error - sum = t; - } - - return sum; -} - -// Modify the matrix multiplication to use Kahan summation -template -std::array matrixMultiplyWithKahan(const std::array& lhs, - const std::array& rhs) { - std::array result; - - const std::size_t n = std::sqrt(N); - for (size_t i = 0; i < n; ++i) { - for (size_t j = 0; j < n; ++j) { - std::array terms; - for (size_t k = 0; k < n; ++k) { - terms[k] = lhs[i * n + k] * rhs[k * n + j]; - } - result[i * n + j] = kahanSum(terms); - } - } - return result; -} - -template -[[nodiscard]] inline Container multiply(fp factor, Container matrix) { - llvm::transform(matrix, std::begin(matrix), - [&](auto&& x) { return factor * x; }); - return matrix; -} - -template -[[nodiscard]] inline Container multiply(qfp factor, Container matrix) { - llvm::transform(matrix, std::begin(matrix), - [&](auto&& x) { return factor * x; }); - return matrix; -} - -template -[[nodiscard]] inline auto multiply(const std::array& lhs, - const std::array& rhs) { - const int n = std::sqrt(N); - Eigen::Matrix lhsEigen; - Eigen::Matrix rhsEigen; - for (int i = 0; i < n; ++i) { - for (int j = 0; j < n; ++j) { - lhsEigen(j, i) = lhs[j * n + i]; - rhsEigen(j, i) = rhs[j * n + i]; - } - } - auto r = lhsEigen * rhsEigen; - std::array result{}; - for (int i = 0; i < n; ++i) { - for (int j = 0; j < n; ++j) { - result[j * n + i] = r(j, i); - } - } - // std::array result{}; - // const int n = std::sqrt(N); - // for (int i = 0; i < n; i++) { - // for (int j = 0; j < n; j++) { - // for (int k = 0; k < n; k++) { - // result[i * n + j] += lhs[i * n + k] * rhs[k * n + j]; - // } - // } - // } - return result; -} - template -[[nodiscard]] inline auto multiply(const std::vector& lhs, - const std::vector& rhs, int columnsLhs) { - int rowsLhs = lhs.size() / columnsLhs; - int rowsRhs = columnsLhs; - int columnsRhs = rhs.size() / rowsRhs; - assert(rowsLhs * columnsLhs == lhs.size()); - assert(rowsRhs * columnsRhs == rhs.size()); - - std::vector result(rowsLhs * columnsRhs, T{}); - for (int i = 0; i < rowsLhs; i++) { - for (int j = 0; j < columnsRhs; j++) { - for (int k = 0; k < columnsLhs; k++) { - result[i * columnsRhs + j] += - lhs[i * columnsLhs + k] * rhs[k * columnsRhs + j]; - } - } - } +inline Eigen::Matrix4 kroneckerProduct(const Eigen::Matrix2& lhs, + const Eigen::Matrix2& rhs) { + Eigen::Matrix4 result; + Eigen::KroneckerProduct kroneckerProduct{lhs, rhs}; + kroneckerProduct.evalTo(result); return result; } -template -[[nodiscard]] inline auto LUdecomposition(std::array matrix) { - std::array L{}; - std::array U{}; - int rowPermutations = 0; - - for (int i = 0; i < 4; i++) { - // --- Partial pivoting: find max row in column i --- - int pivotRow = i; - auto maxVal = matrix[i * 4 + i]; - - for (int r = i + 1; r < 4; r++) { - auto val = matrix[r * 4 + i]; - if (std::abs(val) > std::abs(maxVal)) { - maxVal = val; - pivotRow = r; - } - } - - // --- Swap rows in matrix if needed --- - if (pivotRow != i) { - for (int col = 0; col < 4; ++col) { - std::swap(matrix[i * 4 + col], matrix[pivotRow * 4 + col]); - } - ++rowPermutations; - } - - // --- Compute L matrix (column-wise) --- - for (int j = 0; j < 4; j++) { - if (j < i) - L[j * 4 + i] = 0; - else { - L[j * 4 + i] = matrix[j * 4 + i]; - for (int k = 0; k < i; k++) { - L[j * 4 + i] -= L[j * 4 + k] * U[k * 4 + i]; - } - } - } - - // --- Compute U matrix (row-wise) --- - for (int j = 0; j < 4; j++) { - if (j < i) - U[i * 4 + j] = 0; - else if (j == i) - U[i * 4 + j] = 1; // Diagonal of U is set to 1 - else { - U[i * 4 + j] = matrix[i * 4 + j] / L[i * 4 + i]; - for (int k = 0; k < i; k++) { - U[i * 4 + j] -= (L[i * 4 + k] * U[k * 4 + j]) / L[i * 4 + i]; - } - } - } - } - - return std::make_tuple(L, U, rowPermutations); -} - -template -using ValueType = typename std::remove_cvref_t::value_type; - -template -static auto diagonal(Container&& matrix) { - const int n = std::sqrt(matrix.size()); - auto result = [&]() { - using T = std::remove_cvref_t; - if constexpr (std::is_same_v && Offset == 0) { - return diagonal4x4{}; - } else if constexpr (std::is_same_v && Offset == 0) { - return rdiagonal4x4{}; - } else { - return std::vector>(n - std::abs(Offset)); - } - }(); - for (std::size_t i = 0; i < result.size(); ++i) { - auto x = Offset > 0 ? i + Offset : i; - auto y = Offset < 0 ? i - Offset : i; - result[i] = matrix[y * n + x]; - } - return result; -} - -template -auto submatrix(Container&& matrix, int rowStart, int columnStart, int numRows, - int numColumns) { - const int n = std::sqrt(matrix.size()); - assert((rowStart + numRows) <= n); - assert((columnStart + numColumns) <= n); - - std::vector> result(numRows * numColumns); - for (int i = 0; i < numColumns; ++i) { - for (int j = 0; j < numRows; ++j) { - result[j * numColumns + i] = matrix[(rowStart + j) * n + (columnStart + i)]; - } - } - return result; -} - -template -auto assignSubmatrix(Lhs&& lhs, Rhs&& rhs, int rowStart, int columnStart, - int numRows, int numColumns) { - const int n = std::sqrt(lhs.size()); - assert((rowStart + numRows) <= n); - assert((columnStart + numColumns) <= n); - assert(numColumns * numRows == rhs.size()); - - for (int i = 0; i < numColumns; ++i) { - for (int j = 0; j < numRows; ++j) { - lhs[(rowStart + j) * n + (columnStart + i)] = rhs[j * numColumns + i]; - } - } -} - -template inline auto dot(Args&&... args) { - return helpers::multiply(std::forward(args)...); -} - -template -inline auto vectorsDot(Lhs&& lhs, Rhs&& rhs) { - return std::inner_product(lhs.begin(), lhs.end(), rhs.begin(), - typename Lhs::value_type{}); -} - -template auto add(Lhs lhs, Rhs&& rhs) { - assert(lhs.size() == rhs.size()); - for (int i = 0; i < lhs.size(); ++i) { - lhs[i] += rhs[i]; - } - return lhs; -} - -inline matrix2x2 transpose(const matrix2x2& matrix) { - return {matrix[0 * 2 + 0], matrix[1 * 2 + 0], matrix[0 * 2 + 1], - matrix[1 * 2 + 1]}; -} - -template auto transpose(Container&& matrix) { - const std::size_t n = std::sqrt(matrix.size()); - auto result{matrix}; - for (size_t i = 0; i < n; ++i) { - for (size_t j = 0; j < n; ++j) { - result[j * n + i] = matrix[i * n + j]; - } - } - return result; -} - -template auto conj(T&& x) { - using U = std::remove_cvref_t; - if constexpr (std::is_same_v) { - return std::conj(x); - } else if constexpr (std::is_same_v) { - return x; - } else { - static_assert(!sizeof(U), "Unimplemented case for helpers::conj"); - } -} - -template auto conjugate(Container matrix) { - llvm::transform(matrix, matrix.begin(), [](auto&& x) { return conj(x); }); - return matrix; -} - -template -inline auto transpose_conjugate(Container&& matrix) { - auto result = transpose(matrix); - return conjugate(result); -} - -template -inline T determinant(const std::array& mat) { - const int n = std::sqrt(N); - Eigen::Matrix matEigen; - for (int i = 0; i < n; ++i) { - for (int j = 0; j < n; ++j) { - matEigen(j, i) = mat[j * n + i]; - } - } - return matEigen.determinant(); -} - -template -inline std::array from(const std::array& first_quadrant, - const std::array& second_quadrant, - const std::array& third_quadrant, - const std::array& fourth_quadrant) { - return { - first_quadrant[0 * 2 + 0], first_quadrant[0 * 2 + 1], - second_quadrant[0 * 2 + 0], second_quadrant[0 * 2 + 1], - first_quadrant[1 * 2 + 0], first_quadrant[1 * 2 + 1], - second_quadrant[1 * 2 + 0], second_quadrant[1 * 2 + 1], - third_quadrant[0 * 2 + 0], third_quadrant[0 * 2 + 1], - fourth_quadrant[0 * 2 + 0], fourth_quadrant[0 * 2 + 1], - third_quadrant[1 * 2 + 0], third_quadrant[1 * 2 + 1], - fourth_quadrant[1 * 2 + 0], fourth_quadrant[1 * 2 + 1], - }; -} - -template inline auto toArray4(const std::vector& vec) { - std::array result; - assert(vec.size() == result.size()); - for (std::size_t i = 0; i < vec.size(); ++i) { - result[i] = vec[i]; - } - return result; -} - -template -inline std::array kroneckerProduct(const std::array& lhs, - const std::array& rhs) { - return from(multiply(lhs[0 * 2 + 0], rhs), multiply(lhs[0 * 2 + 1], rhs), - multiply(lhs[1 * 2 + 0], rhs), multiply(lhs[1 * 2 + 1], rhs)); +auto self_adjoint_evd(rmatrix4x4 A) { + Eigen::SelfAdjointEigenSolver s; + std::cerr << "=EigIN==\n" << A << "\n========\n" << std::endl; + s.compute(A); // TODO: computeDirect is faster + auto vecs = s.eigenvectors().eval(); + auto vals = s.eigenvalues(); + std::cerr << "=Eigen==\n" << vecs << "\n========\n" << std::endl; + std::cerr << "=Eigen==\n" << vals << "\n========\n" << std::endl; + return std::make_pair(vecs, vals); } } // namespace mqt::ir::opt::helpers diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/a.h b/mlir/lib/Dialect/MQTOpt/Transforms/a.h deleted file mode 100644 index 57e4678a34..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/a.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include "Helpers.h" - -namespace mqt::ir::opt { - -// Function to compute the norm (Frobenius norm) of a matrix -double norm(const std::array& A) { - double sum = 0.0; - for (size_t i = 0; i < 16; ++i) { - sum += A[i] * A[i]; - } - return sqrt(sum); -} - -// Function to perform a Jacobi rotation -void jacobi_rotate(std::array& A, std::array& V, int p, int q) { - // Compute the Jacobi rotation matrix - double theta = 0.5 * atan2(2 * A[p * 4 + q], A[q * 4 + q] - A[p * 4 + p]); - double c = cos(theta); - double s = sin(theta); - - // Apply the rotation to matrix A - for (int i = 0; i < 4; ++i) { - double ap = A[i * 4 + p]; - double aq = A[i * 4 + q]; - A[i * 4 + p] = c * ap - s * aq; - A[i * 4 + q] = s * ap + c * aq; - } - - // Apply the rotation to matrix V (eigenvectors) - for (int i = 0; i < 4; ++i) { - double vi_p = V[i * 4 + p]; - double vi_q = V[i * 4 + q]; - V[i * 4 + p] = c * vi_p - s * vi_q; - V[i * 4 + q] = s * vi_p + c * vi_q; - } -} - -// Function to perform the Jacobi method for eigenvalue decomposition -void jacobi_eigen_decomposition(std::array& A, std::array& V, std::array& eigenvalues, double tolerance = 1e-9, int max_iterations = 1000) { - // Initialize the eigenvector matrix V to identity - V.fill(0.0); - for (int i = 0; i < 4; ++i) { - V[i * 4 + i] = 1.0; - } - - int iterations = 0; - double off_diagonal_norm = norm(A); - - // Jacobi rotation iterations - while (off_diagonal_norm > tolerance && iterations < max_iterations) { - // Find the largest off-diagonal element - double max_off_diag = 0.0; - int p = 0, q = 0; - for (int i = 0; i < 3; ++i) { - for (int j = i + 1; j < 4; ++j) { - double abs_val = fabs(A[i * 4 + j]); - if (abs_val > max_off_diag) { - max_off_diag = abs_val; - p = i; - q = j; - } - } - } - - // Perform a Jacobi rotation if necessary - if (max_off_diag > tolerance) { - jacobi_rotate(A, V, p, q); - } - - // Update the off-diagonal norm - off_diagonal_norm = norm(A); - ++iterations; - } - - // Extract the eigenvalues from the diagonal of A - for (int i = 0; i < 4; ++i) { - eigenvalues[i] = A[i * 4 + i]; - } -} -} diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/b.h b/mlir/lib/Dialect/MQTOpt/Transforms/b.h deleted file mode 100644 index 3d610ff96d..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/b.h +++ /dev/null @@ -1,469 +0,0 @@ -#pragma once - -#include "Helpers.h" - -namespace mqt::ir::opt { -void tridiagonalization_inplace(rmatrix4x4& mat, rdiagonal4x4& hCoeffs) { - auto n = 4; - - auto makeHouseholder = [](llvm::SmallVector& essential, fp& tau, - fp& beta) { - std::vector tail{essential.begin(), essential.end()}; - - auto squaredNorm = [](auto&& v) { - qfp sum{}; - for (auto&& x : v) { - sum += qfp(std::real(x) * std::real(x), std::imag(x) * std::imag(x)); - } - return sum.real() + sum.imag(); - }; - - auto tailSqNorm = essential.size() == 1 ? 0.0 : squaredNorm(tail); - fp c0 = essential[0]; - const fp tol = (std::numeric_limits::min)(); - - if (tailSqNorm <= tol && std::norm(std::imag(c0)) <= tol) { - tau = 0; - beta = std::real(c0); - llvm::fill(essential, 0); - } else { - beta = std::sqrt(std::norm(c0) + tailSqNorm); - if (std::real(c0) >= 0.0) { - beta = -beta; - } - for (std::size_t i = 0; i < essential.size(); ++i) { - essential[i] = tail[i] / (c0 - beta); - } - tau = helpers::conj((beta - c0) / beta); - } - }; - - auto lowerSelfadjointView = [](auto matrix) { - const int n = std::sqrt(matrix.size()); - for (int i = 0; i < n; ++i) { - for (int j = 0; j <= i; ++j) { - matrix[j * n + i] = matrix[i * n + j]; - } - } - return matrix; - }; - - auto bottomRightCorner = [](auto&& matrix, int rows, int columns) { - const int n = std::sqrt(matrix.size()); - return helpers::submatrix(matrix, n - rows, n - columns, rows, columns); - }; - - auto getColumn = [](const rmatrix4x4& matrix, int column) { - rdiagonal4x4 result; - for (int j = 0; j < 4; ++j) { - result[j] = matrix[j * 4 + column]; - } - return result; - }; - - auto getTail = [](auto&& array, int size) { - std::vector result(size); - for (int i = 0; i < size; ++i) { - result[i] = array[array.size() - size + i]; - } - return result; - }; - - auto rankUpdate = [](auto matrix, auto&& u, auto&& v, auto&& alpha) { - rmatrix4x4 add1{}; - for (int i = 0; i < u.size(); ++i) { - for (int j = 0; j < v.size(); ++j) { - add1[j * 4 + i] += alpha * u[i] * helpers::conj(v[j]); - } - } - rmatrix4x4 add2{}; - for (int i = 0; i < v.size(); ++i) { - for (int j = 0; j < u.size(); ++j) { - add2[j * 4 + i] = helpers::conj(alpha) * v[i] * helpers::conj(u[j]); - } - } - for (int i = 0; i < matrix.size(); ++i) { - matrix[i] += add1[i] + add2[i]; - } - return matrix; - }; - - for (auto i = 0; i < n - 1; ++i) { - auto remainingSize = n - i - 1; - fp beta; - fp h; - - // matA.col(i).tail(remainingSize).makeHouseholderInPlace(h, beta); - llvm::SmallVector tmp; - for (int j = n - remainingSize; j < n; ++j) { - tmp.push_back(mat[j * n + i]); - } - makeHouseholder(tmp, h, beta); - - // Apply similarity transformation to remaining columns, - // i.e., A = H A H' where H = I - h v v' and v = matA.col(i).tail(n-i-1) - // matA.col(i).coeffRef(i + 1) = fp(1); - mat[(i + 1) * n + i] = 1.0; - - // hCoeffs.tail(n - i - 1).noalias() = - // (matA.bottomRightCorner(remainingSize, remainingSize).template - // selfadjointView() * - // (conj(h) * matA.col(i).tail(remainingSize))); - auto tmp2 = helpers::multiply( - lowerSelfadjointView( - bottomRightCorner(mat, remainingSize, remainingSize)), - helpers::multiply(helpers::conj(h), - getTail(getColumn(mat, i), remainingSize)), - remainingSize); - for (int a = 0; a < remainingSize; ++a) { - hCoeffs[i + 1 + a] = tmp2[a]; - } - - // hCoeffs.tail(n - i - 1) += - // (conj(h) * RealScalar(-0.5) * - // (hCoeffs.tail(remainingSize).dot(matA.col(i).tail(remainingSize)))) - // * matA.col(i).tail(n - i - 1); - auto tmpFactor = - helpers::conj(h) * static_cast(-0.5) * - helpers::vectorsDot(getTail(hCoeffs, remainingSize), - getTail(getColumn(mat, i), remainingSize)); - tmp2 = helpers::multiply(tmpFactor, - helpers::submatrix(mat, i + 1, i, n - i - 1, 1)); - for (int a = 0; a < remainingSize; ++a) { - hCoeffs[i + 1 + a] += tmp2[a]; - } - - // matA.bottomRightCorner(remainingSize, remainingSize) - // .template selfadjointView() - // .rankUpdate(matA.col(i).tail(remainingSize), - // hCoeffs.tail(remainingSize), Scalar(-1)); - auto updatedMatrix = bottomRightCorner(mat, remainingSize, remainingSize); - updatedMatrix = lowerSelfadjointView(updatedMatrix); - updatedMatrix = rankUpdate( - updatedMatrix, helpers::submatrix(mat, n - remainingSize, i, remainingSize, 1), - std::vector{hCoeffs.begin() + (n - remainingSize), hCoeffs.end()}, - -1.0); - // update bottom right corner - helpers::assignSubmatrix(mat, updatedMatrix, n - remainingSize, - n - remainingSize, remainingSize, remainingSize); - - // matA.col(i).coeffRef(i + 1) = beta; - mat[(i + 1) * n + i] = beta; - // hCoeffs.coeffRef(i) = h; - hCoeffs[i] = h; - } -} - -void tridiagonal_qr_step(rdiagonal4x4& diag, std::vector& subdiag, - int start, int end, rmatrix4x4& matrixQ) { - // Wilkinson Shift. - auto td = (diag[end - 1] - diag[end]) * static_cast(0.5); - auto e = subdiag[end - 1]; - // Note that thanks to scaling, e^2 or td^2 cannot overflow, however they can - // still underflow thus leading to inf/NaN values when using the following - // commented code: - // RealScalar e2 = numext::abs2(subdiag[end-1]); - // RealScalar mu = diag[end] - e2 / (td + (td>0 ? 1 : -1) * sqrt(td*td + - // e2)); - // This explain the following, somewhat more complicated, version: - auto mu = diag[end]; - if (td == 0.0) { - mu -= std::abs(e); - } else if (e != 0.0) { - const auto e2 = std::norm(e); - const auto h = std::hypot(td, e); - if (e2 == 0.0) { - mu -= e / ((td + (td > static_cast(0) ? h : -h)) / e); - } else { - mu -= e2 / (td + (td > static_cast(0) ? h : -h)); - } - } - - auto x = diag[start] - mu; - auto z = subdiag[start]; - // If z ever becomes zero, the Givens rotation will be the identity and - // z will stay zero for all future iterations. - for (int k = start; k < end && z != 0.0; ++k) { - struct JacobiRotation { - fp c; - fp s; - - void makeGivens(fp p, fp q) { - if (q == 0.0) { - c = p < 0 ? -1 : 1; - s = 0; - } else if (p == 0.0) { - c = 0; - s = q < 0 ? 1 : -1; - } else if (std::abs(p) > std::abs(q)) { - auto t = q / p; - auto u = std::sqrt(1.0 + std::norm(t)); - if (p < 0.0) - u = -u; - c = 1.0 / u; - s = -t * c; - } else { - auto t = p / q; - auto u = std::sqrt(1.0 + std::norm(t)); - if (q < 0) - u = -u; - s = -1.0 / u; - c = -t * s; - } - } - - void applyOnTheRight(rmatrix4x4& matrix, int p, int q) { - const int n = std::sqrt(matrix.size()); - auto x = helpers::submatrix(matrix, 0, p, n, 1); - auto y = helpers::submatrix(matrix, 0, q, n, 1); - auto j = *this; - j.transpose(); - - if (j.c == 0.0 && j.s == 0.0) { - return; - } - - for (int i = 0; i < n; ++i) { - auto xi = x[i]; - auto yi = y[i]; - x[i] = j.c * xi + helpers::conj(j.s) * yi; - y[i] = -j.s * xi + helpers::conj(j.c) * yi; - } - - helpers::assignSubmatrix(matrix, x, 0, p, n, 1); - helpers::assignSubmatrix(matrix, y, 0, q, n, 1); - } - - void transpose() { s = -helpers::conj(s); } - } rot; - rot.makeGivens(x, z); - - // do T = G' T G - auto sdk = rot.s * diag[k] + rot.c * subdiag[k]; - auto dkp1 = rot.s * subdiag[k] + rot.c * diag[k + 1]; - - diag[k] = rot.c * (rot.c * diag[k] - rot.s * subdiag[k]) - - rot.s * (rot.c * subdiag[k] - rot.s * diag[k + 1]); - diag[k + 1] = rot.s * sdk + rot.c * dkp1; - subdiag[k] = rot.c * sdk - rot.s * dkp1; - - if (k > start) - subdiag[k - 1] = rot.c * subdiag[k - 1] - rot.s * z; - - // "Chasing the bulge" to return to triangular form. - x = subdiag[k]; - if (k < end - 1) { - z = -rot.s * subdiag[k + 1]; - subdiag[k + 1] = rot.c * subdiag[k + 1]; - } - - // apply the givens rotation to the unit matrix Q = Q * G - rot.applyOnTheRight(matrixQ, k, k + 1); - } -} - -void computeFromTridiagonal_impl(rdiagonal4x4& diag, std::vector& subdiag, - const int maxIterations, - bool computeEigenvectors, rmatrix4x4& eivec) { - auto n = diag.size(); - auto end = n - 1; - int start = 0; - int iter = 0; // total number of iterations - - constexpr auto considerAsZero = (std::numeric_limits::min)(); - const auto precision_inv = - static_cast(1.0) / std::numeric_limits::epsilon(); - while (end > 0) { - for (int i = start; i < end; ++i) { - if (std::abs(subdiag[i]) < considerAsZero) { - subdiag[i] = static_cast(0); - } else { - // abs(subdiag[i]) <= epsilon * sqrt(abs(diag[i]) + abs(diag[i+1])) - // Scaled to prevent underflows. - const auto scaled_subdiag = precision_inv * subdiag[i]; - if (scaled_subdiag * scaled_subdiag <= - (std::abs(diag[i]) + std::abs(diag[i + 1]))) { - subdiag[i] = static_cast(0); - } - } - } - - // find the largest unreduced block at the end of the matrix. - while (end > 0 && subdiag[end - 1] == 0.0) { - end--; - } - if (end <= 0) { - break; - } - - // if we spent too many iterations, we give up - iter++; - if (iter > maxIterations * n) { - break; - } - - start = end - 1; - while (start > 0 && subdiag[start - 1] != 0.0) { - start--; - } - - tridiagonal_qr_step(diag, subdiag, start, end, eivec); - } - // Sort eigenvalues and corresponding vectors. - // TODO make the sort optional ? - // TODO use a better sort algorithm !! - if (iter > maxIterations * n) { - throw std::runtime_error{"No convergence for eigenvalue decomposition!"}; - } - for (int i = 0; i < n - 1; ++i) { - // diag.segment(i, n - i).minCoeff(&k); - int k = std::distance(diag.begin() + i, - std::min_element(diag.begin() + i, diag.end())); - if (k > 0 && k < n - i) { - std::swap(diag[i], diag[k + i]); - if (computeEigenvectors) { - for (int j = 0; j < n; ++j) { - std::swap(eivec[j * n + i], eivec[j * n + (k + i)]); - } - } - } - } -} - -void householderSequenceEval(rmatrix4x4& m_vectors, - const rdiagonal4x4& m_coeffs, int length, - int shift) { - auto essentialVector = [&](const rmatrix4x4& vectors, int k) { - int start = k + 1 + shift; - return helpers::submatrix(vectors, start, k, 4 - start, 1); - }; - - auto applyThisOnTheLeft = [&](auto&& vectors, auto& dst) { - const auto n = std::sqrt(dst.size()); - for (int k = 0; k < length; ++k) { - int actual_k = n - k - 1; - int dstRows = n - shift - actual_k; - - // TODO - // auto sub_dst = dst.bottomRows(dstRows); - // sub_dst.applyHouseholderOnTheLeft(essentialVector(vectors, actual_k), - // m_coeffs[actual_k]); - } - }; - - auto applyHouseholderOnTheLeft = [](std::vector& matrix, - const std::vector& essential, - const fp& tau) { - const int n = std::sqrt(matrix.size()); - if (tau != 0.0) { - auto firstRow = helpers::submatrix(matrix, 0, 0, 1, n); - auto bottom = helpers::submatrix(matrix, 1, 0, n - 1, n); - - auto tmp = helpers::multiply(helpers::transpose_conjugate(essential), - bottom, essential.size()); - tmp = helpers::add(tmp, firstRow); - - auto tmp2 = helpers::add( - firstRow, helpers::multiply(-1.0, helpers::multiply(tau, tmp))); - // insert first row (first 4 elements) - llvm::copy(tmp2, matrix.begin()); - - tmp2 = helpers::add( - bottom, helpers::multiply(-tau, helpers::multiply(essential, tmp, - 1))); - // insert all rows except first row - llvm::copy(tmp2, matrix.begin() + n); - } - }; - - auto applyHouseholderOnTheRight = [](std::vector& matrix, - const std::vector& essential, - const fp& tau) { - const int n = std::sqrt(matrix.size()); - if (tau != 0.0) { - auto firstColumn = helpers::submatrix(matrix, 0, 0, n, 1); - auto right = helpers::submatrix(matrix, 0, 1, n, n - 1); - - auto tmp = helpers::multiply(right, essential, n - 1); - tmp = helpers::add(tmp, firstColumn); - - auto tmp2 = helpers::add(firstColumn, helpers::multiply(-tau, tmp)); - auto tmp3 = helpers::add( - right, - helpers::multiply( - -tau, helpers::multiply( - tmp, helpers::transpose_conjugate(essential), 1))); - for (int i = 0; i < n; ++i) { - for (int j = 0; j < n; ++j) { - if (i == 0) { - // insert first column (first 4 elements) - matrix[j * n + i] = tmp2[j]; - } else { - // insert all except first column - matrix[j * n + i] = tmp3[j * (n - 1) + i]; - } - } - } - } - }; - - rmatrix4x4 dst; - const int n = std::sqrt(m_vectors.size()); - const int vecs = length; - dst = helpers::kroneckerProduct(std::array{1.0, 0.0, 0.0, 1.0}, - {1.0, 0.0, 0.0, 1.0}); - for (int k = vecs - 1; k >= 0; --k) { - int cornerSize = n - k - shift; - auto bottomRightCorner = helpers::submatrix( - dst, n - cornerSize, n - cornerSize, cornerSize, cornerSize); - applyHouseholderOnTheLeft(bottomRightCorner, essentialVector(m_vectors, k), - m_coeffs[k]); - // update bottom right corner - helpers::assignSubmatrix(dst, bottomRightCorner, n - cornerSize, - n - cornerSize, cornerSize, cornerSize); - } -} - -// https://docs.rs/faer/latest/faer/mat/generic/struct.Mat.html#method.self_adjoint_eigen -inline std::pair self_adjoint_evd(rmatrix4x4 matrix) { - - auto lowerTriangularView = [](auto matrix) { - const int n = std::sqrt(matrix.size()); - for (int i = 0; i < n; ++i) { - for (int j = 0; j <= i; ++j) { - matrix[j * n + i] = 0.0; - } - } - return matrix; - }; - - rdiagonal4x4 eigenvalues{1}; - rmatrix4x4 eigenvectors; - int n = 4; - - // map the matrix coefficients to [-1:1] to avoid over- and underflow. - eigenvectors = lowerTriangularView(matrix); - fp scale = *llvm::max_element(eigenvalues, [](auto&& a, auto&& b) { - return std::abs(a) < std::abs(b); - }); - if (scale == 0.0) - scale = static_cast(1.0); - for (auto& eigenvector : eigenvectors) { - eigenvector /= scale; - } - rdiagonal4x4 hCoeffs; - tridiagonalization_inplace(eigenvectors, hCoeffs); - eigenvalues = helpers::diagonal(eigenvectors); - auto subdiag = helpers::diagonal<-1>(eigenvectors); - householderSequenceEval(eigenvectors, helpers::conjugate(hCoeffs), n - 1, 1); - - computeFromTridiagonal_impl(eigenvalues, subdiag, 1000, true, eigenvectors); - - // scale back the eigen values - for (auto& eigenvalue : eigenvalues) { - eigenvalue *= scale; - } - - return {eigenvectors, eigenvalues}; -} -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/c.h b/mlir/lib/Dialect/MQTOpt/Transforms/c.h deleted file mode 100644 index 315ed0a69d..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/c.h +++ /dev/null @@ -1,142 +0,0 @@ -#pragma once - -#include "Helpers.h" - -namespace mqt::ir::opt { -// return Q, R such that A = Q * R -static void qrDecomposition(const rmatrix4x4& A, rmatrix4x4& Q, rmatrix4x4& R) { - // array of factor Q1, Q2, ... Qm - std::vector qv(4); - - // temp array - auto z(A); - rmatrix4x4 z1; - - auto vmadd = [](const auto& a, const auto& b, double s, auto& c) { - for (int i = 0; i < 4; i++) - c[i] = a[i] + s * b[i]; - }; - - auto compute_householder_factor = [](rmatrix4x4& mat, const rdiagonal4x4& v) { - for (int i = 0; i < 4; i++) - for (int j = 0; j < 4; j++) - mat[i + 4 * j] = -2.0 * v[i] * v[j]; - for (int i = 0; i < 4; i++) - mat[i * 4 + i] += 1; - }; - - // take c-th column of a matrix, put results in Vector v - auto extract_column = [](const rmatrix4x4& m, rdiagonal4x4& v, int c) { - for (int i = 0; i < 4; i++) - v[i] = m[i + 4 * c]; - }; - - auto compute_minor = [](rmatrix4x4& lhs, const rmatrix4x4& rhs, int d) { - for (int i = 0; i < d; i++) - lhs[i * 4 + i] = 1.0; - for (int i = d; i < 4; i++) - for (int j = d; j < 4; j++) - lhs[i + 4 * j] = rhs[i + 4 * j]; - }; - - auto norm = [](auto&& m) { - double sum = 0; - for (int i = 0; i < m.size(); i++) - sum += m[i] * m[i]; - return sqrt(sum); - }; - - auto rescale_unit = [&](auto& m) { - auto factor = norm(m); - for (int i = 0; i < m.size(); i++) - m[i] /= factor; - }; - - for (int k = 0; k < 4 && k < 4 - 1; k++) { - - rdiagonal4x4 e{}, x{}; - double a{}; - - // compute minor - compute_minor(z1, z, k); - - // extract k-th column into x - extract_column(z1, x, k); - - a = norm(x); - if (A[k * 4 + k] > 0) - a = -a; - - for (int i = 0; i < 4; i++) - e[i] = (i == k) ? 1 : 0; - - // e = x + a*e - vmadd(x, e, a, e); - - // e = e / ||e|| - rescale_unit(e); - - // qv[k] = I - 2 *e*e^T - compute_householder_factor(qv[k], e); - - // z = qv[k] * z1 - z = helpers::multiply(qv[k], z1); - } - - Q = qv[0]; - - // after this loop, we will obtain Q (up to a transpose operation) - for (int i = 1; i < 4 && i < 4 - 1; i++) { - - z1 = helpers::multiply(qv[i], Q); - Q = z1; - } - - R = helpers::multiply(Q, A); - Q = helpers::transpose(Q); -} - -// Function to perform self-adjoint eigenvalue decomposition (4x4 matrix) -static rmatrix4x4 // eigenvectors (4x4) -self_adjoint_evd(rmatrix4x4 A, // input symmetric matrix (4x4) - rdiagonal4x4& s // eigenvalues -) { - rmatrix4x4 U = {1, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 1}; // Start with identity - - auto isConverged = [](const rmatrix4x4& A, double tol = 1e-10) -> bool { - double sum = 0.0; - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 4; ++j) { - if (i != j) - sum += A[i + 4 * j] * A[i + 4 * j]; - } - } - return std::sqrt(sum) < tol; - }; - - rmatrix4x4 Q{}; - rmatrix4x4 R{}; - - constexpr auto maxIters = 100; - for (int iter = 0; iter < maxIters; ++iter) { - qrDecomposition(A, Q, R); - - // A = R * Q - A = helpers::multiply(R, Q); - - // eigenVectors = eigenVectors * Q - U = helpers::multiply(U, Q); - - if (isConverged(A)) { - break; - } - } - - for (int i = 0; i < 4; ++i) { - s[i] = A[i * 4 + i]; - } - return U; -} - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/d.h b/mlir/lib/Dialect/MQTOpt/Transforms/d.h deleted file mode 100644 index 6d2a849f50..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/d.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "Helpers.h" - -#include - -namespace mqt::ir::opt { - -auto self_adjoint_evd(rmatrix4x4 A) { - arma::Mat a(4, 4, arma::fill::scalar_holder(0.0)); - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 4; ++j) { - a.at(j, i) = A[j * 4 + i]; - } - } - arma::Mat vecs; - arma::Col vals; - arma::eig_sym(vals, vecs, a, "std"); - rmatrix4x4 rvecs; - rdiagonal4x4 rvals; - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 4; ++j) { - rvecs[j * 4 + i] = vecs.at(j, i); - } - rvals[i] = vals.at(i); - } - std::cerr << "========\n" << vecs << "========\n" << std::endl; - std::cerr << "========\n" << vals << "========\n" << std::endl; - return std::make_pair(rvecs, rvals); -} -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/e.h b/mlir/lib/Dialect/MQTOpt/Transforms/e.h deleted file mode 100644 index d407422a0b..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/e.h +++ /dev/null @@ -1,976 +0,0 @@ -// -// Analytical Eigen-decomposition for 4x4 and 3x3 matrices -// -// -// Robin Straebler - George Terzakis -// -// University of Portsmouth 2016 -// - -#ifndef __EIGENDECOMPOSE_H__ -#define __EIGENDECOMPOSE_H__ - -#include "Helpers.h" - -#include -#include -#include -#include -#include -#include - -// The 4x4 implies that the namespace contains algorithms -// for the eigen decomposition of 3x3 or 4x4 matrices. -// -namespace mqt::ir::opt { - -namespace PolySolvers { - -// see http://mathworld.wolfram.com/QuadraticFormula.html -template inline std::vector

SolveQuadratic(P a, P b, P c) { - P delta = b * b - 4 * a * c; - P inv_2a, sqrt_delta; - P x1, x0; - - if (delta < 0) - return std::vector

(); - if (a == 0) { - // solve first order system - x1 = 0; - if (b != 0) { - x0 = -c / b; - return std::vector

({x0}); - } - - x0 = 0; - return std::vector

(); - } - - inv_2a = 0.5 / a; - if (delta == 0) { - x0 = -b * inv_2a; - x1 = x0; - return std::vector

({x0}); - } - - sqrt_delta = sqrt(delta); - x0 = (-b + sqrt_delta) * inv_2a; - x1 = (-b - sqrt_delta) * inv_2a; - return std::vector

({x0, x1}); -} - -/* see http://mathworld.wolfram.com/CubicEquation.html */ -template inline std::vector

SolveCubic(P a, P b, P c, P d) { - P inv_a, b_a, b_a2, c_a, d_a; - P Q, R, Q3, D, b_a_3; - P AD, BD; - - P x0, x1, x2; - - if (a == 0) { - /* solve second order system */ - if (b == 0) { - /* solve first order system */ - if (c == 0) - return std::vector

(); - - x0 = -d / c; - return std::vector

({x0}); - } - - x2 = 0; - return SolveQuadratic

(b, c, d); - } - - /* calculate the normalized form x^3 + a2 * x^2 + a1 * x + a0 = 0 */ - inv_a = 1.0 / a; - b_a = inv_a * b; - b_a2 = b_a * b_a; - c_a = inv_a * c; - d_a = inv_a * d; - - /* solve the cubic equation */ - Q = (3 * c_a - b_a2) / 9; - R = (9 * b_a * c_a - 27 * d_a - 2 * b_a * b_a2) / 54; - Q3 = Q * Q * Q; - D = Q3 + R * R; - b_a_3 = (1.0 / 3.0) * b_a; - - if (Q == 0) { - if (R == 0) { - x0 = x1 = x2 = -b_a_3; - return std::vector

({x0, x1, x2}); - } else { - x0 = pow(2 * R, 1 / 3.0) - b_a_3; - return std::vector

({x0}); - } - } - - if (D <= 0) { - /* three real roots */ - P theta = acos(R / sqrt(-Q3)); - P sqrt_Q = sqrt(-Q); - x0 = 2 * sqrt_Q * cos(theta / 3.0) - b_a_3; - x1 = 2 * sqrt_Q * cos((theta + 2 * M_PI) / 3.0) - b_a_3; - x2 = 2 * sqrt_Q * cos((theta + 4 * M_PI) / 3.0) - b_a_3; - - return std::vector

({x0, x1, x2}); - } - - /* D > 0, only one real root */ - AD = pow(fabs(R) + sqrt(D), 1.0 / 3.0) * (R > 0 ? 1 : (R < 0 ? -1 : 0)); - BD = (AD == 0) ? 0 : -Q / AD; - - /* calculate the sole real root */ - x0 = AD + BD - b_a_3; - - return std::vector

({x0}); -} - -/* see http://mathworld.wolfram.com/QuarticEquation.html */ -template -inline std::vector

SolveQuartic(P a, P b, P c, P d, P e) { - P inv_a, b2, bc, b3, b_4; - P r0; - int nb_real_roots; - P R2, R_2, R, inv_R; - P D2, E2; - - P x0 = 0, x1 = 0, x2 = 0, x3 = 0; - - if (a == 0) { - x3 = 0; - return SolveCubic

(b, c, d, e); - } - - /* normalize coefficients */ - inv_a = 1.0 / a; - b *= inv_a; - c *= inv_a; - d *= inv_a; - e *= inv_a; - b2 = b * b; - bc = b * c; - b3 = b2 * b; - - /* solve resultant cubic */ - auto solution3 = - SolveCubic

(1, -c, d * b - 4 * e, 4 * c * e - d * d - b2 * e); - int n = (solution3.size() == 0) ? 0 : solution3.size(); - - if (n == 0) - return solution3; - - r0 = solution3[0]; - /* calculate R^2 */ - R2 = 0.25 * b2 - c + r0; - if (R2 < 0) - return std::vector

(); - - R = sqrt(R2); - inv_R = 1.0 / R; - - nb_real_roots = 0; - - /* calculate D^2 and E^2 */ - if (R < 10E-12) { - P temp = r0 * r0 - 4 * e; - if (temp < 0) { - D2 = E2 = -1; - } else { - P sqrt_temp = sqrt(temp); - D2 = 0.75 * b2 - 2 * c + 2 * sqrt_temp; - E2 = D2 - 4 * sqrt_temp; - } - } else { - P u = 0.75 * b2 - 2 * c - R2, v = 0.25 * inv_R * (4 * bc - 8 * d - b3); - D2 = u + v; - E2 = u - v; - } - - b_4 = 0.25 * b; - R_2 = 0.5 * R; - if (D2 >= 0) { - P D = sqrt(D2); - P D_2 = 0.5 * D; - nb_real_roots = 2; - x0 = R_2 + D_2 - b_4; - x1 = x0 - D; - } - - /* calculate E^2 */ - if (E2 >= 0) { - P E = sqrt(E2); - P E_2 = 0.5 * E; - if (nb_real_roots == 0) { - x0 = -R_2 + E_2 - b_4; - x1 = x0 - E; - nb_real_roots = 2; - } else { - x2 = -R_2 + E_2 - b_4; - x3 = x2 - E; - nb_real_roots = 4; - } - } - switch (nb_real_roots) { - // case 0: - // return new Tuple(0, null); - // break; // covered by the "default" case - case 1: - return std::vector

({x0}); - // break; - case 2: - return std::vector

({x0, x1}); - // break; - case 3: - return std::vector

({x0, x1, x2}); - // break; - case 4: - return std::vector

({x0, x1, x2, x3}); - // break; - default: - return std::vector

(); // just to shut the compiler up.... - // break; - } -} - -} // end namespace PolySolvers - -///

-/// This function performs two steps of Gauss-Jordan elmination in a 3x3 matrix. -/// NOTE: The function assumes that the matrix provided is RANK-2 and therefore -/// the solution -// will be the null space (vector). This way we obtain an eigen vector in -// two steps. -/// -/// is the coefficient matrix in flat -/// form std::vector

solutions -template std::vector

GaussJordan3x3(std::vector

flat_mat) { - // For the first step of the Gauss-Jordan Pivoting, we need the max of the - // coefficient from list0Pivot - // - auto absmax1it = std::max_element( - flat_mat.begin(), flat_mat.end(), - [](const int& a, const int& b) { return fabs(a) < fabs(b); }); - - P max1 = *absmax1it; - - // Get the index of max (in absolute value) - int index1 = std::distance(flat_mat.begin(), absmax1it); - - // If max1 = 0, we have a zero matrix and we need to return an empty list - if (max1 == 0) - return std::vector

(); - - // For the second step of the Gauss-Jordan Pivoting, we will require the - // maximum of the elements of the resulting matrix. - P max2; // The value of the absolute max in the next stage of the elimination - // (only need 2 stages for 3x3 matrices) - int index2; // the index of the absolute maximum in the next stage of the - // elimination - std::vector

flat_mat1; // and the next matrix - - // Variable which contain the coordinates of a vector - P x1 = 0, x2 = 0, x3 = 0; - - P* pX[3]; // using a pointer array to store permutations of x1, x2, x3 - P listFinalCoef[2]; - - // Taking cases according to where the maximum lies - if (index1 == 0) { - // List of the value after the first step of the Gauss-Jordan Pivoting - flat_mat1.push_back(flat_mat[4] - ((flat_mat[1] * flat_mat[3]) / max1)); - flat_mat1.push_back(flat_mat[5] - ((flat_mat[2] * flat_mat[3]) / max1)); - flat_mat1.push_back(flat_mat[7] - ((flat_mat[1] * flat_mat[6]) / max1)); - flat_mat1.push_back(flat_mat[8] - ((flat_mat[2] * flat_mat[6]) / max1)); - - // Get maximum and minimum elements (in order to get the maximum in absolute - // value) - auto absmax2it = std::max_element( - flat_mat1.begin(), flat_mat1.end(), - [](const int& a, const int& b) { return fabs(a) < fabs(b); }); - max2 = *absmax2it; - - // NOTE: Now max2it points to the absolute maximum ! - index2 = std::distance(flat_mat1.begin(), absmax2it); - - pX[0] = &x1; - pX[1] = &x2; - pX[2] = &x3; - - listFinalCoef[0] = flat_mat[1]; - listFinalCoef[1] = flat_mat[2]; - - } else if (index1 == 1) { - flat_mat1.push_back(flat_mat[3] - ((flat_mat[0] * flat_mat[4]) / max1)); - flat_mat1.push_back(flat_mat[5] - ((flat_mat[2] * flat_mat[4]) / max1)); - flat_mat1.push_back(flat_mat[6] - ((flat_mat[0] * flat_mat[7]) / max1)); - flat_mat1.push_back(flat_mat[8] - ((flat_mat[2] * flat_mat[7]) / max1)); - - // Get maximum and minimum elements (in order to get the maximum in absolute - // value) - auto absmax2it = std::max_element( - flat_mat1.begin(), flat_mat1.end(), - [](const int& a, const int& b) { return fabs(a) < fabs(b); }); - max2 = *absmax2it; - - // NOTE: Now max2it points to the absolute maximum ! - index2 = std::distance(flat_mat1.begin(), absmax2it); - - pX[0] = &x2; - pX[1] = &x1; - pX[2] = &x3; - - listFinalCoef[0] = flat_mat[0]; - listFinalCoef[1] = flat_mat[2]; - - } else if (index1 == 2) { - flat_mat1.push_back(flat_mat[3] - ((flat_mat[0] * flat_mat[5]) / max1)); - flat_mat1.push_back(flat_mat[4] - ((flat_mat[1] * flat_mat[5]) / max1)); - flat_mat1.push_back(flat_mat[6] - ((flat_mat[0] * flat_mat[8]) / max1)); - flat_mat1.push_back(flat_mat[7] - ((flat_mat[1] * flat_mat[8]) / max1)); - - // Get maximum and minimum elements (in order to get the maximum in absolute - // value) - auto absmax2it = std::max_element( - flat_mat1.begin(), flat_mat1.end(), - [](const int& a, const int& b) { return fabs(a) < fabs(b); }); - max2 = *absmax2it; - - // NOTE: Now max2it points to the absolute maximum ! - index2 = std::distance(flat_mat1.begin(), absmax2it); - - pX[0] = &x3; - pX[1] = &x1; - pX[2] = &x2; - - listFinalCoef[0] = flat_mat[0]; - listFinalCoef[1] = flat_mat[1]; - - } else if (index1 == 3) { - flat_mat1.push_back(flat_mat[1] - ((flat_mat[4] * flat_mat[0]) / max1)); - flat_mat1.push_back(flat_mat[2] - ((flat_mat[0] * flat_mat[5]) / max1)); - flat_mat1.push_back(flat_mat[7] - ((flat_mat[4] * flat_mat[6]) / max1)); - flat_mat1.push_back(flat_mat[8] - ((flat_mat[5] * flat_mat[6]) / max1)); - - auto max2it = std::max_element(flat_mat1.begin(), flat_mat1.end()); - auto min2it = std::min_element(flat_mat1.begin(), flat_mat1.end()); - max2 = *max2it; - - // Get maximum and minimum elements (in order to get the maximum in absolute - // value) - auto absmax2it = std::max_element( - flat_mat1.begin(), flat_mat1.end(), - [](const int& a, const int& b) { return fabs(a) < fabs(b); }); - max2 = *absmax2it; - - // NOTE: Now max2it points to the absolute maximum ! - index2 = std::distance(flat_mat1.begin(), absmax2it); - - pX[0] = &x1; - pX[1] = &x2; - pX[2] = &x3; - - listFinalCoef[0] = flat_mat[4]; - listFinalCoef[1] = flat_mat[5]; - - } else if (index1 == 4) { - flat_mat1.push_back(flat_mat[0] - ((flat_mat[1] * flat_mat[3]) / max1)); - flat_mat1.push_back(flat_mat[2] - ((flat_mat[5] * flat_mat[1]) / max1)); - flat_mat1.push_back(flat_mat[6] - ((flat_mat[3] * flat_mat[7]) / max1)); - flat_mat1.push_back(flat_mat[8] - ((flat_mat[5] * flat_mat[7]) / max1)); - - // Get maximum and minimum elements (in order to get the maximum in absolute - // value) - auto absmax2it = std::max_element( - flat_mat1.begin(), flat_mat1.end(), - [](const int& a, const int& b) { return fabs(a) < fabs(b); }); - max2 = *absmax2it; - - // NOTE: Now max2it points to the absolute maximum ! - index2 = std::distance(flat_mat1.begin(), absmax2it); - - pX[0] = &x2; - pX[1] = &x1; - pX[2] = &x3; - - listFinalCoef[0] = flat_mat[3]; - listFinalCoef[1] = flat_mat[5]; - - } else if (index1 == 5) { - flat_mat1.push_back(flat_mat[0] - ((flat_mat[2] * flat_mat[3]) / max1)); - flat_mat1.push_back(flat_mat[1] - ((flat_mat[2] * flat_mat[4]) / max1)); - flat_mat1.push_back(flat_mat[6] - ((flat_mat[8] * flat_mat[3]) / max1)); - flat_mat1.push_back(flat_mat[7] - ((flat_mat[8] * flat_mat[4]) / max1)); - - // Get maximum and minimum elements (in order to get the maximum in absolute - // value) - auto absmax2it = std::max_element( - flat_mat1.begin(), flat_mat1.end(), - [](const int& a, const int& b) { return fabs(a) < fabs(b); }); - max2 = *absmax2it; - - // NOTE: Now max2it points to the absolute maximum ! - index2 = std::distance(flat_mat1.begin(), absmax2it); - - pX[0] = &x3; - pX[1] = &x1; - pX[2] = &x2; - - listFinalCoef[0] = flat_mat[3]; - listFinalCoef[1] = flat_mat[4]; - - } else if (index1 == 6) { - flat_mat1.push_back(flat_mat[1] - ((flat_mat[0] * flat_mat[7]) / max1)); - flat_mat1.push_back(flat_mat[2] - ((flat_mat[0] * flat_mat[8]) / max1)); - flat_mat1.push_back(flat_mat[4] - ((flat_mat[3] * flat_mat[7]) / max1)); - flat_mat1.push_back(flat_mat[5] - ((flat_mat[3] * flat_mat[8]) / max1)); - - // Get maximum and minimum elements (in order to get the maximum in absolute - // value) - auto absmax2it = std::max_element( - flat_mat1.begin(), flat_mat1.end(), - [](const int& a, const int& b) { return fabs(a) < fabs(b); }); - max2 = *absmax2it; - - // NOTE: Now max2it points to the absolute maximum ! - index2 = std::distance(flat_mat1.begin(), absmax2it); - - pX[0] = &x1; - pX[1] = &x2; - pX[2] = &x3; - - listFinalCoef[0] = flat_mat[7]; - listFinalCoef[1] = flat_mat[8]; - - } else if (index1 == 7) { - flat_mat1.push_back(flat_mat[0] - ((flat_mat[1] * flat_mat[6]) / max1)); - flat_mat1.push_back(flat_mat[2] - ((flat_mat[1] * flat_mat[8]) / max1)); - flat_mat1.push_back(flat_mat[3] - ((flat_mat[4] * flat_mat[6]) / max1)); - flat_mat1.push_back(flat_mat[5] - ((flat_mat[4] * flat_mat[8]) / max1)); - - // Get maximum and minimum elements (in order to get the maximum in absolute - // value) - auto absmax2it = std::max_element( - flat_mat1.begin(), flat_mat1.end(), - [](const int& a, const int& b) { return fabs(a) < fabs(b); }); - max2 = *absmax2it; - - // NOTE: Now max2it points to the absolute maximum ! - index2 = std::distance(flat_mat1.begin(), absmax2it); - - pX[0] = &x2; - pX[1] = &x1; - pX[2] = &x3; - - listFinalCoef[0] = flat_mat[6]; - listFinalCoef[1] = flat_mat[8]; - - } else if (index1 == 8) { - flat_mat1.push_back(flat_mat[0] - ((flat_mat[2] * flat_mat[6]) / max1)); - flat_mat1.push_back(flat_mat[1] - ((flat_mat[2] * flat_mat[7]) / max1)); - flat_mat1.push_back(flat_mat[3] - ((flat_mat[5] * flat_mat[6]) / max1)); - flat_mat1.push_back(flat_mat[4] - ((flat_mat[5] * flat_mat[7]) / max1)); - - // Get maximum and minimum elements (in order to get the maximum in absolute - // value) - auto absmax2it = std::max_element( - flat_mat1.begin(), flat_mat1.end(), - [](const int& a, const int& b) { return fabs(a) < fabs(b); }); - max2 = *absmax2it; - - // NOTE: Now max2it points to the absolute maximum ! - index2 = std::distance(flat_mat1.begin(), absmax2it); - - pX[0] = &x3; - pX[1] = &x1; - pX[2] = &x2; - - listFinalCoef[0] = flat_mat[6]; - listFinalCoef[1] = flat_mat[7]; - } - - if (index2 == 0) { - *(pX[2]) = 1; - *(pX[1]) = -flat_mat1[1] / max2; - } else if (index2 == 1) { - *(pX[2]) = -flat_mat1[0] / max2; - *(pX[1]) = 1; - } else if (index2 == 2) { - *(pX[2]) = 1; - *(pX[1]) = -flat_mat1[3] / max2; - } else if (index2 == 3) { - *(pX[2]) = -flat_mat1[2] / max2; - *(pX[1]) = 1; - } - - *(pX[0]) = -((listFinalCoef[0] * *(pX[1])) / max1) - - ((listFinalCoef[1] * *(pX[2])) / max1); - - // normalize - P norm = sqrt(x1 * x1 + x2 * x2 + x3 * x3); - - return std::vector

({x1 / norm, x2 / norm, x3 / norm}); -} - -/// -/// This function eliminates the coefficients of the column "col" based on an -/// element at (row, col) -/// -/// -template -std::vector

GaussJordanFirstStep(std::vector

flat_mat, int row, int col) { - - // the result - std::vector

result; - - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - - if (i != row && j != col) { - result.push_back(flat_mat[i * 4 + j] - - ((flat_mat[row * 4 + j] * flat_mat[i * 4 + col]) / - flat_mat[row * 4 + col])); - } - } - } - - return result; -} - -/// -/// The Gauss-Jordan elimination for Rank-3 4x4 matrices -/// The steps are fixed and lead to a solution up to arbitrary scale. -/// This is how we solve for the eigenvectors of the a 4x4 marix. -/// -/// The input vector is a flat version of the 4x4 matrix -/// -template std::vector

GaussJordan4x4(std::vector

flat_mat) { - // Working out the the index of the max coefficient (absolute value). - auto absmax1it = std::max_element( - flat_mat.begin(), flat_mat.end(), - [](const int& a, const int& b) { return fabs(a) < fabs(b); }); - - P max1 = *absmax1it; - int index1 = std::distance(flat_mat.begin(), absmax1it); - - // Return empty list if the maximum is zero (zero matrix) - if (max1 == 0) - return std::vector

(); - - // Create new variable which are use after. - std::vector

flat_mat1; - - P x1 = 0, x2 = 0, x3 = 0, x4 = 0; - // Cache for the result of 3x3 Gauss Jordan elimination - std::vector

resultGaussJordan3x3; - - // go... - if (index1 == 0) { - /// We use the GaussJordan3x3 solver after the first step, which is the - /// elimination of coeffcieints along column 0. - flat_mat1 = GaussJordanFirstStep(flat_mat, 0, 0); - resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); - x2 = resultGaussJordan3x3[0]; - x3 = resultGaussJordan3x3[1]; - x4 = resultGaussJordan3x3[2]; - x1 = -(1 / flat_mat[0]) * - (flat_mat[1] * x2 + flat_mat[2] * x3 + flat_mat[3] * x4); - } else if (index1 == 1) { - /// We use the GaussJordan3x3 solver after the first step, which is the - /// elimination of coeffcieints along column 1. - flat_mat1 = GaussJordanFirstStep(flat_mat, 0, 1); - resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); - x1 = resultGaussJordan3x3[0]; - x3 = resultGaussJordan3x3[1]; - x4 = resultGaussJordan3x3[2]; - x2 = -(1 / flat_mat[1]) * - (flat_mat[0] * x1 + flat_mat[2] * x3 + flat_mat[3] * x4); - } else if (index1 == 2) { - flat_mat1 = GaussJordanFirstStep(flat_mat, 0, 2); - resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); - x1 = resultGaussJordan3x3[0]; - x2 = resultGaussJordan3x3[1]; - x4 = resultGaussJordan3x3[2]; - x3 = -(1 / flat_mat[2]) * - (flat_mat[0] * x1 + flat_mat[1] * x2 + flat_mat[3] * x4); - } else if (index1 == 3) { - flat_mat1 = GaussJordanFirstStep(flat_mat, 0, 3); - resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); - x1 = resultGaussJordan3x3[0]; - x2 = resultGaussJordan3x3[1]; - x3 = resultGaussJordan3x3[2]; - x4 = -(1 / flat_mat[3]) * - (flat_mat[0] * x1 + flat_mat[1] * x2 + flat_mat[2] * x3); - } else if (index1 == 4) { - flat_mat1 = GaussJordanFirstStep(flat_mat, 1, 0); - resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); - x2 = resultGaussJordan3x3[0]; - x3 = resultGaussJordan3x3[1]; - x4 = resultGaussJordan3x3[2]; - x1 = -(1 / flat_mat[4]) * - (flat_mat[5] * x2 + flat_mat[6] * x3 + flat_mat[7] * x4); - } else if (index1 == 5) { - flat_mat1 = GaussJordanFirstStep(flat_mat, 1, 1); - resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); - x1 = resultGaussJordan3x3[0]; - x3 = resultGaussJordan3x3[1]; - x4 = resultGaussJordan3x3[2]; - x2 = -(1 / flat_mat[5]) * - (flat_mat[4] * x1 + flat_mat[6] * x3 + flat_mat[7] * x4); - } else if (index1 == 6) { - flat_mat1 = GaussJordanFirstStep(flat_mat, 1, 2); - resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); - x1 = resultGaussJordan3x3[0]; - x2 = resultGaussJordan3x3[1]; - x4 = resultGaussJordan3x3[2]; - x3 = -(1 / flat_mat[6]) * - (flat_mat[4] * x1 + flat_mat[5] * x2 + flat_mat[7] * x4); - } else if (index1 == 7) { - flat_mat1 = GaussJordanFirstStep(flat_mat, 1, 3); - resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); - x1 = resultGaussJordan3x3[0]; - x2 = resultGaussJordan3x3[1]; - x3 = resultGaussJordan3x3[2]; - x4 = -(1 / flat_mat[7]) * - (flat_mat[4] * x1 + flat_mat[5] * x2 + flat_mat[6] * x3); - } else if (index1 == 8) { - flat_mat1 = GaussJordanFirstStep(flat_mat, 2, 0); - resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); - x2 = resultGaussJordan3x3[0]; - x3 = resultGaussJordan3x3[1]; - x4 = resultGaussJordan3x3[2]; - x1 = -(1 / flat_mat[8]) * - (flat_mat[9] * x2 + flat_mat[10] * x3 + flat_mat[11] * x4); - } else if (index1 == 9) { - flat_mat1 = GaussJordanFirstStep(flat_mat, 2, 1); - resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); - x1 = resultGaussJordan3x3[0]; - x3 = resultGaussJordan3x3[1]; - x4 = resultGaussJordan3x3[2]; - x2 = -(1 / flat_mat[9]) * - (flat_mat[8] * x1 + flat_mat[10] * x3 + flat_mat[11] * x4); - } else if (index1 == 10) { - flat_mat1 = GaussJordanFirstStep(flat_mat, 2, 2); - resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); - x1 = resultGaussJordan3x3[0]; - x2 = resultGaussJordan3x3[1]; - x4 = resultGaussJordan3x3[2]; - x3 = -(1 / flat_mat[10]) * - (flat_mat[8] * x1 + flat_mat[9] * x2 + flat_mat[11] * x4); - } else if (index1 == 11) { - flat_mat1 = GaussJordanFirstStep(flat_mat, 2, 3); - resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); - x1 = resultGaussJordan3x3[0]; - x2 = resultGaussJordan3x3[1]; - x3 = resultGaussJordan3x3[2]; - x4 = -(1 / flat_mat[11]) * - (flat_mat[8] * x1 + flat_mat[9] * x2 + flat_mat[10] * x3); - } else if (index1 == 12) { - flat_mat1 = GaussJordanFirstStep(flat_mat, 3, 0); - resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); - x2 = resultGaussJordan3x3[0]; - x3 = resultGaussJordan3x3[1]; - x4 = resultGaussJordan3x3[2]; - x1 = -(1 / flat_mat[12]) * - (flat_mat[13] * x2 + flat_mat[14] * x3 + flat_mat[15] * x4); - - } else if (index1 == 13) { - flat_mat1 = GaussJordanFirstStep(flat_mat, 3, 1); - resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); - x1 = resultGaussJordan3x3[0]; - x3 = resultGaussJordan3x3[1]; - x4 = resultGaussJordan3x3[2]; - x2 = -(1 / flat_mat[13]) * - (flat_mat[12] * x1 + flat_mat[14] * x3 + flat_mat[15] * x4); - } else if (index1 == 14) { - flat_mat1 = GaussJordanFirstStep(flat_mat, 3, 2); - resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); - x1 = resultGaussJordan3x3[0]; - x2 = resultGaussJordan3x3[1]; - x4 = resultGaussJordan3x3[2]; - x3 = -(1 / flat_mat[14]) * - (flat_mat[12] * x1 + flat_mat[13] * x2 + flat_mat[15] * x4); - } else if (index1 == 15) { - flat_mat1 = GaussJordanFirstStep(flat_mat, 3, 3); - resultGaussJordan3x3 = GaussJordan3x3(flat_mat1); - x1 = resultGaussJordan3x3[0]; - x2 = resultGaussJordan3x3[1]; - x3 = resultGaussJordan3x3[2]; - x4 = -(1 / flat_mat[15]) * - (flat_mat[12] * x1 + flat_mat[13] * x2 + flat_mat[14] * x3); - } - - // normalize the solution - P norm = sqrt(x1 * x1 + x2 * x2 + x3 * x3 + x4 * x4); - - return std::vector

({x1 / norm, x2 / norm, x3 / norm, x4 / norm}); -} - -/// -/// Return the list of the eigenvalue of a 4x4 matrix. -/// Parameter is he argumen is a 1D array representing the 4x4 matrix -/// T -/// -template std::vector

EigenValues4x4(P* array) { - - P a11 = array[0 * 4 + 0], a12 = array[0 * 4 + 1], a13 = array[0 * 4 + 2], - a14 = array[0 * 4 + 3]; - P a21 = array[1 * 4 + 0], a22 = array[1 * 4 + 1], a23 = array[1 * 4 + 2], - a24 = array[1 * 4 + 3]; - P a31 = array[2 * 4 + 0], a32 = array[2 * 4 + 1], a33 = array[2 * 4 + 2], - a34 = array[2 * 4 + 3]; - P a41 = array[3 * 4 + 0], a42 = array[3 * 4 + 1], a43 = array[3 * 4 + 2], - a44 = array[3 * 4 + 3]; - - // 1. Obtaining the coefficients of the characteristics polynomial of A11 - - // λ*I where A11 is the (1, 1) submatrix of A: A11 = [a22-λ a23 a24; - // a32 a33-λ a34; - // a42 a43 a44 - λ] - - // The coefficients of the cubic polynomial det(A11-λI) are: - P C3 = -1, C2 = a22 + a33 + a44; - P C1 = a42 * a24 + a32 * a23 + a34 * a43 - a22 * a33 - a22 * a44 - a33 * a44; - P C0 = a22 * a33 * a44 + a23 * a42 * a34 + a24 * a32 * a43 - a22 * a34 * a43 - - a23 * a32 * a44 - a24 * a42 * a33; - - // 2. Now multiplying with a11 to get a quartic polynomial with coeffcients - // W4, W3, W2, W1, W0 as follows: - P W4 = -C3, W3 = a11 * C3 - C2, W2 = a11 * C2 - C1, W1 = a11 * C1 - C0, - W0 = a11 * C0; - - // 4. Now we obtain the coefficients of the 3 quadratics (from the algebraic - // complements A12, A13, A14) as follows: a. (A-λI)12 (-) - P Q1_2 = -a12 * a21, - Q1_1 = -a12 * (-a21 * (a33 + a44) + a23 * a31 + a24 * a41), - Q1_0 = - -a12 * (a24 * (a31 * a43 - a41 * a33) - a23 * (a31 * a44 - a41 * a34) + - a21 * (a33 * a44 - - a43 * a34)); // good all being well... - // multiplying with -a12 - // P1_2 *= -a12; P1_1 *= -a12; P1_0 *= -a12; - - // b. (A-λ)13 (+) - P Q2_2 = a13 * -a31, - Q2_1 = a13 * ((a31 * a44 - a41 * a34 + a22 * a31) - a21 * a32), - Q2_0 = - a13 * (a24 * (a31 * a42 - a41 * a32) + a22 * (a41 * a34 - a31 * a44) + - a21 * (a32 * a44 - a42 * a34)); - - // c. (A-λ)14 (-) - P Q3_2 = -a14 * a41, - Q3_1 = -a14 * ((a31 * a43 - a41 * a33 - a22 * a41) + a21 * a42), - Q3_0 = - -a14 * (a23 * (a31 * a42 - a41 * a32) - a22 * (a31 * a43 - a41 * a33) + - a21 * (a32 * a43 - a42 * a33)); - - // 5. Final coefficients - P A0 = Q3_0 + Q2_0 + Q1_0 + W0, A1 = Q3_1 + Q2_1 + Q1_1 + W1, - A2 = Q3_2 + Q2_2 + Q1_2 + W2, A3 = W3, A4 = W4; - - std::vector

solution = PolySolvers::SolveQuartic(A4, A3, A2, A1, A0); - - // Put the solutions in ascending order - std::sort(solution.begin(), solution.end()); - - return solution; -} - -/// -/// Return the list of the eigenvalues of a 3x3 matrix. -/// Parameter is he argumen is a 1D array representing the 4x4 matrix -/// T -/// -template std::vector

EigenValues3x3(const P* array) { - - P coef1 = -1; - P coef2 = (array[0 * 3 + 0] + array[1 * 3 + 1] + array[2 * 3 + 2]); - P coef3 = (array[2 * 3 + 0] * array[0 * 3 + 2]) + - (array[1 * 3 + 0] * array[0 * 3 + 1]) + - (array[1 * 3 + 2] * array[2 * 3 + 1]) - - (array[0 * 3 + 0] * array[1 * 3 + 1]) - - (array[0 * 3 + 0] * array[2 * 3 + 2]) - - (array[1 * 3 + 1] * array[2 * 3 + 2]); - - P coef4 = (array[0 * 3 + 0] * array[1 * 3 + 1] * array[2 * 3 + 2]) + - (array[0 * 3 + 1] * array[2 * 3 + 0] * array[1 * 3 + 2]) + - (array[0 * 3 + 2] * array[1 * 3 + 0] * array[2 * 3 + 1]) - - (array[0 * 3 + 0] * array[1 * 3 + 2] * array[2 * 3 + 1]) - - (array[0 * 3 + 1] * array[1 * 3 + 0] * array[2 * 3 + 2]) - - (array[0 * 3 + 2] * array[2 * 3 + 0] * array[1 * 3 + 1]); - - std::vector

solution = PolySolvers::SolveCubic(coef1, coef2, coef3, coef4); - - std::sort(solution.begin(), solution.end()); - - // for (int i = 0; i < solution.size(); i++) - // std::cout << "eigenvalue "< std::vector

PrincipalEigenvector4x4(P* M) { - // First obtain the eigenvalues of M - auto eigenvalues = EigenValues4x4(M); - if (eigenvalues.size() == 0) - return std::vector

({0, 0, 0, 0}); - // auto absmaxit = std::max_element( eigenvalues.begin(), - // eigenvalues.end() , - // [](const int& a, const int& b) { return fabs(a) < - // fabs(b);} - // ); - // P lambda = *absmaxit; - P lambda = eigenvalues[eigenvalues.size() - - 1]; // returning the largest eigenvalue (instead or - // trhe largest in absolute value) - if (lambda == 0) - return std::vector

({0, 0, 0, 0}); - // Now obtaining a vector containing the M-lambda * eye(3) - std::vector

A; - A.push_back(M[0] - lambda); - A.push_back(M[1]); - A.push_back(M[2]); - A.push_back(M[3]); - A.push_back(M[4]); - A.push_back(M[5] - lambda); - A.push_back(M[6]); - A.push_back(M[7]); - A.push_back(M[8]); - A.push_back(M[9]); - A.push_back(M[10] - lambda); - A.push_back(M[11]); - A.push_back(M[12]); - A.push_back(M[13]); - A.push_back(M[14]); - A.push_back(M[15] - lambda); - - // Now get the eigenvector using the Gauss-jordan steps - return GaussJordan4x4(A); -} - -// Get the eigenvector of the largest eigenvalue of a 4x4 matrix. -// NOTE: This function will return the zero vector even if no eigenvalues exist -template std::vector

PrincipalEigenvector3x3(P* M) { - // First obtain the eigenvalues of M - auto eigenvalues = EigenValues3x3(M); - if (eigenvalues.size() == 0) - return std::vector

({0, 0, 0}); - // auto maxit = std::max_element( eigenvalues.begin(), - // eigenvalues.end() , - // [](const int& a, const int& b) { return fabs(a) < - // fabs(b); } - // ); - - // P lambda = *maxit; - P lambda = eigenvalues[eigenvalues.size() - 1]; // get the largest eigenvalue - // if there are only zero eigenvalues, return the zero vector - if (lambda == 0) - return std::vector

({0, 0, 0}); // extra check - - // Now obtaining a vector containing the M-lambda * eye(3) - std::vector

A; - A.push_back(M[0] - lambda); - A.push_back(M[1]); - A.push_back(M[2]); - A.push_back(M[3]); - A.push_back(M[4] - lambda); - A.push_back(M[5]); - A.push_back(M[6]); - A.push_back(M[7]); - A.push_back(M[8] - lambda); - - // Now get the eigenvector using the Gauss-jordan steps - return GaussJordan3x3(A); -} - -// 3x3 Matrix eigen decomposition -template -std::pair, std::vector>> EigenDecompose3x3(P* M) { - // First obtain the eigenvalues of M - auto eigenvalues = EigenValues3x3(M); - // vector of eigenvectors - std::vector> eigenvectors; - // return an empty eigenvalue list and a single eigenvector withg zeros in it - if (eigenvalues.size() == 0) - return std::pair, std::vector>>(eigenvalues, - eigenvectors); - - std::vector

A({M[0], M[1], M[2], M[3], M[4], M[5], M[6], M[7], M[8]}); - - for (int i = 0; i < eigenvalues.size(); i++) { - // Now subtract the eigenvalue from the diagonal - A[0] -= eigenvalues[i]; - A[4] -= eigenvalues[i]; - A[8] -= eigenvalues[i]; - - // compute and add the eigen vector to the list - eigenvectors.push_back(GaussJordan3x3(A)); - - // add the eigenvalue back to the diagonal in order to undo the change in - // the elements of A - A[0] += eigenvalues[i]; - A[4] += eigenvalues[i]; - A[8] += eigenvalues[i]; - } - - // return the decomposition (in ascending eigenvalue order) - return std::pair, std::vector>>(eigenvalues, - eigenvectors); -} - -// 4x4 Matrix eigen decomposition -template -std::pair, std::vector>> EigenDecompose4x4(P* M) { - // First obtain the eigenvalues of M - auto eigenvalues = EigenValues4x4(M); - // vector of eigenvectors - std::vector> eigenvectors; - - // return an empty eigenvalue list and a single eigenvector withg zeros in it - if (eigenvalues.size() == 0) - return std::pair, std::vector>>(eigenvalues, - eigenvectors); - - std::vector

A({M[0], M[1], M[2], M[3], M[4], M[5], M[6], M[7], M[8], M[9], - M[10], M[11], M[12], M[13], M[14], M[15]}); - - for (int i = 0; i < eigenvalues.size(); i++) { - // Now subtract the eigenvalue from the diagonal - A[0] -= eigenvalues[i]; - A[5] -= eigenvalues[i]; - A[10] -= eigenvalues[i]; - A[15] -= eigenvalues[i]; - - // compute and add the eigen vector to the list - eigenvectors.push_back(GaussJordan4x4(A)); - - // add the eigenvalue back to the diagonal in order to undo the change in - // the elements of A - A[0] += eigenvalues[i]; - A[5] += eigenvalues[i]; - A[10] += eigenvalues[i]; - A[15] += eigenvalues[i]; - } - - // return the decomposition (in ascending eigenvalue order) - return std::pair, std::vector>>(eigenvalues, - eigenvectors); -} - -std::pair self_adjoint_evd(rmatrix4x4 A) { - rmatrix4x4 rvectors; // TODO: vectors result in nan because max2 is zero - rdiagonal4x4 rvalues; - auto [values, vectors] = EigenDecompose4x4(A.data()); - for (int i = 0; i < values.size(); ++i) { - for (int j = 0; j < vectors[i].size(); ++j) { - rvectors[j * 4 + i] = vectors[j][i]; - } - rvalues[i] = values[i]; - } - return {rvectors, rvalues}; -} -} // namespace mqt::ir::opt - -#endif diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/f.h b/mlir/lib/Dialect/MQTOpt/Transforms/f.h deleted file mode 100644 index 4cbb4c86cb..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/f.h +++ /dev/null @@ -1,97 +0,0 @@ -#pragma once - -#include "Helpers.h" - -namespace mqt::ir::opt { -inline bool jacobi_4x4(double * A, double * D, double * U) -{ - double B[4], Z[4]; - double Id[16] = {1., 0., 0., 0., - 0., 1., 0., 0., - 0., 0., 1., 0., - 0., 0., 0., 1.}; - - memcpy(U, Id, 16 * sizeof(double)); - - B[0] = A[0]; B[1] = A[5]; B[2] = A[10]; B[3] = A[15]; - memcpy(D, B, 4 * sizeof(double)); - memset(Z, 0, 4 * sizeof(double)); - - for(int iter = 0; iter < 50; iter++) { - double sum = fabs(A[1]) + fabs(A[2]) + fabs(A[3]) + fabs(A[6]) + fabs(A[7]) + fabs(A[11]); - - if (sum == 0.0) - return true; - - double tresh = (iter < 3) ? 0.2 * sum / 16. : 0.0; - for(int i = 0; i < 3; i++) { - double * pAij = A + 5 * i + 1; - for(int j = i + 1 ; j < 4; j++) { - double Aij = *pAij; - double eps_machine = 100.0 * fabs(Aij); - - if ( iter > 3 && fabs(D[i]) + eps_machine == fabs(D[i]) && fabs(D[j]) + eps_machine == fabs(D[j]) ) - *pAij = 0.0; - else if (fabs(Aij) > tresh) { - double hh = D[j] - D[i], t; - if (fabs(hh) + eps_machine == fabs(hh)) - t = Aij / hh; - else { - double theta = 0.5 * hh / Aij; - t = 1.0 / (fabs(theta) + sqrt(1.0 + theta * theta)); - if (theta < 0.0) t = -t; - } - - hh = t * Aij; - Z[i] -= hh; - Z[j] += hh; - D[i] -= hh; - D[j] += hh; - *pAij = 0.0; - - double c = 1.0 / sqrt(1 + t * t); - double s = t * c; - double tau = s / (1.0 + c); - for(int k = 0; k <= i - 1; k++) { - double g = A[k * 4 + i], h = A[k * 4 + j]; - A[k * 4 + i] = g - s * (h + g * tau); - A[k * 4 + j] = h + s * (g - h * tau); - } - for(int k = i + 1; k <= j - 1; k++) { - double g = A[i * 4 + k], h = A[k * 4 + j]; - A[i * 4 + k] = g - s * (h + g * tau); - A[k * 4 + j] = h + s * (g - h * tau); - } - for(int k = j + 1; k < 4; k++) { - double g = A[i * 4 + k], h = A[j * 4 + k]; - A[i * 4 + k] = g - s * (h + g * tau); - A[j * 4 + k] = h + s * (g - h * tau); - } - for(int k = 0; k < 4; k++) { - double g = U[k * 4 + i], h = U[k * 4 + j]; - U[k * 4 + i] = g - s * (h + g * tau); - U[k * 4 + j] = h + s * (g - h * tau); - } - } - pAij++; - } - } - - for(int i = 0; i < 4; i++) B[i] += Z[i]; - memcpy(D, B, 4 * sizeof(double)); - memset(Z, 0, 4 * sizeof(double)); - } - - return false; -} - - -std::pair self_adjoint_evd(rmatrix4x4 A) { - rmatrix4x4 rvectors{}; - rdiagonal4x4 rvalues{}; - if (!jacobi_4x4(A.data(), rvectors.data(), rvalues.data())) { - throw std::runtime_error{"bad luck..."}; - } - return {rvectors, rvalues}; -} -} diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/g.h b/mlir/lib/Dialect/MQTOpt/Transforms/g.h deleted file mode 100644 index 8eb6c40540..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/g.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "Helpers.h" -#include "eigen3/Eigen/Eigen" - -namespace mqt::ir::opt { - -auto self_adjoint_evd(rmatrix4x4 A) { - Eigen::Matrix4d a; - Eigen::SelfAdjointEigenSolver s; - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 4; ++j) { - a(j, i) = A[j * 4 + i]; - } - } - std::cerr << "=EigIN==\n" << a << "\n========\n" << std::endl; - s.compute(a); // TODO: computeDirect is faster - auto vecs = s.eigenvectors(); - auto vals = s.eigenvalues(); - rmatrix4x4 rvecs; - rdiagonal4x4 rvals; - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 4; ++j) { - rvecs[j * 4 + i] = vecs(j, i); - } - rvals[i] = vals(i); - } - std::cerr << "=Eigen==\n" << vecs << "\n========\n" << std::endl; - std::cerr << "=Eigen==\n" << vals << "\n========\n" << std::endl; - return std::make_pair(rvecs, rvals); -} -} // namespace mqt::ir::opt From a5dead7b5c8f49da384881ce1b7119dd6b46c816 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 10 Nov 2025 21:35:01 +0100 Subject: [PATCH 045/237] delete pulse-optimizer code (KEEP for future; should be re-added at some point) --- .../Transforms/GateDecompositionPattern.cpp | 260 +----------------- 1 file changed, 1 insertion(+), 259 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 69f5f1a259..bdfdd14bb4 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -1243,7 +1243,6 @@ struct GateDecompositionPattern final QubitGateSequence::Gate basis_gate; fp basis_fidelity; EulerBasis euler_basis; - std::optional pulse_optimize; TwoQubitWeylDecomposition basis_decomposer; bool super_controlled; matrix2x2 u0l; @@ -1271,8 +1270,7 @@ struct GateDecompositionPattern final new_inner(OneQubitGateSequence::Gate basis_gate = {.type = qc::X, .parameter = {}, .qubit_id = {0, 1}}, - fp basis_fidelity = 1.0, EulerBasis euler_basis = EulerBasis::ZYZ, - std::optional pulse_optimize = std::nullopt) { + fp basis_fidelity = 1.0, EulerBasis euler_basis = EulerBasis::ZYZ) { auto relative_eq = [](auto&& lhs, auto&& rhs, auto&& epsilon, auto&& max_relative) { // Handle same infinities @@ -1394,7 +1392,6 @@ struct GateDecompositionPattern final basis_gate, basis_fidelity, euler_basis, - pulse_optimize, basis_decomposer, super_controlled, u0l, @@ -1470,12 +1467,6 @@ struct GateDecompositionPattern final for (auto x : decomposition) { helpers::print(x, "", true); } - if (pulse_optimize.value_or(true)) { - if (auto result = pulse_optimal_chooser(best_nbasis, decomposition, - target_decomposed)) { - return result; - } - } std::vector target_1q_basis_list; // TODO: simplify because list only has one // element? @@ -1573,241 +1564,6 @@ struct GateDecompositionPattern final }; } - std::optional - pulse_optimal_chooser(std::uint8_t best_nbasis, - std::vector decomposition, - const TwoQubitWeylDecomposition& target_decomposed) { - if (pulse_optimize.has_value() && - (best_nbasis == 0 || best_nbasis == 1 || best_nbasis > 3)) { - return std::nullopt; - } - if (euler_basis != EulerBasis::ZSX && euler_basis != EulerBasis::ZSXX) { - if (pulse_optimize.has_value()) { - throw std::runtime_error{ - "'pulse_optimize' currently only works with ZSX basis"}; - } - return std::nullopt; - } - - if (basis_gate.type != qc::X || - basis_gate.qubit_id.size() != 2) { // != CX - if (pulse_optimize.has_value()) { - throw std::runtime_error{"pulse_optimizer currently only works " - "with CNOT entangling gate"}; - } - return std::nullopt; - } - std::optional result; - if (best_nbasis == 3) { - result = - get_sx_vz_3cx_efficient_euler(decomposition, target_decomposed); - } else if (best_nbasis == 2) { - result = - get_sx_vz_2cx_efficient_euler(decomposition, target_decomposed); - } - if (pulse_optimize.has_value() && !result.has_value()) { - throw std::runtime_error{ - "Failed to compute requested pulse optimal decomposition"}; - } - return result; - } - - /// - /// Decomposition of SU(4) gate for device with SX, virtual RZ, and CNOT - /// gates assuming two CNOT gates are needed. - /// - /// This first decomposes each unitary from the KAK decomposition into ZXZ - /// on the source qubit of the CNOTs and XZX on the targets in order to - /// commute operators to beginning and end of decomposition. The beginning - /// and ending single qubit gates are then collapsed and re-decomposed - /// with the single qubit decomposer. This last step could be avoided if - /// performance is a concern. - TwoQubitGateSequence get_sx_vz_2cx_efficient_euler( - const std::vector& decomposition, - const TwoQubitWeylDecomposition& target_decomposed) { - TwoQubitGateSequence gates{.globalPhase = target_decomposed.global_phase}; - gates.globalPhase -= 2. * basis_decomposer.global_phase; - - auto get_euler_angles = [&](std::size_t startIndex, EulerBasis basis) { - std::vector> result; - for (std::size_t i = startIndex; i < decomposition.size(); i += 2) { - auto euler_angles = angles_from_unitary(decomposition[i], basis); - gates.globalPhase += euler_angles[3]; - result.push_back({euler_angles[2], euler_angles[0], euler_angles[1]}); - } - return result; - }; - - auto euler_q0 = get_euler_angles(0, EulerBasis::ZXZ); - auto euler_q1 = get_euler_angles(1, EulerBasis::XZX); - - matrix2x2 euler_matrix_q0 = - rx_matrix(euler_q0[0][1]) * rz_matrix(euler_q0[0][0]); - euler_matrix_q0 = rz_matrix(euler_q0[0][2] + euler_q0[1][0] + qc::PI_2) * - euler_matrix_q0; - append_1q_sequence(gates, euler_matrix_q0, 0); - matrix2x2 euler_matrix_q1 = - rz_matrix(euler_q1[0][1]) * rx_matrix(euler_q1[0][0]); - euler_matrix_q1 = - rx_matrix(euler_q1[0][2] + euler_q1[1][0]) * euler_matrix_q1; - append_1q_sequence(gates, euler_matrix_q1, 1); - gates.gates.push_back( - {.type = qc::X, .qubit_id = {0, 1}}); // TODO: mark CX somehow? - gates.gates.push_back({.type = qc::SX, .qubit_id = {0}}); - gates.gates.push_back({.type = qc::RZ, - .parameter = {euler_q0[1][1] - qc::PI}, - .qubit_id = {0}}); - gates.gates.push_back({.type = qc::SX, .qubit_id = {0}}); - gates.gates.push_back( - {.type = qc::RZ, .parameter = {euler_q1[1][1]}, .qubit_id = {1}}); - gates.globalPhase += qc::PI_2; - gates.gates.push_back( - {.type = qc::X, .qubit_id = {0, 1}}); // TODO: mark CX somehow? - euler_matrix_q0 = rx_matrix(euler_q0[2][1]) * - rz_matrix(euler_q0[1][2] + euler_q0[2][0] + qc::PI_2); - euler_matrix_q0 = rz_matrix(euler_q0[2][2]) * euler_matrix_q0; - append_1q_sequence(gates, euler_matrix_q0, 0); - euler_matrix_q1 = rz_matrix(euler_q1[2][1]) * - rx_matrix(euler_q1[1][2] + euler_q1[2][0]); - euler_matrix_q1 = rx_matrix(euler_q1[2][2]) * euler_matrix_q1; - append_1q_sequence(gates, euler_matrix_q1, 1); - return gates; - } - - /// Decomposition of SU(4) gate for device with SX, virtual RZ, and CNOT - /// gates assuming three CNOT gates are needed. - /// - /// This first decomposes each unitary from the KAK decomposition into ZXZ - /// on the source qubit of the CNOTs and XZX on the targets in order - /// commute operators to beginning and end of decomposition. Inserting - /// Hadamards reverses the direction of the CNOTs and transforms a - /// variable Rx -> variable virtual Rz. The beginning and ending single - /// qubit gates are then collapsed and re-decomposed with the single qubit - /// decomposer. This last step could be avoided if performance is a - /// concern. - std::optional get_sx_vz_3cx_efficient_euler( - const std::vector& decomposition, - const TwoQubitWeylDecomposition& target_decomposed) { - TwoQubitGateSequence gates{.globalPhase = target_decomposed.global_phase}; - gates.globalPhase -= 3. * basis_decomposer.global_phase; - gates.globalPhase = remEuclid(gates.globalPhase, qc::TAU); - auto atol = 1e-10; // absolute tolerance for floats - // Decompose source unitaries to zxz - - auto get_euler_angles = [&](std::size_t startIndex, EulerBasis basis) { - std::vector> result; - for (std::size_t i = startIndex; i < decomposition.size(); i += 2) { - auto euler_angles = angles_from_unitary(decomposition[i], basis); - gates.globalPhase += euler_angles[3]; - result.push_back({euler_angles[2], euler_angles[0], euler_angles[1]}); - } - return result; - }; - - auto euler_q0 = get_euler_angles(0, EulerBasis::ZXZ); - // Decompose target unitaries to xzx - auto euler_q1 = get_euler_angles(1, EulerBasis::XZX); - - auto x12 = euler_q0[1][2] + euler_q0[2][0]; - auto x12_is_non_zero = std::abs(x12) < atol; - auto x12_is_old_mult = std::abs(std::cos(x12) - 1.0) < atol; - auto x12_phase = 0.; - auto x12_is_pi_mult = std::abs(std::sin(x12)) < atol; - if (x12_is_pi_mult) { - x12_phase = qc::PI * std::cos(x12); - } - auto x02_add = x12 - euler_q0[1][0]; - auto x12_is_half_pi = std::abs(x12 - qc::PI_2) < atol; - - matrix2x2 euler_matrix_q0 = - rx_matrix(euler_q0[0][1]) * rz_matrix(euler_q0[0][0]); - if (x12_is_non_zero && x12_is_pi_mult) { - euler_matrix_q0 = rz_matrix(euler_q0[0][2] - x02_add) * euler_matrix_q0; - } else { - euler_matrix_q0 = - rz_matrix(euler_q0[0][2] + euler_q0[1][0]) * euler_matrix_q0; - } - euler_matrix_q0 = hGate * euler_matrix_q0; - append_1q_sequence(gates, euler_matrix_q0, 0); - - auto rx_0 = rx_matrix(euler_q1[0][0]); - auto rz = rz_matrix(euler_q1[0][1]); - auto rx_1 = rx_matrix(euler_q1[0][2] + euler_q1[1][0]); - matrix2x2 euler_matrix_q1 = rz * rx_0; - euler_matrix_q1 = rx_1 * euler_matrix_q1; - euler_matrix_q1 = hGate * euler_matrix_q1; - append_1q_sequence(gates, euler_matrix_q1, 1); - - gates.gates.push_back({.type = qc::X, .qubit_id = {1, 0}}); - - if (x12_is_pi_mult) { - // even or odd multiple - if (x12_is_non_zero) { - gates.globalPhase += x12_phase; - } - if (x12_is_non_zero && x12_is_old_mult) { - gates.gates.push_back({.type = qc::RZ, - .parameter = {-euler_q0[1][1]}, - .qubit_id = {0}}); - } else { - gates.gates.push_back( - {.type = qc::RZ, .parameter = {euler_q0[1][1]}, .qubit_id = {0}}); - gates.globalPhase += qc::PI; - } - } - if (x12_is_half_pi) { - gates.gates.push_back({.type = qc::SX, .qubit_id = {0}}); - gates.globalPhase -= qc::PI_4; - } else if (x12_is_non_zero && !x12_is_pi_mult) { - if (!pulse_optimize.has_value()) { - append_1q_sequence(gates, rx_matrix(x12), 0); - } else { - return std::nullopt; - } - } - if (std::abs(euler_q1[1][1] - qc::PI_2) < atol) { - gates.gates.push_back({.type = qc::SX, .qubit_id = {1}}); - gates.globalPhase -= qc::PI_4; - } else if (!pulse_optimize.has_value()) { - append_1q_sequence(gates, rx_matrix(euler_q1[1][1]), 1); - } else { - return std::nullopt; - } - gates.gates.push_back({.type = qc::RZ, - .parameter = {euler_q1[1][2] + euler_q1[2][0]}, - .qubit_id = {1}}); - gates.gates.push_back({.type = qc::X, .qubit_id = {1, 0}}); - gates.gates.push_back( - {.type = qc::RZ, .parameter = {euler_q1[2][1]}, .qubit_id = {0}}); - if (std::abs(euler_q1[2][1] - qc::PI_2) < atol) { - gates.gates.push_back({.type = qc::SX, .qubit_id = {1}}); - gates.globalPhase -= qc::PI_4; - } else if (!pulse_optimize.has_value()) { - append_1q_sequence(gates, rx_matrix(euler_q1[2][1]), 1); - } else { - return std::nullopt; - } - gates.gates.push_back({.type = qc::X, .qubit_id = {1, 0}}); - matrix2x2 eulerMatrix = - rz_matrix(euler_q0[2][2] + euler_q0[3][0]) * hGate; - eulerMatrix = rx_matrix(euler_q0[3][1]) * eulerMatrix; - eulerMatrix = rz_matrix(euler_q0[3][2]) * eulerMatrix; - append_1q_sequence(gates, eulerMatrix, 0); - - eulerMatrix = rx_matrix(euler_q1[2][2] + euler_q1[3][0]) * hGate; - eulerMatrix = rz_matrix(euler_q1[3][1]) * eulerMatrix; - eulerMatrix = rx_matrix(euler_q1[3][2]) * eulerMatrix; - append_1q_sequence(gates, eulerMatrix, 1); - - auto out_unitary = compute_unitary(gates, gates.globalPhase); - // TODO: fix the sign problem to avoid correction here - if (std::abs(target_decomposed.unitary_matrix(0, 0) - - (-out_unitary(0, 0))) < atol) { - gates.globalPhase += qc::PI; - } - return gates; - } - matrix4x4 compute_unitary(const TwoQubitGateSequence& sequence, fp global_phase) { auto phase = std::exp(std::complex{0, global_phase}); @@ -1822,20 +1578,6 @@ struct GateDecompositionPattern final return matrix; } - void append_1q_sequence(TwoQubitGateSequence& twoQubitSequence, - matrix2x2 unitary, std::size_t qubit) { - std::vector target_1q_basis_list; - target_1q_basis_list.push_back(euler_basis); - auto sequence = unitary_to_gate_sequence_inner( - unitary, target_1q_basis_list, qubit, {}, true, std::nullopt); - twoQubitSequence.globalPhase += sequence.globalPhase; - for (auto&& gate : sequence.gates) { - twoQubitSequence.gates.push_back({.type = gate.type, - .parameter = gate.parameter, - .qubit_id = {qubit}}); - } - } - [[nodiscard]] std::array traces(TwoQubitWeylDecomposition target) const { return { From 5aeb8664f388dcf40a1e4172efd8efc1f708845a Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 11 Nov 2025 12:39:49 +0100 Subject: [PATCH 046/237] use designated initializer lists --- .../Transforms/GateDecompositionPattern.cpp | 338 +++++++++--------- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 2 +- 2 files changed, 169 insertions(+), 171 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index bdfdd14bb4..85eca547fa 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -422,9 +422,7 @@ struct GateDecompositionPattern final std::cerr << "CORRECTION!\n"; // if determinant of eigenvector matrix is -1.0, multiply first // eigenvector by -1.0 - for (int i = 0; i < 4; ++i) { - U(i, 0) *= -1.0; - } + U.col(0) *= -1.0; } return std::make_pair(U, S); @@ -926,19 +924,19 @@ struct GateDecompositionPattern final auto specialization = _specialization.value_or(get_default_specialzation()); TwoQubitWeylDecomposition general{ - a, - b, - c, - global_phase, - K1l, - K2l, - K1r, - K2r, - Specialization::General, - default_euler_basis, - fidelity, - -1.0, - unitary_matrix, + .a=a, + .b=b, + .c=c, + .global_phase=global_phase, + .K1l=K1l, + .K2l=K2l, + .K1r=K1r, + .K2r=K2r, + .specialization=Specialization::General, + .default_euler_basis=default_euler_basis, + .requested_fidelity=fidelity, + .calculated_fidelity=-1.0, + .unitary_matrix=unitary_matrix, }; auto get_specialized_decomposition = [&]() { // :math:`U \sim U_d(0,0,0) \sim Id` @@ -948,19 +946,19 @@ struct GateDecompositionPattern final // :math:`K2_l = Id` , :math:`K2_r = Id`. if (specialization == Specialization::IdEquiv) { return TwoQubitWeylDecomposition{ - 0., - 0., - 0., - general.global_phase, - general.K1l * general.K2l, - identityGate, - general.K1r * general.K2r, - identityGate, - specialization, - general.default_euler_basis, - general.requested_fidelity, - general.calculated_fidelity, - general.unitary_matrix, + .a=0., + .b=0., + .c=0., + .global_phase=general.global_phase, + .K1l=general.K1l * general.K2l, + .K2l=identityGate, + .K1r=general.K1r * general.K2r, + .K2r=identityGate, + .specialization=specialization, + .default_euler_basis=general.default_euler_basis, + .requested_fidelity=general.requested_fidelity, + .calculated_fidelity=general.calculated_fidelity, + .unitary_matrix=general.unitary_matrix, }; } // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, @@ -972,36 +970,36 @@ struct GateDecompositionPattern final if (specialization == Specialization::SWAPEquiv) { if (c > 0.) { return TwoQubitWeylDecomposition{ - qc::PI_4, - qc::PI_4, - qc::PI_4, - general.global_phase, - general.K1l * general.K2r, - identityGate, - general.K1r * general.K2l, - identityGate, - specialization, - general.default_euler_basis, - general.requested_fidelity, - general.calculated_fidelity, - general.unitary_matrix, + .a=qc::PI_4, + .b=qc::PI_4, + .c=qc::PI_4, + .global_phase=general.global_phase, + .K1l=general.K1l * general.K2r, + .K2l=identityGate, + .K1r=general.K1r * general.K2l, + .K2r=identityGate, + .specialization=specialization, + .default_euler_basis=general.default_euler_basis, + .requested_fidelity=general.requested_fidelity, + .calculated_fidelity=general.calculated_fidelity, + .unitary_matrix=general.unitary_matrix, }; } else { flipped_from_original = true; return TwoQubitWeylDecomposition{ - qc::PI_4, - qc::PI_4, - qc::PI_4, - global_phase + qc::PI_2, - general.K1l * IPZ * general.K2r, - identityGate, - general.K1r * IPZ * general.K2l, - identityGate, - specialization, - general.default_euler_basis, - general.requested_fidelity, - general.calculated_fidelity, - general.unitary_matrix, + .a=qc::PI_4, + .b=qc::PI_4, + .c=qc::PI_4, + .global_phase=global_phase + qc::PI_2, + .K1l=general.K1l * IPZ * general.K2r, + .K2l=identityGate, + .K1r=general.K1r * IPZ * general.K2l, + .K2r=identityGate, + .specialization=specialization, + .default_euler_basis=general.default_euler_basis, + .requested_fidelity=general.requested_fidelity, + .calculated_fidelity=general.calculated_fidelity, + .unitary_matrix=general.unitary_matrix, }; } } @@ -1016,19 +1014,19 @@ struct GateDecompositionPattern final auto k2l_dag = general.K2l.transpose().conjugate(); return TwoQubitWeylDecomposition{ - closest, - closest, - closest, - general.global_phase, - general.K1l * general.K2l, - identityGate, - general.K1r * general.K2l, - k2l_dag * general.K2r, - specialization, - general.default_euler_basis, - general.requested_fidelity, - general.calculated_fidelity, - general.unitary_matrix, + .a=closest, + .b=closest, + .c=closest, + .global_phase=general.global_phase, + .K1l=general.K1l * general.K2l, + .K2l=identityGate, + .K1r=general.K1r * general.K2l, + .K2r=k2l_dag * general.K2r, + .specialization=specialization, + .default_euler_basis=general.default_euler_basis, + .requested_fidelity=general.requested_fidelity, + .calculated_fidelity=general.calculated_fidelity, + .unitary_matrix=general.unitary_matrix, }; } // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4) \sim @@ -1046,19 +1044,19 @@ struct GateDecompositionPattern final auto k2l_dag = general.K2l.transpose().conjugate(); return TwoQubitWeylDecomposition{ - closest, - closest, - -closest, - general.global_phase, - general.K1l * general.K2l, - identityGate, - general.K1r * IPZ * general.K2l * IPZ, - IPZ * k2l_dag * IPZ * general.K2r, - specialization, - general.default_euler_basis, - general.requested_fidelity, - general.calculated_fidelity, - general.unitary_matrix, + .a=closest, + .b=closest, + .c=-closest, + .global_phase=general.global_phase, + .K1l=general.K1l * general.K2l, + .K2l=identityGate, + .K1r=general.K1r * IPZ * general.K2l * IPZ, + .K2r=IPZ * k2l_dag * IPZ * general.K2r, + .specialization=specialization, + .default_euler_basis=general.default_euler_basis, + .requested_fidelity=general.requested_fidelity, + .calculated_fidelity=general.calculated_fidelity, + .unitary_matrix=general.unitary_matrix, }; } // :math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}` @@ -1074,19 +1072,19 @@ struct GateDecompositionPattern final auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = angles_from_unitary(general.K2r, euler_basis); return TwoQubitWeylDecomposition{ - a, - 0., - 0., - global_phase + k2lphase + k2rphase, - general.K1l * rx_matrix(k2lphi), - ry_matrix(k2ltheta) * rx_matrix(k2llambda), - general.K1r * rx_matrix(k2rphi), - ry_matrix(k2rtheta) * rx_matrix(k2rlambda), - specialization, - euler_basis, - general.requested_fidelity, - general.calculated_fidelity, - general.unitary_matrix, + .a=a, + .b=0., + .c=0., + .global_phase=global_phase + k2lphase + k2rphase, + .K1l=general.K1l * rx_matrix(k2lphi), + .K2l=ry_matrix(k2ltheta) * rx_matrix(k2llambda), + .K1r=general.K1r * rx_matrix(k2rphi), + .K2r=ry_matrix(k2rtheta) * rx_matrix(k2rlambda), + .specialization=specialization, + .default_euler_basis=euler_basis, + .requested_fidelity=general.requested_fidelity, + .calculated_fidelity=general.calculated_fidelity, + .unitary_matrix=general.unitary_matrix, }; } // :math:`U \sim U_d(\pi/4, \pi/4, \alpha) \sim \text{SWAP} \cdot @@ -1102,19 +1100,19 @@ struct GateDecompositionPattern final auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = angles_from_unitary(general.K2r, EulerBasis::ZYZ); return TwoQubitWeylDecomposition{ - qc::PI_4, - qc::PI_4, - c, - global_phase + k2lphase + k2rphase, - general.K1l * rz_matrix(k2rphi), - ry_matrix(k2ltheta) * rz_matrix(k2llambda), - general.K1r * rz_matrix(k2lphi), - ry_matrix(k2rtheta) * rz_matrix(k2rlambda), - specialization, - general.default_euler_basis, - general.requested_fidelity, - general.calculated_fidelity, - general.unitary_matrix, + .a=qc::PI_4, + .b=qc::PI_4, + .c=c, + .global_phase=global_phase + k2lphase + k2rphase, + .K1l=general.K1l * rz_matrix(k2rphi), + .K2l=ry_matrix(k2ltheta) * rz_matrix(k2llambda), + .K1r=general.K1r * rz_matrix(k2lphi), + .K2r=ry_matrix(k2rtheta) * rz_matrix(k2rlambda), + .specialization=specialization, + .default_euler_basis=general.default_euler_basis, + .requested_fidelity=general.requested_fidelity, + .calculated_fidelity=general.calculated_fidelity, + .unitary_matrix=general.unitary_matrix, }; } // :math:`U \sim U_d(\alpha, \alpha, \beta), \alpha \geq |\beta|` @@ -1126,19 +1124,19 @@ struct GateDecompositionPattern final auto [k2ltheta, k2lphi, k2llambda, k2lphase] = angles_from_unitary(general.K2l, EulerBasis::ZYZ); return TwoQubitWeylDecomposition{ - (a + b) / 2., - (a + b) / 2., - c, - global_phase + k2lphase, - general.K1l * rz_matrix(k2lphi), - ry_matrix(k2ltheta) * rz_matrix(k2llambda), - general.K1r * rz_matrix(k2lphi), - rz_matrix(-k2lphi) * general.K2r, - specialization, - general.default_euler_basis, - general.requested_fidelity, - general.calculated_fidelity, - general.unitary_matrix, + .a=(a + b) / 2., + .b=(a + b) / 2., + .c=c, + .global_phase=global_phase + k2lphase, + .K1l=general.K1l * rz_matrix(k2lphi), + .K2l=ry_matrix(k2ltheta) * rz_matrix(k2llambda), + .K1r=general.K1r * rz_matrix(k2lphi), + .K2r=rz_matrix(-k2lphi) * general.K2r, + .specialization=specialization, + .default_euler_basis=general.default_euler_basis, + .requested_fidelity=general.requested_fidelity, + .calculated_fidelity=general.calculated_fidelity, + .unitary_matrix=general.unitary_matrix, }; } // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` @@ -1151,19 +1149,19 @@ struct GateDecompositionPattern final auto [k2ltheta, k2lphi, k2llambda, k2lphase] = angles_from_unitary(general.K2l, euler_basis); return TwoQubitWeylDecomposition{ - a, - (b + c) / 2., - (b + c) / 2., - global_phase + k2lphase, - general.K1l * rx_matrix(k2lphi), - ry_matrix(k2ltheta) * rx_matrix(k2llambda), - general.K1r * rx_matrix(k2lphi), - rx_matrix(-k2lphi) * general.K2r, - specialization, - euler_basis, - general.requested_fidelity, - general.calculated_fidelity, - general.unitary_matrix, + .a=a, + .b=(b + c) / 2., + .c=(b + c) / 2., + .global_phase=global_phase + k2lphase, + .K1l=general.K1l * rx_matrix(k2lphi), + .K2l=ry_matrix(k2ltheta) * rx_matrix(k2llambda), + .K1r=general.K1r * rx_matrix(k2lphi), + .K2r=rx_matrix(-k2lphi) * general.K2r, + .specialization=specialization, + .default_euler_basis=euler_basis, + .requested_fidelity=general.requested_fidelity, + .calculated_fidelity=general.calculated_fidelity, + .unitary_matrix=general.unitary_matrix, }; } // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` @@ -1176,19 +1174,19 @@ struct GateDecompositionPattern final auto [k2ltheta, k2lphi, k2llambda, k2lphase] = angles_from_unitary(general.K2l, euler_basis); return TwoQubitWeylDecomposition{ - a, - (b - c) / 2., - -((b - c) / 2.), - global_phase + k2lphase, - general.K1l * rx_matrix(k2lphi), - ry_matrix(k2ltheta) * rx_matrix(k2llambda), - general.K1r * IPZ * rx_matrix(k2lphi) * IPZ, - IPZ * rx_matrix(-k2lphi) * IPZ * general.K2r, - specialization, - euler_basis, - general.requested_fidelity, - general.calculated_fidelity, - general.unitary_matrix, + .a=a, + .b=(b - c) / 2., + .c=-((b - c) / 2.), + .global_phase=global_phase + k2lphase, + .K1l=general.K1l * rx_matrix(k2lphi), + .K2l=ry_matrix(k2ltheta) * rx_matrix(k2llambda), + .K1r=general.K1r * IPZ * rx_matrix(k2lphi) * IPZ, + .K2r=IPZ * rx_matrix(-k2lphi) * IPZ * general.K2r, + .specialization=specialization, + .default_euler_basis=euler_basis, + .requested_fidelity=general.requested_fidelity, + .calculated_fidelity=general.calculated_fidelity, + .unitary_matrix=general.unitary_matrix, }; } // U has no special symmetry. @@ -1389,30 +1387,30 @@ struct GateDecompositionPattern final auto q2r = k2rd * K12R_ARR; return TwoQubitBasisDecomposer{ - basis_gate, - basis_fidelity, - euler_basis, - basis_decomposer, - super_controlled, - u0l, - u0r, - u1l, - u1ra, - u1rb, - u2la, - u2lb, - u2ra, - u2rb, - u3l, - u3r, - q0l, - q0r, - q1la, - q1lb, - q1ra, - q1rb, - q2l, - q2r, + .basis_gate=basis_gate, + .basis_fidelity=basis_fidelity, + .euler_basis=euler_basis, + .basis_decomposer=basis_decomposer, + .super_controlled=super_controlled, + .u0l=u0l, + .u0r=u0r, + .u1l=u1l, + .u1ra=u1ra, + .u1rb=u1rb, + .u2la=u2la, + .u2lb=u2lb, + .u2ra=u2ra, + .u2rb=u2rb, + .u3l=u3l, + .u3r=u3r, + .q0l=q0l, + .q0r=q0r, + .q1la=q1la, + .q1lb=q1lb, + .q1ra=q1ra, + .q1rb=q1rb, + .q2l=q2l, + .q2r=q2r, }; } diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index 3a153248e9..595ce25663 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -18,7 +18,7 @@ #include #include #include -#include +#include // TODO: unstable #include #include From 07147459b17cf14931d158d185adadd22c76aecf Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 11 Nov 2025 13:13:19 +0100 Subject: [PATCH 047/237] fix warnings, rename all variables --- .../Transforms/GateDecompositionPattern.cpp | 1140 ++++++++--------- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 32 +- 2 files changed, 587 insertions(+), 585 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 85eca547fa..e50cb5e5bf 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -16,12 +16,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include namespace mqt::ir::opt { @@ -60,19 +62,19 @@ struct GateDecompositionPattern final } matrix4x4 unitaryMatrix = - helpers::kroneckerProduct(identityGate, identityGate); + helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); int i{}; for (auto&& gate : series.gates) { auto gateMatrix = getTwoQubitMatrix({.type = helpers::getQcType(gate.op), .parameter = helpers::getParameters(gate.op), - .qubit_id = gate.qubitIds}); + .qubitId = gate.qubitIds}); unitaryMatrix = gateMatrix * unitaryMatrix; helpers::print(gateMatrix, "GATE MATRIX " + std::to_string(i++), true); } helpers::print(unitaryMatrix, "UNITARY MATRIX", true); - auto decomposer = TwoQubitBasisDecomposer::new_inner(); + auto decomposer = TwoQubitBasisDecomposer::newInner(); auto sequence = decomposer.twoQubitDecompose( unitaryMatrix, DEFAULT_FIDELITY, true, std::nullopt); if (!sequence) { @@ -147,12 +149,12 @@ struct GateDecompositionPattern final if (helpers::isSingleQubitOperation(initialOperation)) { inQubits = {in[0], mlir::Value{}}; outQubits = {out[0], mlir::Value{}}; - gates.push_back({initialOperation, {0}}); + gates.push_back({.op = initialOperation, .qubitIds = {0}}); complexity += 1; } else if (helpers::isTwoQubitOperation(initialOperation)) { inQubits = {in[0], in[1]}; outQubits = {out[0], out[1]}; - gates.push_back({initialOperation, {0, 1}}); + gates.push_back({.op = initialOperation, .qubitIds = {0, 1}}); complexity += 2; } } @@ -170,7 +172,7 @@ struct GateDecompositionPattern final std::size_t qubitId = std::distance(outQubits.begin(), it); *it = nextGate->getResult(0); - gates.push_back({nextGate, {qubitId}}); + gates.push_back({.op = nextGate, .qubitIds = {qubitId}}); complexity += 1; return true; } @@ -205,7 +207,8 @@ struct GateDecompositionPattern final std::size_t secondQubitId = std::distance(outQubits.begin(), secondQubitIt); - gates.push_back({nextGate, {firstQubitId, secondQubitId}}); + gates.push_back( + {.op = nextGate, .qubitIds = {firstQubitId, secondQubitId}}); complexity += 2; return true; } @@ -237,7 +240,7 @@ struct GateDecompositionPattern final static OpType createGate(mlir::PatternRewriter& rewriter, mlir::Location location, mlir::ValueRange inQubits, mlir::ValueRange ctrlQubits, - llvm::SmallVector parameters) { + const llvm::SmallVector& parameters) { mlir::SmallVector parameterValues; for (auto&& parameter : parameters) { auto parameterValue = rewriter.create( @@ -255,17 +258,17 @@ struct GateDecompositionPattern final struct Gate { qc::OpType type{qc::I}; llvm::SmallVector parameter; - llvm::SmallVector qubit_id = {0}; + llvm::SmallVector qubitId = {0}; }; std::vector gates; std::size_t complexity() { std::size_t c{}; for (auto&& gate : gates) { - c += gate.qubit_id.size(); + c += gate.qubitId.size(); } return c; } - fp globalPhase; + fp globalPhase{}; }; using OneQubitGateSequence = QubitGateSequence; using TwoQubitGateSequence = QubitGateSequence; @@ -287,11 +290,11 @@ struct GateDecompositionPattern final [&inQubits](const TwoQubitGateSequence::Gate& gateDescription, auto&& newGate) { auto results = newGate.getAllOutQubits(); - if (gateDescription.qubit_id.size() == 2) { - inQubits[gateDescription.qubit_id[0]] = results[0]; - inQubits[gateDescription.qubit_id[1]] = results[1]; - } else if (gateDescription.qubit_id.size() == 1) { - inQubits[gateDescription.qubit_id[0]] = results[0]; + if (gateDescription.qubitId.size() == 2) { + inQubits[gateDescription.qubitId[0]] = results[0]; + inQubits[gateDescription.qubitId[1]] = results[1]; + } else if (gateDescription.qubitId.size() == 1) { + inQubits[gateDescription.qubitId[0]] = results[0]; } else { throw std::logic_error{"Invalid number of qubit IDs!"}; } @@ -307,15 +310,15 @@ struct GateDecompositionPattern final std::cerr << qc::toString(gate.type) << ", "; if (gate.type == qc::X) { mlir::SmallVector inCtrlQubits; - if (gate.qubit_id.size() > 1) { - inCtrlQubits.push_back(inQubits[gate.qubit_id[1]]); + if (gate.qubitId.size() > 1) { + inCtrlQubits.push_back(inQubits[gate.qubitId[1]]); } auto newGate = createGate(rewriter, location, {inQubits[0]}, inCtrlQubits, gate.parameter); updateInQubits(gate, newGate); } else if (gate.type == qc::RX) { mlir::SmallVector qubits; - for (auto&& x : gate.qubit_id) { + for (auto&& x : gate.qubitId) { qubits.push_back(inQubits[x]); } auto newGate = @@ -323,7 +326,7 @@ struct GateDecompositionPattern final updateInQubits(gate, newGate); } else if (gate.type == qc::RY) { mlir::SmallVector qubits; - for (auto&& x : gate.qubit_id) { + for (auto&& x : gate.qubitId) { qubits.push_back(inQubits[x]); } auto newGate = @@ -331,7 +334,7 @@ struct GateDecompositionPattern final updateInQubits(gate, newGate); } else if (gate.type == qc::RZ) { mlir::SmallVector qubits; - for (auto&& x : gate.qubit_id) { + for (auto&& x : gate.qubitId) { qubits.push_back(inQubits[x]); } auto newGate = @@ -348,7 +351,7 @@ struct GateDecompositionPattern final } } - enum class Specialization { + enum class Specialization : std::uint8_t { General, IdEquiv, SWAPEquiv, @@ -358,17 +361,17 @@ struct GateDecompositionPattern final MirrorControlledEquiv, // These next 3 gates use the definition of fSim from eq (1) in: // https://arxiv.org/pdf/2001.08343.pdf - fSimaabEquiv, - fSimabbEquiv, - fSimabmbEquiv, + FSimaabEquiv, + FSimabbEquiv, + FSimabmbEquiv, }; - enum class MagicBasisTransform { + enum class MagicBasisTransform : std::uint8_t { Into, OutOf, }; - enum class EulerBasis { + enum class EulerBasis : std::uint8_t { U3 = 0, U321 = 1, U = 2, @@ -383,9 +386,9 @@ struct GateDecompositionPattern final ZSX = 11, }; - static constexpr auto sqrt2 = static_cast(1.4142135623730950488L); - static const matrix2x2 identityGate; - static const matrix2x2 hGate; + static constexpr auto SQRT2 = std::numbers::sqrt2_v; + static const matrix2x2 IDENTITY_GATE; + static const matrix2x2 H_GATE; static fp remEuclid(fp a, fp b) { auto r = std::fmod(a, b); @@ -408,14 +411,14 @@ struct GateDecompositionPattern final // https://docs.rs/faer/latest/faer/mat/generic/struct.Mat.html#method.self_adjoint_eigen static std::pair - self_adjoint_eigen_lower(rmatrix4x4 A) { + selfAdjointEigenLower(rmatrix4x4 a) { // rdiagonal4x4 S; // auto U = self_adjoint_evd(A, S); // rmatrix4x4 U; // jacobi_eigen_decomposition(A, U, S); - auto [U, S] = helpers::self_adjoint_evd(A); + auto [U, S] = helpers::selfAdjointEvd(a); // TODO: not in original code if (std::abs(U.determinant() + 1.0) < 1e-5) { @@ -429,31 +432,31 @@ struct GateDecompositionPattern final } static std::tuple - decompose_two_qubit_product_gate(matrix4x4 special_unitary) { - helpers::print(special_unitary, "SPECIAL_UNITARY"); + decomposeTwoQubitProductGate(matrix4x4 specialUnitary) { + helpers::print(specialUnitary, "SPECIAL_UNITARY"); // first quadrant - matrix2x2 r{{special_unitary(0, 0), special_unitary(0, 1)}, - {special_unitary(1, 0), special_unitary(1, 1)}}; - auto det_r = r.determinant(); - if (std::abs(det_r) < 0.1) { + matrix2x2 r{{specialUnitary(0, 0), specialUnitary(0, 1)}, + {specialUnitary(1, 0), specialUnitary(1, 1)}}; + auto detR = r.determinant(); + if (std::abs(detR) < 0.1) { // third quadrant - r = matrix2x2{{special_unitary(2, 0), special_unitary(2, 1)}, - {special_unitary(3, 0), special_unitary(3, 1)}}; - det_r = r.determinant(); + r = matrix2x2{{specialUnitary(2, 0), specialUnitary(2, 1)}, + {specialUnitary(3, 0), specialUnitary(3, 1)}}; + detR = r.determinant(); } - std::cerr << "DET_R: " << det_r << '\n'; - if (std::abs(det_r) < 0.1) { + std::cerr << "DET_R: " << detR << '\n'; + if (std::abs(detR) < 0.1) { throw std::runtime_error{ "decompose_two_qubit_product_gate: unable to decompose: det_r < 0.1"}; } - r /= std::sqrt(det_r); + r /= std::sqrt(detR); helpers::print(r, "R"); // transpose with complex conjugate of each element - matrix2x2 r_t_conj = r.transpose().conjugate(); + matrix2x2 rTConj = r.transpose().conjugate(); - auto temp = helpers::kroneckerProduct(identityGate, r_t_conj); + auto temp = helpers::kroneckerProduct(IDENTITY_GATE, rTConj); helpers::print(temp, "TEMP (decompose_two_qubit_product_gate, 1)"); - temp = special_unitary * temp; + temp = specialUnitary * temp; helpers::print(temp, "TEMP (decompose_two_qubit_product_gate, 2)"); // [[a, b, c, d], @@ -461,27 +464,27 @@ struct GateDecompositionPattern final // [i, j, k, l], [i, k]] // [m, n, o, p]] matrix2x2 l{{temp(0, 0), temp(0, 2)}, {temp(2, 0), temp(2, 2)}}; - auto det_l = l.determinant(); - if (std::abs(det_l) < 0.9) { + auto detL = l.determinant(); + if (std::abs(detL) < 0.9) { throw std::runtime_error{ "decompose_two_qubit_product_gate: unable to decompose: detL < 0.9"}; } - l /= std::sqrt(det_l); - auto phase = std::arg(det_l) / 2.; + l /= std::sqrt(detL); + auto phase = std::arg(detL) / 2.; return {l, r, phase}; } - static matrix4x4 magic_basis_transform(const matrix4x4& unitary, - MagicBasisTransform direction) { - const matrix4x4 B_NON_NORMALIZED{ + static matrix4x4 magicBasisTransform(const matrix4x4& unitary, + MagicBasisTransform direction) { + const matrix4x4 bNonNormalized{ {C_ONE, IM, C_ZERO, C_ZERO}, {C_ZERO, C_ZERO, IM, C_ONE}, {C_ZERO, C_ZERO, IM, C_M_ONE}, {C_ONE, M_IM, C_ZERO, C_ZERO}, }; - const matrix4x4 B_NON_NORMALIZED_DAGGER{ + const matrix4x4 bNonNormalizedDagger{ {qfp(0.5, 0.), C_ZERO, C_ZERO, qfp(0.5, 0.)}, {qfp(0., -0.5), C_ZERO, C_ZERO, qfp(0., 0.5)}, {C_ZERO, qfp(0., -0.5), qfp(0., -0.5), C_ZERO}, @@ -489,41 +492,41 @@ struct GateDecompositionPattern final }; helpers::print(unitary, "UNITARY in MAGIC BASIS TRANSFORM"); if (direction == MagicBasisTransform::OutOf) { - return B_NON_NORMALIZED_DAGGER * unitary * B_NON_NORMALIZED; + return bNonNormalizedDagger * unitary * bNonNormalized; } if (direction == MagicBasisTransform::Into) { - return B_NON_NORMALIZED * unitary * B_NON_NORMALIZED_DAGGER; + return bNonNormalized * unitary * bNonNormalizedDagger; } throw std::logic_error{"Unknown MagicBasisTransform direction!"}; } - static fp trace_to_fid(const qfp& x) { - auto x_abs = std::abs(x); - return (4.0 + x_abs * x_abs) / 20.0; + static fp traceToFid(const qfp& x) { + auto xAbs = std::abs(x); + return (4.0 + xAbs * xAbs) / 20.0; } - static fp closest_partial_swap(fp a, fp b, fp c) { + static fp closestPartialSwap(fp a, fp b, fp c) { auto m = (a + b + c) / 3.; auto [am, bm, cm] = std::array{a - m, b - m, c - m}; auto [ab, bc, ca] = std::array{a - b, b - c, c - a}; - return m + am * bm * cm * (6. + ab * ab + bc * bc + ca * ca) / 18.; + return m + (am * bm * cm * (6. + ab * ab + bc * bc + ca * ca) / 18.); } - static matrix2x2 rx_matrix(fp theta) { - auto half_theta = theta / 2.; - auto cos = qfp(std::cos(half_theta), 0.); - auto isin = qfp(0., -std::sin(half_theta)); + static matrix2x2 rxMatrix(fp theta) { + auto halfTheta = theta / 2.; + auto cos = qfp(std::cos(halfTheta), 0.); + auto isin = qfp(0., -std::sin(halfTheta)); return matrix2x2{{cos, isin}, {isin, cos}}; } - static matrix2x2 ry_matrix(fp theta) { - auto half_theta = theta / 2.; - auto cos = qfp(std::cos(half_theta), 0.); - auto sin = qfp(std::sin(half_theta), 0.); + static matrix2x2 ryMatrix(fp theta) { + auto halfTheta = theta / 2.; + auto cos = qfp(std::cos(halfTheta), 0.); + auto sin = qfp(std::sin(halfTheta), 0.); return matrix2x2{{cos, -sin}, {sin, cos}}; } - static matrix2x2 rz_matrix(fp theta) { + static matrix2x2 rzMatrix(fp theta) { return matrix2x2{{qfp{std::cos(theta / 2.), -std::sin(theta / 2.)}, 0}, {0, qfp{std::cos(theta / 2.), std::sin(theta / 2.)}}}; // auto ilam2 = qfp(0., 0.5 * theta); @@ -550,21 +553,21 @@ struct GateDecompositionPattern final {C_ZERO, C_ZERO, C_ZERO, {cosTheta, -sinTheta}}}; } - static std::array angles_from_unitary(const matrix2x2& matrix, - EulerBasis basis) { + static std::array anglesFromUnitary(const matrix2x2& matrix, + EulerBasis basis) { if (basis == EulerBasis::XYX) { - return params_xyx_inner(matrix); + return paramsXyxInner(matrix); } if (basis == EulerBasis::ZYZ) { - return params_zyz_inner(matrix); + return paramsZyzInner(matrix); } if (basis == EulerBasis::ZXZ) { - return params_zxz_inner(matrix); + return paramsZxzInner(matrix); } throw std::invalid_argument{"Unknown EulerBasis for angles_from_unitary"}; } - static std::array params_zyz_inner(const matrix2x2& matrix) { + static std::array paramsZyzInner(const matrix2x2& matrix) { auto detArg = std::arg(matrix.determinant()); auto phase = 0.5 * detArg; auto theta = @@ -576,13 +579,13 @@ struct GateDecompositionPattern final return {theta, phi, lam, phase}; } - static std::array params_zxz_inner(const matrix2x2& matrix) { - auto [theta, phi, lam, phase] = params_zyz_inner(matrix); - return {theta, phi + qc::PI / 2., lam - qc::PI / 2., phase}; + static std::array paramsZxzInner(const matrix2x2& matrix) { + auto [theta, phi, lam, phase] = paramsZyzInner(matrix); + return {theta, phi + (qc::PI / 2.), lam - (qc::PI / 2.), phase}; } - static std::array params_xyx_inner(const matrix2x2& matrix) { - auto mat_zyz = matrix2x2{ + static std::array paramsXyxInner(const matrix2x2& matrix) { + auto matZyz = matrix2x2{ {static_cast(0.5) * (matrix(0, 0) + matrix(0, 1) + matrix(1, 0) + matrix(1, 1)), static_cast(0.5) * @@ -592,14 +595,14 @@ struct GateDecompositionPattern final static_cast(0.5) * (matrix(0, 0) - matrix(0, 1) - matrix(1, 0) + matrix(1, 1))}, }; - auto [theta, phi, lam, phase] = params_zyz_inner(mat_zyz); - auto new_phi = mod2pi(phi + qc::PI, 0.); - auto new_lam = mod2pi(lam + qc::PI, 0.); + auto [theta, phi, lam, phase] = paramsZyzInner(matZyz); + auto newPhi = mod2pi(phi + qc::PI, 0.); + auto newLam = mod2pi(lam + qc::PI, 0.); return { theta, - new_phi, - new_lam, - phase + (new_phi + new_lam - phi - lam) / 2., + newPhi, + newLam, + phase + ((newPhi + newLam - phi - lam) / 2.), }; } @@ -613,19 +616,19 @@ struct GateDecompositionPattern final {qfp{0.5, -0.5}, qfp{0.5, 0.5}}}; } if (gate.type == qc::RX) { - return rx_matrix(gate.parameter[0]); + return rxMatrix(gate.parameter[0]); } if (gate.type == qc::RY) { - return ry_matrix(gate.parameter[0]); + return ryMatrix(gate.parameter[0]); } if (gate.type == qc::RZ) { - return rz_matrix(gate.parameter[0]); + return rzMatrix(gate.parameter[0]); } if (gate.type == qc::X) { return matrix2x2{{0, 1}, {1, 0}}; } if (gate.type == qc::I) { - return identityGate; + return IDENTITY_GATE; } if (gate.type == qc::H) { static constexpr fp SQRT2_2 = static_cast( @@ -640,26 +643,26 @@ struct GateDecompositionPattern final static matrix4x4 getTwoQubitMatrix(const QubitGateSequence::Gate& gate) { using helpers::kroneckerProduct; - if (gate.qubit_id.empty()) { - return kroneckerProduct(identityGate, identityGate); + if (gate.qubitId.empty()) { + return kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); } - if (gate.qubit_id.size() == 1) { - if (gate.qubit_id[0] == 0) { - return kroneckerProduct(identityGate, getSingleQubitMatrix(gate)); + if (gate.qubitId.size() == 1) { + if (gate.qubitId[0] == 0) { + return kroneckerProduct(IDENTITY_GATE, getSingleQubitMatrix(gate)); } - if (gate.qubit_id[0] == 1) { - return kroneckerProduct(getSingleQubitMatrix(gate), identityGate); + if (gate.qubitId[0] == 1) { + return kroneckerProduct(getSingleQubitMatrix(gate), IDENTITY_GATE); } throw std::logic_error{"Invalid qubit ID in getTwoQubitMatrix"}; } - if (gate.qubit_id.size() == 2) { + if (gate.qubitId.size() == 2) { if (gate.type == qc::X) { // controlled X (CX) - if (gate.qubit_id == llvm::SmallVector{1, 0}) { + if (gate.qubitId == llvm::SmallVector{1, 0}) { return matrix4x4{ {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}}; } - if (gate.qubit_id == llvm::SmallVector{0, 1}) { + if (gate.qubitId == llvm::SmallVector{0, 1}) { return matrix4x4{ {1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}, {0, 1, 0, 0}}; } @@ -688,31 +691,31 @@ struct GateDecompositionPattern final fp a; fp b; fp c; - fp global_phase; - matrix2x2 K1l; - matrix2x2 K2l; - matrix2x2 K1r; - matrix2x2 K2r; + fp globalPhase; + matrix2x2 k1l; + matrix2x2 k2l; + matrix2x2 k1r; + matrix2x2 k2r; Specialization specialization; - EulerBasis default_euler_basis; - std::optional requested_fidelity; - fp calculated_fidelity; - matrix4x4 unitary_matrix; + EulerBasis defaultEulerBasis; + std::optional requestedFidelity; + fp calculatedFidelity; + matrix4x4 unitaryMatrix; static TwoQubitWeylDecomposition - new_inner(matrix4x4 unitary_matrix, std::optional fidelity, - std::optional _specialization) { - auto& u = unitary_matrix; - auto det_u = u.determinant(); - std::cerr << "DET_U: " << det_u << '\n'; - auto det_pow = std::pow(det_u, static_cast(-0.25)); - u *= det_pow; + newInner(matrix4x4 unitaryMatrix, std::optional fidelity, + std::optional specialization) { + auto& u = unitaryMatrix; + auto detU = u.determinant(); + std::cerr << "DET_U: " << detU << '\n'; + auto detPow = std::pow(detU, static_cast(-0.25)); + u *= detPow; helpers::print(u, "U", true); - auto global_phase = std::arg(det_u) / 4.; - auto u_p = magic_basis_transform(u, MagicBasisTransform::OutOf); - helpers::print(u_p, "U_P", true); - matrix4x4 m2 = u_p.transpose() * u_p; - auto default_euler_basis = EulerBasis::ZYZ; + auto globalPhase = std::arg(detU) / 4.; + auto uP = magicBasisTransform(u, MagicBasisTransform::OutOf); + helpers::print(uP, "U_P", true); + matrix4x4 m2 = uP.transpose() * uP; + auto defaultEulerBasis = EulerBasis::ZYZ; helpers::print(m2, "M2", true); std::cerr << "DET_U after division: " << u.determinant() << '\n'; @@ -738,35 +741,35 @@ struct GateDecompositionPattern final matrix4x4 p = matrix4x4::Zero(); for (int i = 0; i < 100; ++i) { - fp rand_a; - fp rand_b; + fp randA{}; + fp randB{}; // For debugging the algorithm use the same RNG values from the // previous Python implementation for the first random trial. // In most cases this loop only executes a single iteration and // using the same rng values rules out possible RNG differences // as the root cause of a test failure if (i == 0) { - rand_a = 1.2602066112249388; - rand_b = 0.22317849046722027; + randA = 1.2602066112249388; + randB = 0.22317849046722027; } else { - rand_a = dist(state); - rand_b = dist(state); + randA = dist(state); + randB = dist(state); } - rmatrix4x4 m2_real = rand_a * m2.real() + rand_b * m2.imag(); - rmatrix4x4 p_inner_real = self_adjoint_eigen_lower(m2_real).first; - matrix4x4 p_inner = p_inner_real; - diagonal4x4 d_inner = (p_inner.transpose() * m2 * p_inner).diagonal(); + rmatrix4x4 m2Real = randA * m2.real() + randB * m2.imag(); + rmatrix4x4 pInnerReal = selfAdjointEigenLower(m2Real).first; + matrix4x4 pInner = pInnerReal; + diagonal4x4 dInner = (pInner.transpose() * m2 * pInner).diagonal(); - helpers::print(d_inner, "D_INNER", true); - helpers::print(p_inner, "P_INNER", true); - matrix4x4 diag_d = d_inner.asDiagonal(); + helpers::print(dInner, "D_INNER", true); + helpers::print(pInner, "P_INNER", true); + matrix4x4 diagD = dInner.asDiagonal(); - matrix4x4 compare = p_inner * diag_d * p_inner.transpose(); + matrix4x4 compare = pInner * diagD * pInner.transpose(); helpers::print(compare, "COMPARE"); found = (compare - m2).cwiseAbs().cwiseLessOrEqual(1.0e-13).all(); if (found) { - p = p_inner; - d = d_inner; + p = pInner; + d = dInner; break; } } @@ -774,13 +777,13 @@ struct GateDecompositionPattern final throw std::runtime_error{ "TwoQubitWeylDecomposition: failed to diagonalize M2."}; } - rdiagonal4x4 d_real = -1.0 * d.cwiseArg() / 2.0; - helpers::print(d_real, "D_REAL", true); - d_real(3) = -d_real(0) - d_real(1) - d_real(2); - std::array cs; - for (std::size_t i = 0; i < cs.size(); ++i) { - assert(i < d_real.size()); - cs[i] = remEuclid((d_real(i) + d_real(3)) / 2.0, qc::TAU); + rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; + helpers::print(dReal, "D_REAL", true); + dReal(3) = -dReal(0) - dReal(1) - dReal(2); + std::array cs{}; + for (int i = 0; i < static_cast(cs.size()); ++i) { + assert(i < dReal.size()); + cs[i] = remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); } helpers::print(cs, "CS", true); decltype(cs) cstemp; @@ -788,24 +791,23 @@ struct GateDecompositionPattern final auto tmp = remEuclid(x, qc::PI_2); return std::min(tmp, qc::PI_2 - tmp); }); - std::array order{ + std::array order{ 2, 1, 0}; // TODO: needs to be adjusted depending on eigenvector // order in eigen decomposition algorithm? llvm::stable_sort(order, [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); std::tie(order[0], order[1], order[2]) = std::tuple{order[1], order[2], order[0]}; - helpers::print(order, "ORDER", true); std::tie(cs[0], cs[1], cs[2]) = std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; - std::tie(d_real(0), d_real(1), d_real(2)) = - std::tuple{d_real(order[0]), d_real(order[1]), d_real(order[2])}; - helpers::print(d_real, "D_REAL (sorted)", true); + std::tie(dReal(0), dReal(1), dReal(2)) = + std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2])}; + helpers::print(dReal, "D_REAL (sorted)", true); // swap columns of p according to order - auto p_orig = p; - for (std::size_t i = 0; i < order.size(); ++i) { - p.col(i) = p_orig.col(order[i]); + auto pOrig = p; + for (int i = 0; i < static_cast(order.size()); ++i) { + p.col(i) = pOrig.col(order[i]); } if (p.determinant().real() < 0.0) { @@ -813,31 +815,30 @@ struct GateDecompositionPattern final p.col(lastColumnIndex) = -p.col(lastColumnIndex); } - matrix4x4 temp = d_real.asDiagonal(); + matrix4x4 temp = dReal.asDiagonal(); temp *= IM; temp = temp.exp(); helpers::print(temp, "TEMP"); helpers::print(p, "P", true); - auto k1 = - magic_basis_transform(u_p * p * temp, MagicBasisTransform::Into); - auto k2 = magic_basis_transform(p.transpose(), MagicBasisTransform::Into); + auto k1 = magicBasisTransform(uP * p * temp, MagicBasisTransform::Into); + auto k2 = magicBasisTransform(p.transpose(), MagicBasisTransform::Into); - auto [K1l, K1r, phase_l] = decompose_two_qubit_product_gate(k1); - auto [K2l, K2r, phase_r] = decompose_two_qubit_product_gate(k2); - global_phase += phase_l + phase_r; + auto [K1l, K1r, phase_l] = decomposeTwoQubitProductGate(k1); + auto [K2l, K2r, phase_r] = decomposeTwoQubitProductGate(k2); + globalPhase += phase_l + phase_r; // Flip into Weyl chamber if (cs[0] > qc::PI_2) { cs[0] -= 3.0 * qc::PI_2; K1l = K1l * IPY; K1r = K1r * IPY; - global_phase += qc::PI_2; + globalPhase += qc::PI_2; } if (cs[1] > qc::PI_2) { cs[1] -= 3.0 * qc::PI_2; K1l = K1l * IPX; K1r = K1r * IPX; - global_phase += qc::PI_2; + globalPhase += qc::PI_2; } auto conjs = 0; if (cs[0] > qc::PI_4) { @@ -845,41 +846,41 @@ struct GateDecompositionPattern final K1l = K1l * IPY; K2r = IPY * K2r; conjs += 1; - global_phase -= qc::PI_2; + globalPhase -= qc::PI_2; } if (cs[1] > qc::PI_4) { cs[1] = qc::PI_2 - cs[1]; K1l = K1l * IPX; K2r = IPX * K2r; conjs += 1; - global_phase += qc::PI_2; + globalPhase += qc::PI_2; if (conjs == 1) { - global_phase -= qc::PI; + globalPhase -= qc::PI; } } if (cs[2] > qc::PI_2) { cs[2] -= 3.0 * qc::PI_2; K1l = K1l * IPZ; K1r = K1r * IPZ; - global_phase += qc::PI_2; + globalPhase += qc::PI_2; if (conjs == 1) { - global_phase -= qc::PI; + globalPhase -= qc::PI; } } if (conjs == 1) { cs[2] = qc::PI_2 - cs[2]; K1l = K1l * IPZ; K2r = IPZ * K2r; - global_phase += qc::PI_2; + globalPhase += qc::PI_2; } if (cs[2] > qc::PI_4) { cs[2] -= qc::PI_2; K1l = K1l * IPZ; K1r = K1r * IPZ; - global_phase -= qc::PI_2; + globalPhase -= qc::PI_2; } auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); - auto is_close = [&](fp ap, fp bp, fp cp) -> bool { + auto isClose = [&](fp ap, fp bp, fp cp) -> bool { auto da = a - ap; auto db = b - bp; auto dc = c - cp; @@ -887,78 +888,84 @@ struct GateDecompositionPattern final qfp(std::cos(da) * std::cos(db) * std::cos(dc), std::sin(da) * std::sin(db) * std::sin(dc)); if (fidelity) { - return trace_to_fid(tr) >= *fidelity; + return traceToFid(tr) >= *fidelity; } return false; }; - auto closest_abc = closest_partial_swap(a, b, c); - auto closest_ab_minus_c = closest_partial_swap(a, b, -c); - auto flipped_from_original = false; + auto closestAbc = closestPartialSwap(a, b, c); + auto closestAbMinusC = closestPartialSwap(a, b, -c); + auto flippedFromOriginal = false; - auto get_default_specialzation = [&]() { - if (is_close(0., 0., 0.)) { + auto getDefaultSpecialzation = [&]() { + if (isClose(0., 0., 0.)) { return Specialization::IdEquiv; - } else if (is_close(qc::PI_4, qc::PI_4, qc::PI_4) || - is_close(qc::PI_4, qc::PI_4, -qc::PI_4)) { + } + if (isClose(qc::PI_4, qc::PI_4, qc::PI_4) || + isClose(qc::PI_4, qc::PI_4, -qc::PI_4)) { return Specialization::SWAPEquiv; - } else if (is_close(closest_abc, closest_abc, closest_abc)) { + } + if (isClose(closestAbc, closestAbc, closestAbc)) { return Specialization::PartialSWAPEquiv; - } else if (is_close(closest_ab_minus_c, closest_ab_minus_c, - -closest_ab_minus_c)) { + } + if (isClose(closestAbMinusC, closestAbMinusC, -closestAbMinusC)) { return Specialization::PartialSWAPFlipEquiv; - } else if (is_close(a, 0., 0.)) { + } + if (isClose(a, 0., 0.)) { return Specialization::ControlledEquiv; - } else if (is_close(qc::PI_4, qc::PI_4, c)) { + } + if (isClose(qc::PI_4, qc::PI_4, c)) { return Specialization::MirrorControlledEquiv; - } else if (is_close((a + b) / 2., (a + b) / 2., c)) { - return Specialization::fSimaabEquiv; - } else if (is_close(a, (b + c) / 2., (b + c) / 2.)) { - return Specialization::fSimabbEquiv; - } else if (is_close(a, (b - c) / 2., (c - b) / 2.)) { - return Specialization::fSimabmbEquiv; - } else { - return Specialization::General; } + if (isClose((a + b) / 2., (a + b) / 2., c)) { + return Specialization::FSimaabEquiv; + } + if (isClose(a, (b + c) / 2., (b + c) / 2.)) { + return Specialization::FSimabbEquiv; + } + if (isClose(a, (b - c) / 2., (c - b) / 2.)) { + return Specialization::FSimabmbEquiv; + } + return Specialization::General; }; - auto specialization = - _specialization.value_or(get_default_specialzation()); + auto actualSpecialization = + specialization.value_or(getDefaultSpecialzation()); TwoQubitWeylDecomposition general{ - .a=a, - .b=b, - .c=c, - .global_phase=global_phase, - .K1l=K1l, - .K2l=K2l, - .K1r=K1r, - .K2r=K2r, - .specialization=Specialization::General, - .default_euler_basis=default_euler_basis, - .requested_fidelity=fidelity, - .calculated_fidelity=-1.0, - .unitary_matrix=unitary_matrix, + .a = a, + .b = b, + .c = c, + .globalPhase = globalPhase, + .k1l = K1l, + .k2l = K2l, + .k1r = K1r, + .k2r = K2r, + .specialization = Specialization::General, + .defaultEulerBasis = defaultEulerBasis, + .requestedFidelity = fidelity, + .calculatedFidelity = -1.0, + .unitaryMatrix = unitaryMatrix, }; - auto get_specialized_decomposition = [&]() { + auto getSpecializedDecomposition = [&]() { // :math:`U \sim U_d(0,0,0) \sim Id` // // This gate binds 0 parameters, we make it canonical by // setting // :math:`K2_l = Id` , :math:`K2_r = Id`. - if (specialization == Specialization::IdEquiv) { + if (actualSpecialization == Specialization::IdEquiv) { return TwoQubitWeylDecomposition{ - .a=0., - .b=0., - .c=0., - .global_phase=general.global_phase, - .K1l=general.K1l * general.K2l, - .K2l=identityGate, - .K1r=general.K1r * general.K2r, - .K2r=identityGate, - .specialization=specialization, - .default_euler_basis=general.default_euler_basis, - .requested_fidelity=general.requested_fidelity, - .calculated_fidelity=general.calculated_fidelity, - .unitary_matrix=general.unitary_matrix, + .a = 0., + .b = 0., + .c = 0., + .globalPhase = general.globalPhase, + .k1l = general.k1l * general.k2l, + .k2l = IDENTITY_GATE, + .k1r = general.k1r * general.k2r, + .k2r = IDENTITY_GATE, + .specialization = actualSpecialization, + .defaultEulerBasis = general.defaultEulerBasis, + .requestedFidelity = general.requestedFidelity, + .calculatedFidelity = general.calculatedFidelity, + .unitaryMatrix = general.unitaryMatrix, }; } // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, @@ -967,41 +974,40 @@ struct GateDecompositionPattern final // This gate binds 0 parameters, we make it canonical by // setting // :math:`K2_l = Id` , :math:`K2_r = Id`. - if (specialization == Specialization::SWAPEquiv) { + if (actualSpecialization == Specialization::SWAPEquiv) { if (c > 0.) { return TwoQubitWeylDecomposition{ - .a=qc::PI_4, - .b=qc::PI_4, - .c=qc::PI_4, - .global_phase=general.global_phase, - .K1l=general.K1l * general.K2r, - .K2l=identityGate, - .K1r=general.K1r * general.K2l, - .K2r=identityGate, - .specialization=specialization, - .default_euler_basis=general.default_euler_basis, - .requested_fidelity=general.requested_fidelity, - .calculated_fidelity=general.calculated_fidelity, - .unitary_matrix=general.unitary_matrix, - }; - } else { - flipped_from_original = true; - return TwoQubitWeylDecomposition{ - .a=qc::PI_4, - .b=qc::PI_4, - .c=qc::PI_4, - .global_phase=global_phase + qc::PI_2, - .K1l=general.K1l * IPZ * general.K2r, - .K2l=identityGate, - .K1r=general.K1r * IPZ * general.K2l, - .K2r=identityGate, - .specialization=specialization, - .default_euler_basis=general.default_euler_basis, - .requested_fidelity=general.requested_fidelity, - .calculated_fidelity=general.calculated_fidelity, - .unitary_matrix=general.unitary_matrix, + .a = qc::PI_4, + .b = qc::PI_4, + .c = qc::PI_4, + .globalPhase = general.globalPhase, + .k1l = general.k1l * general.k2r, + .k2l = IDENTITY_GATE, + .k1r = general.k1r * general.k2l, + .k2r = IDENTITY_GATE, + .specialization = actualSpecialization, + .defaultEulerBasis = general.defaultEulerBasis, + .requestedFidelity = general.requestedFidelity, + .calculatedFidelity = general.calculatedFidelity, + .unitaryMatrix = general.unitaryMatrix, }; } + flippedFromOriginal = true; + return TwoQubitWeylDecomposition{ + .a = qc::PI_4, + .b = qc::PI_4, + .c = qc::PI_4, + .globalPhase = globalPhase + qc::PI_2, + .k1l = general.k1l * IPZ * general.k2r, + .k2l = IDENTITY_GATE, + .k1r = general.k1r * IPZ * general.k2l, + .k2r = IDENTITY_GATE, + .specialization = actualSpecialization, + .defaultEulerBasis = general.defaultEulerBasis, + .requestedFidelity = general.requestedFidelity, + .calculatedFidelity = general.calculatedFidelity, + .unitaryMatrix = general.unitaryMatrix, + }; } // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4) \sim // \text{SWAP}^\alpha` @@ -1009,24 +1015,24 @@ struct GateDecompositionPattern final // This gate binds 3 parameters, we make it canonical by setting: // // :math:`K2_l = Id`. - if (specialization == Specialization::PartialSWAPEquiv) { - auto closest = closest_partial_swap(a, b, c); - auto k2l_dag = general.K2l.transpose().conjugate(); + if (actualSpecialization == Specialization::PartialSWAPEquiv) { + auto closest = closestPartialSwap(a, b, c); + auto k2lDag = general.k2l.transpose().conjugate(); return TwoQubitWeylDecomposition{ - .a=closest, - .b=closest, - .c=closest, - .global_phase=general.global_phase, - .K1l=general.K1l * general.K2l, - .K2l=identityGate, - .K1r=general.K1r * general.K2l, - .K2r=k2l_dag * general.K2r, - .specialization=specialization, - .default_euler_basis=general.default_euler_basis, - .requested_fidelity=general.requested_fidelity, - .calculated_fidelity=general.calculated_fidelity, - .unitary_matrix=general.unitary_matrix, + .a = closest, + .b = closest, + .c = closest, + .globalPhase = general.globalPhase, + .k1l = general.k1l * general.k2l, + .k2l = IDENTITY_GATE, + .k1r = general.k1r * general.k2l, + .k2r = k2lDag * general.k2r, + .specialization = actualSpecialization, + .defaultEulerBasis = general.defaultEulerBasis, + .requestedFidelity = general.requestedFidelity, + .calculatedFidelity = general.calculatedFidelity, + .unitaryMatrix = general.unitaryMatrix, }; } // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4) \sim @@ -1039,24 +1045,24 @@ struct GateDecompositionPattern final // This gate binds 3 parameters, we make it canonical by setting: // // :math:`K2_l = Id` - if (specialization == Specialization::PartialSWAPFlipEquiv) { - auto closest = closest_partial_swap(a, b, -c); - auto k2l_dag = general.K2l.transpose().conjugate(); + if (actualSpecialization == Specialization::PartialSWAPFlipEquiv) { + auto closest = closestPartialSwap(a, b, -c); + auto k2lDag = general.k2l.transpose().conjugate(); return TwoQubitWeylDecomposition{ - .a=closest, - .b=closest, - .c=-closest, - .global_phase=general.global_phase, - .K1l=general.K1l * general.K2l, - .K2l=identityGate, - .K1r=general.K1r * IPZ * general.K2l * IPZ, - .K2r=IPZ * k2l_dag * IPZ * general.K2r, - .specialization=specialization, - .default_euler_basis=general.default_euler_basis, - .requested_fidelity=general.requested_fidelity, - .calculated_fidelity=general.calculated_fidelity, - .unitary_matrix=general.unitary_matrix, + .a = closest, + .b = closest, + .c = -closest, + .globalPhase = general.globalPhase, + .k1l = general.k1l * general.k2l, + .k2l = IDENTITY_GATE, + .k1r = general.k1r * IPZ * general.k2l * IPZ, + .k2r = IPZ * k2lDag * IPZ * general.k2r, + .specialization = actualSpecialization, + .defaultEulerBasis = general.defaultEulerBasis, + .requestedFidelity = general.requestedFidelity, + .calculatedFidelity = general.calculatedFidelity, + .unitaryMatrix = general.unitaryMatrix, }; } // :math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}` @@ -1065,26 +1071,26 @@ struct GateDecompositionPattern final // // :math:`K2_l = Ry(\theta_l) Rx(\lambda_l)` , // :math:`K2_r = Ry(\theta_r) Rx(\lambda_r)` . - if (specialization == Specialization::ControlledEquiv) { - auto euler_basis = EulerBasis::XYX; + if (actualSpecialization == Specialization::ControlledEquiv) { + auto eulerBasis = EulerBasis::XYX; auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l, euler_basis); + anglesFromUnitary(general.k2l, eulerBasis); auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = - angles_from_unitary(general.K2r, euler_basis); + anglesFromUnitary(general.k2r, eulerBasis); return TwoQubitWeylDecomposition{ - .a=a, - .b=0., - .c=0., - .global_phase=global_phase + k2lphase + k2rphase, - .K1l=general.K1l * rx_matrix(k2lphi), - .K2l=ry_matrix(k2ltheta) * rx_matrix(k2llambda), - .K1r=general.K1r * rx_matrix(k2rphi), - .K2r=ry_matrix(k2rtheta) * rx_matrix(k2rlambda), - .specialization=specialization, - .default_euler_basis=euler_basis, - .requested_fidelity=general.requested_fidelity, - .calculated_fidelity=general.calculated_fidelity, - .unitary_matrix=general.unitary_matrix, + .a = a, + .b = 0., + .c = 0., + .globalPhase = globalPhase + k2lphase + k2rphase, + .k1l = general.k1l * rxMatrix(k2lphi), + .k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda), + .k1r = general.k1r * rxMatrix(k2rphi), + .k2r = ryMatrix(k2rtheta) * rxMatrix(k2rlambda), + .specialization = actualSpecialization, + .defaultEulerBasis = eulerBasis, + .requestedFidelity = general.requestedFidelity, + .calculatedFidelity = general.calculatedFidelity, + .unitaryMatrix = general.unitaryMatrix, }; } // :math:`U \sim U_d(\pi/4, \pi/4, \alpha) \sim \text{SWAP} \cdot @@ -1094,25 +1100,25 @@ struct GateDecompositionPattern final // // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` , :math:`K2_r = // Ry(\theta_r)\cdot Rz(\lambda_r)` - if (specialization == Specialization::MirrorControlledEquiv) { + if (actualSpecialization == Specialization::MirrorControlledEquiv) { auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l, EulerBasis::ZYZ); + anglesFromUnitary(general.k2l, EulerBasis::ZYZ); auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = - angles_from_unitary(general.K2r, EulerBasis::ZYZ); + anglesFromUnitary(general.k2r, EulerBasis::ZYZ); return TwoQubitWeylDecomposition{ - .a=qc::PI_4, - .b=qc::PI_4, - .c=c, - .global_phase=global_phase + k2lphase + k2rphase, - .K1l=general.K1l * rz_matrix(k2rphi), - .K2l=ry_matrix(k2ltheta) * rz_matrix(k2llambda), - .K1r=general.K1r * rz_matrix(k2lphi), - .K2r=ry_matrix(k2rtheta) * rz_matrix(k2rlambda), - .specialization=specialization, - .default_euler_basis=general.default_euler_basis, - .requested_fidelity=general.requested_fidelity, - .calculated_fidelity=general.calculated_fidelity, - .unitary_matrix=general.unitary_matrix, + .a = qc::PI_4, + .b = qc::PI_4, + .c = c, + .globalPhase = globalPhase + k2lphase + k2rphase, + .k1l = general.k1l * rzMatrix(k2rphi), + .k2l = ryMatrix(k2ltheta) * rzMatrix(k2llambda), + .k1r = general.k1r * rzMatrix(k2lphi), + .k2r = ryMatrix(k2rtheta) * rzMatrix(k2rlambda), + .specialization = actualSpecialization, + .defaultEulerBasis = general.defaultEulerBasis, + .requestedFidelity = general.requestedFidelity, + .calculatedFidelity = general.calculatedFidelity, + .unitaryMatrix = general.unitaryMatrix, }; } // :math:`U \sim U_d(\alpha, \alpha, \beta), \alpha \geq |\beta|` @@ -1120,23 +1126,23 @@ struct GateDecompositionPattern final // This gate binds 5 parameters, we make it canonical by setting: // // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)`. - if (specialization == Specialization::fSimaabEquiv) { + if (actualSpecialization == Specialization::FSimaabEquiv) { auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l, EulerBasis::ZYZ); + anglesFromUnitary(general.k2l, EulerBasis::ZYZ); return TwoQubitWeylDecomposition{ - .a=(a + b) / 2., - .b=(a + b) / 2., - .c=c, - .global_phase=global_phase + k2lphase, - .K1l=general.K1l * rz_matrix(k2lphi), - .K2l=ry_matrix(k2ltheta) * rz_matrix(k2llambda), - .K1r=general.K1r * rz_matrix(k2lphi), - .K2r=rz_matrix(-k2lphi) * general.K2r, - .specialization=specialization, - .default_euler_basis=general.default_euler_basis, - .requested_fidelity=general.requested_fidelity, - .calculated_fidelity=general.calculated_fidelity, - .unitary_matrix=general.unitary_matrix, + .a = (a + b) / 2., + .b = (a + b) / 2., + .c = c, + .globalPhase = globalPhase + k2lphase, + .k1l = general.k1l * rzMatrix(k2lphi), + .k2l = ryMatrix(k2ltheta) * rzMatrix(k2llambda), + .k1r = general.k1r * rzMatrix(k2lphi), + .k2r = rzMatrix(-k2lphi) * general.k2r, + .specialization = actualSpecialization, + .defaultEulerBasis = general.defaultEulerBasis, + .requestedFidelity = general.requestedFidelity, + .calculatedFidelity = general.calculatedFidelity, + .unitaryMatrix = general.unitaryMatrix, }; } // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` @@ -1144,24 +1150,24 @@ struct GateDecompositionPattern final // This gate binds 5 parameters, we make it canonical by setting: // // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` - if (specialization == Specialization::fSimabbEquiv) { - auto euler_basis = EulerBasis::XYX; + if (actualSpecialization == Specialization::FSimabbEquiv) { + auto eulerBasis = EulerBasis::XYX; auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l, euler_basis); + anglesFromUnitary(general.k2l, eulerBasis); return TwoQubitWeylDecomposition{ - .a=a, - .b=(b + c) / 2., - .c=(b + c) / 2., - .global_phase=global_phase + k2lphase, - .K1l=general.K1l * rx_matrix(k2lphi), - .K2l=ry_matrix(k2ltheta) * rx_matrix(k2llambda), - .K1r=general.K1r * rx_matrix(k2lphi), - .K2r=rx_matrix(-k2lphi) * general.K2r, - .specialization=specialization, - .default_euler_basis=euler_basis, - .requested_fidelity=general.requested_fidelity, - .calculated_fidelity=general.calculated_fidelity, - .unitary_matrix=general.unitary_matrix, + .a = a, + .b = (b + c) / 2., + .c = (b + c) / 2., + .globalPhase = globalPhase + k2lphase, + .k1l = general.k1l * rxMatrix(k2lphi), + .k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda), + .k1r = general.k1r * rxMatrix(k2lphi), + .k2r = rxMatrix(-k2lphi) * general.k2r, + .specialization = actualSpecialization, + .defaultEulerBasis = eulerBasis, + .requestedFidelity = general.requestedFidelity, + .calculatedFidelity = general.calculatedFidelity, + .unitaryMatrix = general.unitaryMatrix, }; } // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` @@ -1169,40 +1175,40 @@ struct GateDecompositionPattern final // This gate binds 5 parameters, we make it canonical by setting: // // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` - if (specialization == Specialization::fSimabmbEquiv) { - auto euler_basis = EulerBasis::XYX; + if (actualSpecialization == Specialization::FSimabmbEquiv) { + auto eulerBasis = EulerBasis::XYX; auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - angles_from_unitary(general.K2l, euler_basis); + anglesFromUnitary(general.k2l, eulerBasis); return TwoQubitWeylDecomposition{ - .a=a, - .b=(b - c) / 2., - .c=-((b - c) / 2.), - .global_phase=global_phase + k2lphase, - .K1l=general.K1l * rx_matrix(k2lphi), - .K2l=ry_matrix(k2ltheta) * rx_matrix(k2llambda), - .K1r=general.K1r * IPZ * rx_matrix(k2lphi) * IPZ, - .K2r=IPZ * rx_matrix(-k2lphi) * IPZ * general.K2r, - .specialization=specialization, - .default_euler_basis=euler_basis, - .requested_fidelity=general.requested_fidelity, - .calculated_fidelity=general.calculated_fidelity, - .unitary_matrix=general.unitary_matrix, + .a = a, + .b = (b - c) / 2., + .c = -((b - c) / 2.), + .globalPhase = globalPhase + k2lphase, + .k1l = general.k1l * rxMatrix(k2lphi), + .k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda), + .k1r = general.k1r * IPZ * rxMatrix(k2lphi) * IPZ, + .k2r = IPZ * rxMatrix(-k2lphi) * IPZ * general.k2r, + .specialization = actualSpecialization, + .defaultEulerBasis = eulerBasis, + .requestedFidelity = general.requestedFidelity, + .calculatedFidelity = general.calculatedFidelity, + .unitaryMatrix = general.unitaryMatrix, }; } // U has no special symmetry. // // This gate binds all 6 possible parameters, so there is no need to // make the single-qubit pre-/post-gates canonical. - if (specialization == Specialization::General) { + if (actualSpecialization == Specialization::General) { return general; } throw std::logic_error{"Unknown specialization"}; }; - TwoQubitWeylDecomposition specialized = get_specialized_decomposition(); + TwoQubitWeylDecomposition specialized = getSpecializedDecomposition(); - auto get_tr = [&]() { - if (flipped_from_original) { + auto getTr = [&]() { + if (flippedFromOriginal) { auto [da, db, dc] = std::array{ qc::PI_2 - a - specialized.a, b - specialized.b, @@ -1211,26 +1217,25 @@ struct GateDecompositionPattern final return static_cast(4.) * qfp(std::cos(da) * std::cos(db) * std::cos(dc), std::sin(da) * std::sin(db) * std::sin(dc)); - } else { - auto [da, db, dc] = std::array{a - specialized.a, b - specialized.b, - c - specialized.c}; - return static_cast(4.) * - qfp(std::cos(da) * std::cos(db) * std::cos(dc), - std::sin(da) * std::sin(db) * std::sin(dc)); } + auto [da, db, dc] = + std::array{a - specialized.a, b - specialized.b, c - specialized.c}; + return static_cast(4.) * + qfp(std::cos(da) * std::cos(db) * std::cos(dc), + std::sin(da) * std::sin(db) * std::sin(dc)); }; - auto tr = get_tr(); - specialized.calculated_fidelity = trace_to_fid(tr); - if (specialized.requested_fidelity) { - if (specialized.calculated_fidelity + 1.0e-13 < - *specialized.requested_fidelity) { + auto tr = getTr(); + specialized.calculatedFidelity = traceToFid(tr); + if (specialized.requestedFidelity) { + if (specialized.calculatedFidelity + 1.0e-13 < + *specialized.requestedFidelity) { throw std::runtime_error{ "Specialization: {:?} calculated fidelity: {} is worse than " "requested fidelity: {}", }; } } - specialized.global_phase += std::arg(tr); + specialized.globalPhase += std::arg(tr); return specialized; } }; @@ -1238,11 +1243,11 @@ struct GateDecompositionPattern final static constexpr auto DEFAULT_FIDELITY = 1.0 - 1.0e-9; struct TwoQubitBasisDecomposer { - QubitGateSequence::Gate basis_gate; - fp basis_fidelity; - EulerBasis euler_basis; - TwoQubitWeylDecomposition basis_decomposer; - bool super_controlled; + QubitGateSequence::Gate basisGate; + fp basisFidelity; + EulerBasis eulerBasis; + TwoQubitWeylDecomposition basisDecomposer; + bool superControlled; matrix2x2 u0l; matrix2x2 u0r; matrix2x2 u1l; @@ -1265,12 +1270,12 @@ struct GateDecompositionPattern final public: static TwoQubitBasisDecomposer - new_inner(OneQubitGateSequence::Gate basis_gate = {.type = qc::X, - .parameter = {}, - .qubit_id = {0, 1}}, - fp basis_fidelity = 1.0, EulerBasis euler_basis = EulerBasis::ZYZ) { - auto relative_eq = [](auto&& lhs, auto&& rhs, auto&& epsilon, - auto&& max_relative) { + newInner(const OneQubitGateSequence::Gate& basisGate = {.type = qc::X, + .parameter = {}, + .qubitId = {0, 1}}, + fp basisFidelity = 1.0, EulerBasis eulerBasis = EulerBasis::ZYZ) { + auto relativeEq = [](auto&& lhs, auto&& rhs, auto&& epsilon, + auto&& maxRelative) { // Handle same infinities if (lhs == rhs) { return true; @@ -1281,55 +1286,55 @@ struct GateDecompositionPattern final return false; } - auto abs_diff = std::abs(lhs - rhs); + auto absDiff = std::abs(lhs - rhs); // For when the numbers are really close together - if (abs_diff <= epsilon) { + if (absDiff <= epsilon) { return true; } - auto abs_lhs = std::abs(lhs); - auto abs_rhs = std::abs(rhs); - if (abs_rhs > abs_lhs) { - return abs_diff <= abs_rhs * max_relative; + auto absLhs = std::abs(lhs); + auto absRhs = std::abs(rhs); + if (absRhs > absLhs) { + return absDiff <= absRhs * maxRelative; } - return abs_diff <= abs_lhs * max_relative; + return absDiff <= absLhs * maxRelative; }; - constexpr auto FRAC_1_SQRT_2 = + constexpr auto frac1Sqrt2 = static_cast(0.707106781186547524400844362104849039); - const auto K12R_ARR = matrix2x2{ - {qfp(0., FRAC_1_SQRT_2), qfp(FRAC_1_SQRT_2, 0.)}, - {qfp(-FRAC_1_SQRT_2, 0.), qfp(0., -FRAC_1_SQRT_2)}, + const auto k12RArr = matrix2x2{ + {qfp(0., frac1Sqrt2), qfp(frac1Sqrt2, 0.)}, + {qfp(-frac1Sqrt2, 0.), qfp(0., -frac1Sqrt2)}, }; - const auto K12L_ARR = matrix2x2{ + const auto k12LArr = matrix2x2{ {qfp(0.5, 0.5), qfp(0.5, 0.5)}, {qfp(-0.5, 0.5), qfp(0.5, -0.5)}, }; - auto basis_decomposer = TwoQubitWeylDecomposition::new_inner( - getTwoQubitMatrix(basis_gate), DEFAULT_FIDELITY, std::nullopt); - auto super_controlled = - relative_eq(basis_decomposer.a, qc::PI_4, - std::numeric_limits::epsilon(), 1e-09) && - relative_eq(basis_decomposer.c, 0.0, - std::numeric_limits::epsilon(), 1e-09); + auto basisDecomposer = TwoQubitWeylDecomposition::newInner( + getTwoQubitMatrix(basisGate), DEFAULT_FIDELITY, std::nullopt); + auto superControlled = + relativeEq(basisDecomposer.a, qc::PI_4, + std::numeric_limits::epsilon(), 1e-09) && + relativeEq(basisDecomposer.c, 0.0, std::numeric_limits::epsilon(), + 1e-09); // Create some useful matrices U1, U2, U3 are equivalent to the basis, // expand as Ui = Ki1.Ubasis.Ki2 - auto b = basis_decomposer.b; + auto b = basisDecomposer.b; auto temp = qfp(0.5, -0.5); auto k11l = matrix2x2{ {temp * (M_IM * std::exp(qfp(0., -b))), temp * std::exp(qfp(0., -b))}, {temp * (M_IM * std::exp(qfp(0., b))), temp * -(std::exp(qfp(0., b)))}}; - auto k11r = matrix2x2{{FRAC_1_SQRT_2 * std::exp((IM * qfp(0., -b))), - FRAC_1_SQRT_2 * -std::exp(qfp(0., -b))}, - {FRAC_1_SQRT_2 * std::exp(qfp(0., b)), - FRAC_1_SQRT_2 * (M_IM * std::exp(qfp(0., b)))}}; - auto k32l_k21l = matrix2x2{{FRAC_1_SQRT_2 * std::cos(qfp(1., (2. * b))), - FRAC_1_SQRT_2 * (IM * std::sin((2. * b)))}, - {FRAC_1_SQRT_2 * (IM * std::sin(2. * b)), - FRAC_1_SQRT_2 * qfp(1., -std::cos(2. * b))}}; + auto k11r = matrix2x2{{frac1Sqrt2 * std::exp((IM * qfp(0., -b))), + frac1Sqrt2 * -std::exp(qfp(0., -b))}, + {frac1Sqrt2 * std::exp(qfp(0., b)), + frac1Sqrt2 * (M_IM * std::exp(qfp(0., b)))}}; + auto k32lK21l = matrix2x2{{frac1Sqrt2 * std::cos(qfp(1., (2. * b))), + frac1Sqrt2 * (IM * std::sin((2. * b)))}, + {frac1Sqrt2 * (IM * std::sin(2. * b)), + frac1Sqrt2 * qfp(1., -std::cos(2. * b))}}; temp = qfp(0.5, 0.5); auto k21r = matrix2x2{ {temp * (M_IM * std::exp(qfp(0., -2. * b))), @@ -1337,16 +1342,16 @@ struct GateDecompositionPattern final {temp * (IM * std::exp(qfp(0., 2. * b))), temp * std::exp(qfp(0., 2. * b))}, }; - const auto K22L_ARR = matrix2x2{ - {qfp(FRAC_1_SQRT_2, 0.), qfp(-FRAC_1_SQRT_2, 0.)}, - {qfp(FRAC_1_SQRT_2, 0.), qfp(FRAC_1_SQRT_2, 0.)}, + const auto k22LArr = matrix2x2{ + {qfp(frac1Sqrt2, 0.), qfp(-frac1Sqrt2, 0.)}, + {qfp(frac1Sqrt2, 0.), qfp(frac1Sqrt2, 0.)}, }; - const auto K22R_ARR = matrix2x2{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; + const auto k22RArr = matrix2x2{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; auto k31l = matrix2x2{ - {FRAC_1_SQRT_2 * std::exp(qfp(0., -b)), - FRAC_1_SQRT_2 * std::exp(qfp(0., -b))}, - {FRAC_1_SQRT_2 * -std::exp(qfp(0., b)), - FRAC_1_SQRT_2 * std::exp(qfp(0., b))}, + {frac1Sqrt2 * std::exp(qfp(0., -b)), + frac1Sqrt2 * std::exp(qfp(0., -b))}, + {frac1Sqrt2 * -std::exp(qfp(0., b)), + frac1Sqrt2 * std::exp(qfp(0., b))}, }; auto k31r = matrix2x2{ {IM * std::exp(qfp(0., b)), C_ZERO}, @@ -1358,81 +1363,81 @@ struct GateDecompositionPattern final {temp * (M_IM * std::exp(qfp(0., b))), temp * (M_IM * std::exp(qfp(0., -b)))}, }; - auto k1ld = basis_decomposer.K1l.transpose().conjugate(); - auto k1rd = basis_decomposer.K1r.transpose().conjugate(); - auto k2ld = basis_decomposer.K2l.transpose().conjugate(); - auto k2rd = basis_decomposer.K2r.transpose().conjugate(); + auto k1ld = basisDecomposer.k1l.transpose().conjugate(); + auto k1rd = basisDecomposer.k1r.transpose().conjugate(); + auto k2ld = basisDecomposer.k2l.transpose().conjugate(); + auto k2rd = basisDecomposer.k2r.transpose().conjugate(); // Pre-build the fixed parts of the matrices used in 3-part // decomposition auto u0l = k31l * k1ld; auto u0r = k31r * k1rd; - auto u1l = k2ld * k32l_k21l * k1ld; + auto u1l = k2ld * k32lK21l * k1ld; auto u1ra = k2rd * k32r; auto u1rb = k21r * k1rd; - auto u2la = k2ld * K22L_ARR; + auto u2la = k2ld * k22LArr; auto u2lb = k11l * k1ld; - auto u2ra = k2rd * K22R_ARR; + auto u2ra = k2rd * k22RArr; auto u2rb = k11r * k1rd; - auto u3l = k2ld * K12L_ARR; - auto u3r = k2rd * K12R_ARR; + auto u3l = k2ld * k12LArr; + auto u3r = k2rd * k12RArr; // Pre-build the fixed parts of the matrices used in the 2-part // decomposition - auto q0l = K12L_ARR.transpose().conjugate() * k1ld; - auto q0r = K12R_ARR.transpose().conjugate() * IPZ * k1rd; + auto q0l = k12LArr.transpose().conjugate() * k1ld; + auto q0r = k12RArr.transpose().conjugate() * IPZ * k1rd; auto q1la = k2ld * k11l.transpose().conjugate(); auto q1lb = k11l * k1ld; auto q1ra = k2rd * IPZ * k11r.transpose().conjugate(); auto q1rb = k11r * k1rd; - auto q2l = k2ld * K12L_ARR; - auto q2r = k2rd * K12R_ARR; + auto q2l = k2ld * k12LArr; + auto q2r = k2rd * k12RArr; return TwoQubitBasisDecomposer{ - .basis_gate=basis_gate, - .basis_fidelity=basis_fidelity, - .euler_basis=euler_basis, - .basis_decomposer=basis_decomposer, - .super_controlled=super_controlled, - .u0l=u0l, - .u0r=u0r, - .u1l=u1l, - .u1ra=u1ra, - .u1rb=u1rb, - .u2la=u2la, - .u2lb=u2lb, - .u2ra=u2ra, - .u2rb=u2rb, - .u3l=u3l, - .u3r=u3r, - .q0l=q0l, - .q0r=q0r, - .q1la=q1la, - .q1lb=q1lb, - .q1ra=q1ra, - .q1rb=q1rb, - .q2l=q2l, - .q2r=q2r, + .basisGate = basisGate, + .basisFidelity = basisFidelity, + .eulerBasis = eulerBasis, + .basisDecomposer = basisDecomposer, + .superControlled = superControlled, + .u0l = u0l, + .u0r = u0r, + .u1l = u1l, + .u1ra = u1ra, + .u1rb = u1rb, + .u2la = u2la, + .u2lb = u2lb, + .u2ra = u2ra, + .u2rb = u2rb, + .u3l = u3l, + .u3r = u3r, + .q0l = q0l, + .q0r = q0r, + .q1la = q1la, + .q1lb = q1lb, + .q1ra = q1ra, + .q1rb = q1rb, + .q2l = q2l, + .q2r = q2r, }; } std::optional twoQubitDecompose(const matrix4x4& unitaryMatrix, - std::optional _basis_fidelity, bool approximate, - std::optional _num_basis_uses) { - auto get_basis_fidelity = [&]() { + std::optional basisFidelity, bool approximate, + std::optional numBasisUses) { + auto getBasisFidelity = [&]() { if (approximate) { - return _basis_fidelity.value_or(this->basis_fidelity); + return basisFidelity.value_or(this->basisFidelity); } return static_cast(1.0); }; - fp basis_fidelity = get_basis_fidelity(); - auto target_decomposed = TwoQubitWeylDecomposition::new_inner( + fp actualBasisFidelity = getBasisFidelity(); + auto targetDecomposed = TwoQubitWeylDecomposition::newInner( unitaryMatrix, DEFAULT_FIDELITY, std::nullopt); - auto traces = this->traces(target_decomposed); - auto get_default_nbasis = [&]() { + auto traces = this->traces(targetDecomposed); + auto getDefaultNbasis = [&]() { auto minValue = std::numeric_limits::min(); auto minIndex = -1; - for (std::size_t i = 0; i < traces.size(); ++i) { - auto value = trace_to_fid(traces[i]) * std::pow(basis_fidelity, i); + for (int i = 0; i < static_cast(traces.size()); ++i) { + auto value = traceToFid(traces[i]) * std::pow(actualBasisFidelity, i); if (value > minValue) { minIndex = i; minValue = value; @@ -1440,138 +1445,136 @@ struct GateDecompositionPattern final } return minIndex; }; - auto best_nbasis = _num_basis_uses.value_or(get_default_nbasis()); - auto choose_decomposition = [&]() { - if (best_nbasis == 0) { - return decomp0_inner(target_decomposed); + auto bestNbasis = numBasisUses.value_or(getDefaultNbasis()); + auto chooseDecomposition = [&]() { + if (bestNbasis == 0) { + return decomp0Inner(targetDecomposed); } - if (best_nbasis == 1) { - return decomp1_inner(target_decomposed); + if (bestNbasis == 1) { + return decomp1Inner(targetDecomposed); } - if (best_nbasis == 2) { - return decomp2_supercontrolled_inner(target_decomposed); + if (bestNbasis == 2) { + return decomp2SupercontrolledInner(targetDecomposed); } - if (best_nbasis == 3) { - return decomp3_supercontrolled_inner(target_decomposed); + if (bestNbasis == 3) { + return decomp3SupercontrolledInner(targetDecomposed); } throw std::logic_error{"Invalid basis to use"}; }; - auto decomposition = choose_decomposition(); - std::cerr << "NBasis: " << (int)best_nbasis - << "; basis_fid: " << basis_fidelity + auto decomposition = chooseDecomposition(); + std::cerr << "NBasis: " << static_cast(bestNbasis) + << "; basis_fid: " << actualBasisFidelity << "; Traces: " << traces[0] << ", " << traces[1] << ", " << traces[2] << ", " << traces[3]; std::cerr << "\nDecomposition:\n"; for (auto x : decomposition) { helpers::print(x, "", true); } - std::vector - target_1q_basis_list; // TODO: simplify because list only has one - // element? - target_1q_basis_list.push_back(euler_basis); + std::vector target1qBasisList; // TODO: simplify because list + // only has one element? + target1qBasisList.push_back(eulerBasis); llvm::SmallVector, 8> - euler_decompositions; + eulerDecompositions; for (auto&& decomp : decomposition) { - auto euler_decomp = unitary_to_gate_sequence_inner( - decomp, target_1q_basis_list, 0, {}, true, std::nullopt); - euler_decompositions.push_back(euler_decomp); + auto eulerDecomp = unitaryToGateSequenceInner( + decomp, target1qBasisList, 0, {}, true, std::nullopt); + eulerDecompositions.push_back(eulerDecomp); } - TwoQubitGateSequence gates{.globalPhase = target_decomposed.global_phase}; + TwoQubitGateSequence gates{.globalPhase = targetDecomposed.globalPhase}; // Worst case length is 5x 1q gates for each 1q decomposition + 1x 2q // gate We might overallocate a bit if the euler basis is different but // the worst case is just 16 extra elements with just a String and 2 // smallvecs each. This is only transient though as the circuit // sequences aren't long lived and are just used to create a // QuantumCircuit or DAGCircuit when we return to Python space. - constexpr auto TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY = 21; - gates.gates.reserve(TWO_QUBIT_SEQUENCE_DEFAULT_CAPACITY); - gates.globalPhase -= best_nbasis * basis_decomposer.global_phase; - if (best_nbasis == 2) { + constexpr auto twoQubitSequenceDefaultCapacity = 21; + gates.gates.reserve(twoQubitSequenceDefaultCapacity); + gates.globalPhase -= bestNbasis * basisDecomposer.globalPhase; + if (bestNbasis == 2) { gates.globalPhase += qc::PI; } - auto add_euler_decomposition = [&](std::size_t index, - std::size_t qubit_id) { - if (auto&& euler_decomp = euler_decompositions[index]) { - for (auto&& gate : euler_decomp->gates) { + auto addEulerDecomposition = [&](std::size_t index, std::size_t qubitId) { + if (auto&& eulerDecomp = eulerDecompositions[index]) { + for (auto&& gate : eulerDecomp->gates) { gates.gates.push_back({.type = gate.type, .parameter = gate.parameter, - .qubit_id = {qubit_id}}); - gates.globalPhase += euler_decomp->globalPhase; + .qubitId = {qubitId}}); + gates.globalPhase += eulerDecomp->globalPhase; } } }; - for (std::size_t i = 0; i < best_nbasis; ++i) { - add_euler_decomposition(2 * i, 0); - add_euler_decomposition(2 * i + 1, 1); + for (std::size_t i = 0; i < bestNbasis; ++i) { + addEulerDecomposition(2 * i, 0); + addEulerDecomposition((2 * i) + 1, 1); - gates.gates.push_back(basis_gate); + gates.gates.push_back(basisGate); } - add_euler_decomposition(2 * best_nbasis, 0); - add_euler_decomposition(2 * best_nbasis + 1, 1); + addEulerDecomposition(2UL * bestNbasis, 0); + addEulerDecomposition((2UL * bestNbasis) + 1, 1); return gates; } private: - [[nodiscard]] std::vector - decomp0_inner(const TwoQubitWeylDecomposition& target) const { + [[nodiscard]] static std::vector + decomp0Inner(const TwoQubitWeylDecomposition& target) { return { - target.K1r * target.K2r, - target.K1l * target.K2l, + target.k1r * target.k2r, + target.k1l * target.k2l, }; } [[nodiscard]] std::vector - decomp1_inner(const TwoQubitWeylDecomposition& target) const { + decomp1Inner(const TwoQubitWeylDecomposition& target) const { // FIXME: fix for z!=0 and c!=0 using closest reflection (not always in // the Weyl chamber) return { - basis_decomposer.K2r.transpose().conjugate() * target.K2r, - basis_decomposer.K2l.transpose().conjugate() * target.K2l, - target.K1r * basis_decomposer.K1r.transpose().conjugate(), - target.K1l * basis_decomposer.K1l.transpose().conjugate(), + basisDecomposer.k2r.transpose().conjugate() * target.k2r, + basisDecomposer.k2l.transpose().conjugate() * target.k2l, + target.k1r * basisDecomposer.k1r.transpose().conjugate(), + target.k1l * basisDecomposer.k1l.transpose().conjugate(), }; } - [[nodiscard]] std::vector decomp2_supercontrolled_inner( - const TwoQubitWeylDecomposition& target) const { + [[nodiscard]] std::vector + decomp2SupercontrolledInner(const TwoQubitWeylDecomposition& target) const { return { - q2r * target.K2r, - q2l * target.K2l, - q1ra * rz_matrix(2. * target.b) * q1rb, - q1la * rz_matrix(-2. * target.a) * q1lb, - target.K1r * q0r, - target.K1l * q0l, + q2r * target.k2r, + q2l * target.k2l, + q1ra * rzMatrix(2. * target.b) * q1rb, + q1la * rzMatrix(-2. * target.a) * q1lb, + target.k1r * q0r, + target.k1l * q0l, }; } - [[nodiscard]] std::vector decomp3_supercontrolled_inner( - const TwoQubitWeylDecomposition& target) const { + [[nodiscard]] std::vector + decomp3SupercontrolledInner(const TwoQubitWeylDecomposition& target) const { return { - u3r * target.K2r, - u3l * target.K2l, - u2ra * rz_matrix(2. * target.b) * u2rb, - u2la * rz_matrix(-2. * target.a) * u2lb, - u1ra * rz_matrix(-2. * target.c) * u1rb, + u3r * target.k2r, + u3l * target.k2l, + u2ra * rzMatrix(2. * target.b) * u2rb, + u2la * rzMatrix(-2. * target.a) * u2lb, + u1ra * rzMatrix(-2. * target.c) * u1rb, u1l, - target.K1r * u0r, - target.K1l * u0l, + target.k1r * u0r, + target.k1l * u0l, }; } - matrix4x4 compute_unitary(const TwoQubitGateSequence& sequence, - fp global_phase) { - auto phase = std::exp(std::complex{0, global_phase}); + static matrix4x4 computeUnitary(const TwoQubitGateSequence& sequence, + fp globalPhase) { + auto phase = std::exp(std::complex{0, globalPhase}); matrix4x4 matrix{}; matrix.diagonal().setConstant(phase); for (auto&& gate : sequence.gates) { - matrix4x4 gate_matrix = getTwoQubitMatrix(gate); + matrix4x4 gateMatrix = getTwoQubitMatrix(gate); - matrix = gate_matrix * matrix; + matrix = gateMatrix * matrix; } return matrix; } @@ -1582,26 +1585,25 @@ struct GateDecompositionPattern final static_cast(4.) * qfp(std::cos(target.a) * std::cos(target.b) * std::cos(target.c), std::sin(target.a) * std::sin(target.b) * std::sin(target.c)), - static_cast(4.) * - qfp(std::cos(qc::PI_4 - target.a) * - std::cos(basis_decomposer.b - target.b) * - std::cos(target.c), - std::sin(qc::PI_4 - target.a) * - std::sin(basis_decomposer.b - target.b) * - std::sin(target.c)), + static_cast(4.) * qfp(std::cos(qc::PI_4 - target.a) * + std::cos(basisDecomposer.b - target.b) * + std::cos(target.c), + std::sin(qc::PI_4 - target.a) * + std::sin(basisDecomposer.b - target.b) * + std::sin(target.c)), qfp(4. * std::cos(target.c), 0.), qfp(4., 0.), }; } - OneQubitGateSequence generate_circuit(EulerBasis target_basis, - const matrix2x2& unitaryMatrix, - bool simplify, - std::optional atol) { + static OneQubitGateSequence generateCircuit(EulerBasis targetBasis, + const matrix2x2& unitaryMatrix, + bool simplify, + std::optional atol) { auto [theta, phi, lambda, phase] = - angles_from_unitary(unitaryMatrix, target_basis); + anglesFromUnitary(unitaryMatrix, targetBasis); - switch (target_basis) { + switch (targetBasis) { case EulerBasis::ZYZ: return calculateRotationGates(theta, phi, lambda, phase, qc::RZ, qc::RY, simplify, atol); @@ -1620,23 +1622,21 @@ struct GateDecompositionPattern final } } - OneQubitGateSequence unitary_to_gate_sequence_inner( - matrix2x2 unitary_mat, const std::vector& target_basis_list, - std::size_t qubit, + static OneQubitGateSequence unitaryToGateSequenceInner( + matrix2x2 unitaryMat, const std::vector& targetBasisList, + std::size_t /*qubit*/, const std::vector>& - error_map, // TODO: remove error_map+qubit for platform + /*error_map*/, // TODO: remove error_map+qubit for platform // independence bool simplify, std::optional atol) { - auto calculateError = [](const OneQubitGateSequence& sequence) { - return sequence.gates.size(); + auto calculateError = [](const OneQubitGateSequence& sequence) -> fp { + return static_cast(sequence.gates.size()); }; auto minError = std::numeric_limits::max(); OneQubitGateSequence bestCircuit; - for (std::size_t i = 0; i < target_basis_list.size(); ++i) { - auto& target_basis = target_basis_list[i]; - auto circuit = - generate_circuit(target_basis, unitary_mat, simplify, atol); + for (auto targetBasis : targetBasisList) { + auto circuit = generateCircuit(targetBasis, unitaryMat, simplify, atol); auto error = calculateError(circuit); if (error < minError) { bestCircuit = circuit; @@ -1709,9 +1709,9 @@ struct GateDecompositionPattern final }; // namespace mqt::ir::opt }; -const matrix2x2 GateDecompositionPattern::identityGate = matrix2x2::Identity(); -const matrix2x2 GateDecompositionPattern::hGate{{1.0 / sqrt2, 1.0 / sqrt2}, - {1.0 / sqrt2, -1.0 / sqrt2}}; +const matrix2x2 GateDecompositionPattern::IDENTITY_GATE = matrix2x2::Identity(); +const matrix2x2 GateDecompositionPattern::H_GATE{{1.0 / SQRT2, 1.0 / SQRT2}, + {1.0 / SQRT2, -1.0 / SQRT2}}; const matrix2x2 GateDecompositionPattern::IPZ{{IM, C_ZERO}, {C_ZERO, M_IM}}; const matrix2x2 GateDecompositionPattern::IPY{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index 595ce25663..46d314bcb5 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -10,15 +10,15 @@ #pragma once -#include "dd/GateMatrixDefinitions.hpp" -#include "dd/Package.hpp" -#include "ir/Definitions.hpp" #include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" +#include "ir/operations/OpType.hpp" #include #include #include #include // TODO: unstable +#include // TODO: remove +#include // TODO: remove #include #include @@ -42,19 +42,20 @@ constexpr qfp M_IM{0., -1.}; namespace mqt::ir::opt::helpers { -void print(std::size_t x) { std::cerr << x; } -void print(fp x) { std::cerr << x; } +inline void print(std::size_t x) { std::cerr << x; } +inline void print(fp x) { std::cerr << x; } -void print(qfp x) { +inline void print(qfp x) { std::cerr << std::setprecision(17) << x.real() << 'i' << x.imag(); } // TODO: remove template -void print(Eigen::Matrix matrix, std::string s = "", +void print(Eigen::Matrix matrix, const std::string& s = "", bool force = false) { - if (!force) + if (!force) { return; +} if (!s.empty()) { llvm::errs() << "=== " << s << " ===\n"; } @@ -69,9 +70,10 @@ void print(Eigen::Matrix matrix, std::string s = "", } template -void print(T&& matrix, std::string s = "", bool force = false) { - if (!force) +void print(T matrix, const std::string& s = "", bool force = false) { + if (!force) { return; +} if (!s.empty()) { llvm::errs() << "=== " << s << " ===\n"; } @@ -217,14 +219,14 @@ inline Eigen::Matrix4 kroneckerProduct(const Eigen::Matrix2& lhs, return result; } -auto self_adjoint_evd(rmatrix4x4 A) { +inline auto selfAdjointEvd(rmatrix4x4 a) { Eigen::SelfAdjointEigenSolver s; - std::cerr << "=EigIN==\n" << A << "\n========\n" << std::endl; - s.compute(A); // TODO: computeDirect is faster + std::cerr << "=EigIN==\n" << a << "\n========\n" << '\n'; + s.compute(a); // TODO: computeDirect is faster auto vecs = s.eigenvectors().eval(); auto vals = s.eigenvalues(); - std::cerr << "=Eigen==\n" << vecs << "\n========\n" << std::endl; - std::cerr << "=Eigen==\n" << vals << "\n========\n" << std::endl; + std::cerr << "=Eigen==\n" << vecs << "\n========\n" << '\n'; + std::cerr << "=Eigen==\n" << vals << "\n========\n" << '\n'; return std::make_pair(vecs, vals); } From 579b6e85325483ba0ed50495e2049a8447402235 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 11 Nov 2025 14:08:10 +0100 Subject: [PATCH 048/237] test normalized magic basis, allow complex eigenvector decomposition --- .../Transforms/GateDecompositionPattern.cpp | 52 +++++++++++-------- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 5 +- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index e50cb5e5bf..d1a0ae8bcb 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -13,10 +13,10 @@ #include "mlir/Dialect/MQTOpt/Transforms/Passes.h" #include +#include #include #include #include -#include #include #include #include @@ -281,6 +281,8 @@ struct GateDecompositionPattern final rewriter.setInsertionPointAfter(lastSeriesOp); if (sequence.globalPhase != 0.0) { + // TODO: use POp instead and apply negative globalPhase after insertion of + // new gates to "undo" the phase shift createOneParameterGate(rewriter, location, sequence.globalPhase, {}); } @@ -387,6 +389,8 @@ struct GateDecompositionPattern final }; static constexpr auto SQRT2 = std::numbers::sqrt2_v; + static constexpr auto FRAC1_SQRT2 = + static_cast(0.707106781186547524400844362104849039); static const matrix2x2 IDENTITY_GATE; static const matrix2x2 H_GATE; @@ -410,15 +414,14 @@ struct GateDecompositionPattern final } // https://docs.rs/faer/latest/faer/mat/generic/struct.Mat.html#method.self_adjoint_eigen - static std::pair - selfAdjointEigenLower(rmatrix4x4 a) { + template static auto selfAdjointEigenLower(T&& a) { // rdiagonal4x4 S; // auto U = self_adjoint_evd(A, S); // rmatrix4x4 U; // jacobi_eigen_decomposition(A, U, S); - auto [U, S] = helpers::selfAdjointEvd(a); + auto [U, S] = helpers::selfAdjointEvd(std::forward(a)); // TODO: not in original code if (std::abs(U.determinant() + 1.0) < 1e-5) { @@ -483,6 +486,10 @@ struct GateDecompositionPattern final {C_ZERO, C_ZERO, IM, C_M_ONE}, {C_ONE, M_IM, C_ZERO, C_ZERO}, }; + const matrix4x4 b = FRAC1_SQRT2 * matrix4x4{{C_ONE, C_ZERO, C_ZERO, IM}, + {C_ZERO, IM, C_ONE, C_ZERO}, + {C_ZERO, IM, -C_ONE, C_ZERO}, + {C_ONE, C_ZERO, C_ZERO, -IM}}; const matrix4x4 bNonNormalizedDagger{ {qfp(0.5, 0.), C_ZERO, C_ZERO, qfp(0.5, 0.)}, @@ -490,11 +497,14 @@ struct GateDecompositionPattern final {C_ZERO, qfp(0., -0.5), qfp(0., -0.5), C_ZERO}, {C_ZERO, qfp(0.5, 0.), qfp(-0.5, 0.), C_ZERO}, }; + const matrix4x4 bDagger = b.conjugate().transpose(); helpers::print(unitary, "UNITARY in MAGIC BASIS TRANSFORM"); if (direction == MagicBasisTransform::OutOf) { + // return bDagger * unitary * b; // TODO: same result? return bNonNormalizedDagger * unitary * bNonNormalized; } if (direction == MagicBasisTransform::Into) { + // return b * unitary * bDagger; return bNonNormalized * unitary * bNonNormalizedDagger; } throw std::logic_error{"Unknown MagicBasisTransform direction!"}; @@ -1300,11 +1310,9 @@ struct GateDecompositionPattern final } return absDiff <= absLhs * maxRelative; }; - constexpr auto frac1Sqrt2 = - static_cast(0.707106781186547524400844362104849039); const auto k12RArr = matrix2x2{ - {qfp(0., frac1Sqrt2), qfp(frac1Sqrt2, 0.)}, - {qfp(-frac1Sqrt2, 0.), qfp(0., -frac1Sqrt2)}, + {qfp(0., FRAC1_SQRT2), qfp(FRAC1_SQRT2, 0.)}, + {qfp(-FRAC1_SQRT2, 0.), qfp(0., -FRAC1_SQRT2)}, }; const auto k12LArr = matrix2x2{ {qfp(0.5, 0.5), qfp(0.5, 0.5)}, @@ -1327,14 +1335,14 @@ struct GateDecompositionPattern final {temp * (M_IM * std::exp(qfp(0., -b))), temp * std::exp(qfp(0., -b))}, {temp * (M_IM * std::exp(qfp(0., b))), temp * -(std::exp(qfp(0., b)))}}; - auto k11r = matrix2x2{{frac1Sqrt2 * std::exp((IM * qfp(0., -b))), - frac1Sqrt2 * -std::exp(qfp(0., -b))}, - {frac1Sqrt2 * std::exp(qfp(0., b)), - frac1Sqrt2 * (M_IM * std::exp(qfp(0., b)))}}; - auto k32lK21l = matrix2x2{{frac1Sqrt2 * std::cos(qfp(1., (2. * b))), - frac1Sqrt2 * (IM * std::sin((2. * b)))}, - {frac1Sqrt2 * (IM * std::sin(2. * b)), - frac1Sqrt2 * qfp(1., -std::cos(2. * b))}}; + auto k11r = matrix2x2{{FRAC1_SQRT2 * std::exp((IM * qfp(0., -b))), + FRAC1_SQRT2 * -std::exp(qfp(0., -b))}, + {FRAC1_SQRT2 * std::exp(qfp(0., b)), + FRAC1_SQRT2 * (M_IM * std::exp(qfp(0., b)))}}; + auto k32lK21l = matrix2x2{{FRAC1_SQRT2 * std::cos(qfp(1., (2. * b))), + FRAC1_SQRT2 * (IM * std::sin((2. * b)))}, + {FRAC1_SQRT2 * (IM * std::sin(2. * b)), + FRAC1_SQRT2 * qfp(1., -std::cos(2. * b))}}; temp = qfp(0.5, 0.5); auto k21r = matrix2x2{ {temp * (M_IM * std::exp(qfp(0., -2. * b))), @@ -1343,15 +1351,15 @@ struct GateDecompositionPattern final temp * std::exp(qfp(0., 2. * b))}, }; const auto k22LArr = matrix2x2{ - {qfp(frac1Sqrt2, 0.), qfp(-frac1Sqrt2, 0.)}, - {qfp(frac1Sqrt2, 0.), qfp(frac1Sqrt2, 0.)}, + {qfp(FRAC1_SQRT2, 0.), qfp(-FRAC1_SQRT2, 0.)}, + {qfp(FRAC1_SQRT2, 0.), qfp(FRAC1_SQRT2, 0.)}, }; const auto k22RArr = matrix2x2{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; auto k31l = matrix2x2{ - {frac1Sqrt2 * std::exp(qfp(0., -b)), - frac1Sqrt2 * std::exp(qfp(0., -b))}, - {frac1Sqrt2 * -std::exp(qfp(0., b)), - frac1Sqrt2 * std::exp(qfp(0., b))}, + {FRAC1_SQRT2 * std::exp(qfp(0., -b)), + FRAC1_SQRT2 * std::exp(qfp(0., -b))}, + {FRAC1_SQRT2 * -std::exp(qfp(0., b)), + FRAC1_SQRT2 * std::exp(qfp(0., b))}, }; auto k31r = matrix2x2{ {IM * std::exp(qfp(0., b)), C_ZERO}, diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index 46d314bcb5..875f8a3d0d 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -219,8 +219,9 @@ inline Eigen::Matrix4 kroneckerProduct(const Eigen::Matrix2& lhs, return result; } -inline auto selfAdjointEvd(rmatrix4x4 a) { - Eigen::SelfAdjointEigenSolver s; +template +inline auto selfAdjointEvd(Eigen::Matrix a) { + Eigen::SelfAdjointEigenSolver s; std::cerr << "=EigIN==\n" << a << "\n========\n" << '\n'; s.compute(a); // TODO: computeDirect is faster auto vecs = s.eigenvectors().eval(); From 7ca534148d570872927f48a6c560876349f148a1 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 12 Nov 2025 00:19:39 +0100 Subject: [PATCH 049/237] finally figured some stuff out --- .../MQTOpt/Transforms/GateDecompositionPattern.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index d1a0ae8bcb..dba3f0e5ef 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -702,6 +702,11 @@ struct GateDecompositionPattern final fp b; fp c; fp globalPhase; + /** + * - k2r - X - k1r - + * X + * - k2l - X - k1l - + */ matrix2x2 k1l; matrix2x2 k2l; matrix2x2 k1r; @@ -830,8 +835,10 @@ struct GateDecompositionPattern final temp = temp.exp(); helpers::print(temp, "TEMP"); helpers::print(p, "P", true); + // https://threeplusone.com/pubs/on_gates.pdf + // uP = V, m2 = V^T*V, temp = D, p = Q1 auto k1 = magicBasisTransform(uP * p * temp, MagicBasisTransform::Into); - auto k2 = magicBasisTransform(p.transpose(), MagicBasisTransform::Into); + auto k2 = magicBasisTransform(p.transpose().conjugate(), MagicBasisTransform::Into); auto [K1l, K1r, phase_l] = decomposeTwoQubitProductGate(k1); auto [K2l, K2r, phase_r] = decomposeTwoQubitProductGate(k2); From e1548d2157e2e74d4f922d0f33b9d96c464facf5 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 12 Nov 2025 14:52:57 +0100 Subject: [PATCH 050/237] try out lauging-umbrella weyl-chamber --- .../Transforms/GateDecompositionPattern.cpp | 109 ++++++++++++------ 1 file changed, 74 insertions(+), 35 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index dba3f0e5ef..b454960f17 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -12,6 +12,7 @@ #include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" #include "mlir/Dialect/MQTOpt/Transforms/Passes.h" +#include #include #include #include @@ -792,53 +793,91 @@ struct GateDecompositionPattern final throw std::runtime_error{ "TwoQubitWeylDecomposition: failed to diagonalize M2."}; } - rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; - helpers::print(dReal, "D_REAL", true); - dReal(3) = -dReal(0) - dReal(1) - dReal(2); - std::array cs{}; - for (int i = 0; i < static_cast(cs.size()); ++i) { - assert(i < dReal.size()); - cs[i] = remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); + // check that p is in SO(4) + assert((p.transpose() * p).isIdentity()); + assert(std::abs(p.determinant() - 1.0) < 1e-13); + // make sure de4terminant of sqrt(eigenvalues) is 1.0 + assert(std::abs(matrix4x4{d.asDiagonal()}.determinant() - 1.0) < 1e-13); + + // see https://github.com/mpham26uchicago/laughing-umbrella/blob/main/background/Full%20Two%20Qubit%20KAK%20Implementation.ipynb, Step 7 + Eigen::Matrix coefficientMatrix {{1, 1, 1}, {-1, 1, -1}, {1, -1, -1}}; + auto theta_vec = d.cwiseArg().head(3); + Eigen::Vector tmp = coefficientMatrix.inverse() * theta_vec; + Eigen::Vector cs{ 2.0 * tmp(0), -2.0 * tmp(1), 2.0 * tmp(2) }; + // bring cs into weyl chamber + cs /= qc::PI; + cs -= cs.unaryExpr([](auto&& x) { return std::floor(x); }); + std::ranges::sort(cs, std::greater<>()); + if (cs[0] + cs[1] >= 1.0) { + cs = decltype(cs){ 1 - cs[0], cs[1], 0.0 }; + std::ranges::sort(cs, std::greater<>()); } - helpers::print(cs, "CS", true); - decltype(cs) cstemp; - llvm::transform(cs, cstemp.begin(), [](auto&& x) { - auto tmp = remEuclid(x, qc::PI_2); - return std::min(tmp, qc::PI_2 - tmp); - }); - std::array order{ - 2, 1, 0}; // TODO: needs to be adjusted depending on eigenvector - // order in eigen decomposition algorithm? - llvm::stable_sort(order, - [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); - std::tie(order[0], order[1], order[2]) = - std::tuple{order[1], order[2], order[0]}; - std::tie(cs[0], cs[1], cs[2]) = - std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; - std::tie(dReal(0), dReal(1), dReal(2)) = - std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2])}; - helpers::print(dReal, "D_REAL (sorted)", true); - - // swap columns of p according to order - auto pOrig = p; - for (int i = 0; i < static_cast(order.size()); ++i) { - p.col(i) = pOrig.col(order[i]); + if (cs[0] > 0.5 && std::abs(cs[2]) < 1e-13) { + cs = decltype(cs) { 1 - cs[0], cs[1], 0.0 }; } + cs *= qc::PI; + + // rdiagonal4x4 dReal = d.cwiseArg() / 2.0; + // helpers::print(dReal, "D_REAL", true); + // dReal(3) = -dReal(0) - dReal(1) - dReal(2); + // std::array cs{}; + // for (int i = 0; i < static_cast(cs.size()); ++i) { + // assert(i < dReal.size()); + // cs[i] = remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); + // } + // helpers::print(cs, "CS", true); + + // decltype(cs) cstemp; + // llvm::transform(cs, cstemp.begin(), [](auto&& x) { + // auto tmp = remEuclid(x, qc::PI_2); + // return std::min(tmp, qc::PI_2 - tmp); + // }); + // std::array order{ + // 2, 1, 0}; // TODO: needs to be adjusted depending on eigenvector + // // order in eigen decomposition algorithm? + // llvm::stable_sort(order, + // [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); + // std::tie(order[0], order[1], order[2]) = + // std::tuple{order[1], order[2], order[0]}; + // std::tie(cs[0], cs[1], cs[2]) = + // std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; + // std::tie(dReal(0), dReal(1), dReal(2)) = + // std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2])}; + // helpers::print(dReal, "D_REAL (sorted)", true); + + // // swap columns of p according to order + // auto pOrig = p; + // for (int i = 0; i < static_cast(order.size()); ++i) { + // p.col(i) = pOrig.col(order[i]); + // } + + // std::tie(p, d) = selfAdjointEigenLower(m2); + + // dReal = -1.0 * d.cwiseArg() / 2.0; + // helpers::print(dReal, "D_REAL", true); + // dReal(3) = -dReal(0) - dReal(1) - dReal(2); + // for (int i = 0; i < static_cast(cs.size()); ++i) { + // assert(i < dReal.size()); + // cs[i] = remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); + // } + // matrix4x4 temp = d.asDiagonal(); if (p.determinant().real() < 0.0) { + std::cerr << "SECOND CORRECTION?\n"; auto lastColumnIndex = p.cols() - 1; - p.col(lastColumnIndex) = -p.col(lastColumnIndex); + p.col(lastColumnIndex) *= -1.0; } - matrix4x4 temp = dReal.asDiagonal(); - temp *= IM; - temp = temp.exp(); + matrix4x4 temp = d.asDiagonal(); + // temp *= IM; + // temp = temp.exp(); helpers::print(temp, "TEMP"); helpers::print(p, "P", true); // https://threeplusone.com/pubs/on_gates.pdf // uP = V, m2 = V^T*V, temp = D, p = Q1 auto k1 = magicBasisTransform(uP * p * temp, MagicBasisTransform::Into); - auto k2 = magicBasisTransform(p.transpose().conjugate(), MagicBasisTransform::Into); + auto k2 = magicBasisTransform(p.transpose().conjugate(), + MagicBasisTransform::Into); auto [K1l, K1r, phase_l] = decomposeTwoQubitProductGate(k1); auto [K2l, K2r, phase_r] = decomposeTwoQubitProductGate(k2); From 9dfd4538425d52b541ecfa96367bbcff779f80d7 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 12 Nov 2025 16:59:32 +0100 Subject: [PATCH 051/237] try to implement weylchamber.c1c2c3 algorithm --- .../Transforms/GateDecompositionPattern.cpp | 83 ++++++++++--------- 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index b454960f17..7aa4987737 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -799,28 +799,13 @@ struct GateDecompositionPattern final // make sure de4terminant of sqrt(eigenvalues) is 1.0 assert(std::abs(matrix4x4{d.asDiagonal()}.determinant() - 1.0) < 1e-13); - // see https://github.com/mpham26uchicago/laughing-umbrella/blob/main/background/Full%20Two%20Qubit%20KAK%20Implementation.ipynb, Step 7 - Eigen::Matrix coefficientMatrix {{1, 1, 1}, {-1, 1, -1}, {1, -1, -1}}; - auto theta_vec = d.cwiseArg().head(3); - Eigen::Vector tmp = coefficientMatrix.inverse() * theta_vec; - Eigen::Vector cs{ 2.0 * tmp(0), -2.0 * tmp(1), 2.0 * tmp(2) }; - // bring cs into weyl chamber - cs /= qc::PI; - cs -= cs.unaryExpr([](auto&& x) { return std::floor(x); }); - std::ranges::sort(cs, std::greater<>()); - if (cs[0] + cs[1] >= 1.0) { - cs = decltype(cs){ 1 - cs[0], cs[1], 0.0 }; - std::ranges::sort(cs, std::greater<>()); - } - if (cs[0] > 0.5 && std::abs(cs[2]) < 1e-13) { - cs = decltype(cs) { 1 - cs[0], cs[1], 0.0 }; - } - cs *= qc::PI; - - // rdiagonal4x4 dReal = d.cwiseArg() / 2.0; + // see + // https://github.com/mpham26uchicago/laughing-umbrella/blob/main/background/Full%20Two%20Qubit%20KAK%20Implementation.ipynb, + // Step 7 + // rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; // helpers::print(dReal, "D_REAL", true); // dReal(3) = -dReal(0) - dReal(1) - dReal(2); - // std::array cs{}; + // Eigen::Vector cs{}; // for (int i = 0; i < static_cast(cs.size()); ++i) { // assert(i < dReal.size()); // cs[i] = remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); @@ -851,33 +836,53 @@ struct GateDecompositionPattern final // p.col(i) = pOrig.col(order[i]); // } - // std::tie(p, d) = selfAdjointEigenLower(m2); - - // dReal = -1.0 * d.cwiseArg() / 2.0; - // helpers::print(dReal, "D_REAL", true); - // dReal(3) = -dReal(0) - dReal(1) - dReal(2); - // for (int i = 0; i < static_cast(cs.size()); ++i) { - // assert(i < dReal.size()); - // cs[i] = remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); + // if (p.determinant().real() < 0.0) { + // std::cerr << "SECOND CORRECTION?\n"; + // auto lastColumnIndex = p.cols() - 1; + // p.col(lastColumnIndex) *= -1.0; // } - // matrix4x4 temp = d.asDiagonal(); - if (p.determinant().real() < 0.0) { - std::cerr << "SECOND CORRECTION?\n"; - auto lastColumnIndex = p.cols() - 1; - p.col(lastColumnIndex) *= -1.0; + // https://weylchamber.readthedocs.io/en/latest/_modules/weylchamber/coordinates.html#c1c2c3 + Eigen::Vector cs; + Eigen::Vector dReal = d.cwiseArg() / qc::PI; + for (auto& x : dReal) { + if (x <= -0.5) { + x += 2.0; + } + } + dReal /= 2.0; + llvm::stable_sort(dReal, std::greater<>()); + int n = std::round(dReal.sum()); + for (int i = 0; i < n; ++i) { + dReal[i] -= 1.0; } + decltype(dReal) origDReal = dReal; + for (int i = 0; i < dReal.size(); ++i) { + // roll(-n) + dReal[i] = origDReal[i >= n ? i - n : dReal.size() - (i - n)]; + } + Eigen::Matrix M {{1, 1, 0}, {1, 0, 1}, {0, 1, 1}}; + cs = M * dReal.head(3); + if (cs[2] < 0.0) { + cs[0] = 1.0 - cs[0]; + cs[2] *= -1.0; + } + dReal *= qc::PI; + cs *= qc::PI; - matrix4x4 temp = d.asDiagonal(); - // temp *= IM; - // temp = temp.exp(); + matrix4x4 temp = dReal.asDiagonal(); + temp *= IM; + temp = temp.exp(); helpers::print(temp, "TEMP"); helpers::print(p, "P", true); // https://threeplusone.com/pubs/on_gates.pdf // uP = V, m2 = V^T*V, temp = D, p = Q1 - auto k1 = magicBasisTransform(uP * p * temp, MagicBasisTransform::Into); - auto k2 = magicBasisTransform(p.transpose().conjugate(), - MagicBasisTransform::Into); + matrix4x4 k1 = uP * p * temp; + assert((k1.transpose() * k1).isIdentity()); // k1 must be orthogonal + k1 = magicBasisTransform(k1, MagicBasisTransform::Into); + matrix4x4 k2 = p.transpose().conjugate(); + assert((k2.transpose() * k2).isIdentity()); // k2 must be orthogonal + k2 = magicBasisTransform(k2, MagicBasisTransform::Into); auto [K1l, K1r, phase_l] = decomposeTwoQubitProductGate(k1); auto [K2l, K2r, phase_r] = decomposeTwoQubitProductGate(k2); From e78b4f3de79a854f53eef413835c1d45e7e9ca58 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 12 Nov 2025 22:52:38 +0100 Subject: [PATCH 052/237] on_gates two-qubit product decompose --- .../Transforms/GateDecompositionPattern.cpp | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 7aa4987737..99a11320b3 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -437,7 +438,43 @@ struct GateDecompositionPattern final static std::tuple decomposeTwoQubitProductGate(matrix4x4 specialUnitary) { + // see pennylane math.decomposition.su2su2_to_tensor_products + // or "7.1 Kronecker decomposition" in on_gates.pdf helpers::print(specialUnitary, "SPECIAL_UNITARY"); + + // Step 1: Reshape and permute C like in Python + // C (4x4) is considered as (2,2,2,2) tensor, transpose(0,2,1,3) + matrix4x4 C; + + // Perform the same rearrangement as Python's transpose(0,2,1,3) + // This swaps certain 2x2 blocks in C_in. + // The pattern is determined by the tensor index mapping. + for (int i = 0; i < 2; ++i) + for (int j = 0; j < 2; ++j) + for (int k = 0; k < 2; ++k) + for (int l = 0; l < 2; ++l) { + int row_in = 2 * i + k; + int col_in = 2 * j + l; + int row_out = 2 * i + j; + int col_out = 2 * k + l; + C(row_out, col_out) = specialUnitary(row_in, col_in); + } + + // Step 2: SVD + Eigen::JacobiSVD svd(C, Eigen::ComputeFullU | Eigen::ComputeFullV); + Eigen::Vector4d singularValues = svd.singularValues(); + matrix4x4 U = svd.matrixU(); + matrix4x4 V = svd.matrixV(); + + // Step 3: Extract first singular component + double s = std::sqrt(singularValues(0)); + + matrix2x2 A = s * U.col(0).reshaped(2, 2); + matrix2x2 B = s * V.col(0).reshaped(2, 2).transpose(); // vh[0, :] in numpy == V^T.row(0) + + // return {A, B, s}; + + // first quadrant matrix2x2 r{{specialUnitary(0, 0), specialUnitary(0, 1)}, {specialUnitary(1, 0), specialUnitary(1, 1)}}; From cf9d649ec209ea2262ead06347b03c9909dac2dc Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 12 Nov 2025 22:54:03 +0100 Subject: [PATCH 053/237] minor additions --- .../Transforms/GateDecompositionPattern.cpp | 23 +++++++++++++++---- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 8 +------ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 99a11320b3..405cd9a94a 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -426,11 +426,14 @@ struct GateDecompositionPattern final auto [U, S] = helpers::selfAdjointEvd(std::forward(a)); // TODO: not in original code - if (std::abs(U.determinant() + 1.0) < 1e-5) { + if (std::real(U.determinant()) < 0.0) { std::cerr << "CORRECTION!\n"; // if determinant of eigenvector matrix is -1.0, multiply first // eigenvector by -1.0 U.col(0) *= -1.0; + // U.col(U.cols() - 1) *= -1.0; + // U *= -1.0; + // U += std::remove_cvref_t::Constant(0.0); // ensure no -0.0 exists } return std::make_pair(U, S); @@ -479,13 +482,14 @@ struct GateDecompositionPattern final matrix2x2 r{{specialUnitary(0, 0), specialUnitary(0, 1)}, {specialUnitary(1, 0), specialUnitary(1, 1)}}; auto detR = r.determinant(); + std::cerr << "DET_R: " << detR << '\n'; if (std::abs(detR) < 0.1) { // third quadrant r = matrix2x2{{specialUnitary(2, 0), specialUnitary(2, 1)}, {specialUnitary(3, 0), specialUnitary(3, 1)}}; detR = r.determinant(); + std::cerr << "DET_R CORRECTION: " << detR << '\n'; } - std::cerr << "DET_R: " << detR << '\n'; if (std::abs(detR) < 0.1) { throw std::runtime_error{ "decompose_two_qubit_product_gate: unable to decompose: det_r < 0.1"}; @@ -729,6 +733,9 @@ struct GateDecompositionPattern final // TODO: check qubit order return rzzMatrix(gate.parameter[0]); } + if (gate.type == qc::I) { + return kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); + } throw std::invalid_argument{ "unsupported gate type for two qubit matrix "}; } @@ -821,7 +828,11 @@ struct GateDecompositionPattern final helpers::print(compare, "COMPARE"); found = (compare - m2).cwiseAbs().cwiseLessOrEqual(1.0e-13).all(); if (found) { + // p are the eigenvectors which are decomposed into the + // single-qubit gates surrounding the canonical gate p = pInner; + // d is the sqrt of the eigenvalues that are used to determine the + // weyl coordinates and thus the parameters of the canonical gate d = dInner; break; } @@ -832,8 +843,7 @@ struct GateDecompositionPattern final } // check that p is in SO(4) assert((p.transpose() * p).isIdentity()); - assert(std::abs(p.determinant() - 1.0) < 1e-13); - // make sure de4terminant of sqrt(eigenvalues) is 1.0 + // make sure determinant of sqrt(eigenvalues) is 1.0 assert(std::abs(matrix4x4{d.asDiagonal()}.determinant() - 1.0) < 1e-13); // see @@ -906,18 +916,21 @@ struct GateDecompositionPattern final } dReal *= qc::PI; cs *= qc::PI; + assert(std::abs(p.determinant() - 1.0) < 1e-13); matrix4x4 temp = dReal.asDiagonal(); temp *= IM; temp = temp.exp(); - helpers::print(temp, "TEMP"); + helpers::print(temp, "TEMP", true); helpers::print(p, "P", true); // https://threeplusone.com/pubs/on_gates.pdf // uP = V, m2 = V^T*V, temp = D, p = Q1 matrix4x4 k1 = uP * p * temp; + helpers::print(k1, "K1 (1)", true); assert((k1.transpose() * k1).isIdentity()); // k1 must be orthogonal k1 = magicBasisTransform(k1, MagicBasisTransform::Into); matrix4x4 k2 = p.transpose().conjugate(); + helpers::print(k2, "K2 (1)", true); assert((k2.transpose() * k2).isIdentity()); // k2 must be orthogonal k2 = magicBasisTransform(k2, MagicBasisTransform::Into); diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index 875f8a3d0d..3849f66cc2 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -59,13 +59,7 @@ void print(Eigen::Matrix matrix, const std::string& s = "", if (!s.empty()) { llvm::errs() << "=== " << s << " ===\n"; } - for (int i = 0; i < matrix.cols(); ++i) { - for (int j = 0; j < matrix.rows(); ++j) { - print(matrix(j, i)); - std::cerr << ' '; - } - llvm::errs() << '\n'; - } + std::cerr << matrix; llvm::errs() << '\n'; } From c02bfa6813426757ec50af451b8e61cad4351a47 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 12 Nov 2025 22:55:00 +0100 Subject: [PATCH 054/237] try ordering all four eigenvectors --- .../Transforms/GateDecompositionPattern.cpp | 115 ++++++++---------- 1 file changed, 50 insertions(+), 65 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 405cd9a94a..c1e4d003c6 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -849,78 +849,62 @@ struct GateDecompositionPattern final // see // https://github.com/mpham26uchicago/laughing-umbrella/blob/main/background/Full%20Two%20Qubit%20KAK%20Implementation.ipynb, // Step 7 - // rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; - // helpers::print(dReal, "D_REAL", true); - // dReal(3) = -dReal(0) - dReal(1) - dReal(2); - // Eigen::Vector cs{}; - // for (int i = 0; i < static_cast(cs.size()); ++i) { - // assert(i < dReal.size()); - // cs[i] = remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); - // } - // helpers::print(cs, "CS", true); - - // decltype(cs) cstemp; - // llvm::transform(cs, cstemp.begin(), [](auto&& x) { - // auto tmp = remEuclid(x, qc::PI_2); - // return std::min(tmp, qc::PI_2 - tmp); - // }); - // std::array order{ - // 2, 1, 0}; // TODO: needs to be adjusted depending on eigenvector - // // order in eigen decomposition algorithm? - // llvm::stable_sort(order, - // [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); - // std::tie(order[0], order[1], order[2]) = - // std::tuple{order[1], order[2], order[0]}; - // std::tie(cs[0], cs[1], cs[2]) = - // std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; - // std::tie(dReal(0), dReal(1), dReal(2)) = - // std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2])}; - // helpers::print(dReal, "D_REAL (sorted)", true); - - // // swap columns of p according to order - // auto pOrig = p; - // for (int i = 0; i < static_cast(order.size()); ++i) { - // p.col(i) = pOrig.col(order[i]); - // } - - // if (p.determinant().real() < 0.0) { - // std::cerr << "SECOND CORRECTION?\n"; - // auto lastColumnIndex = p.cols() - 1; - // p.col(lastColumnIndex) *= -1.0; - // } - - // https://weylchamber.readthedocs.io/en/latest/_modules/weylchamber/coordinates.html#c1c2c3 - Eigen::Vector cs; - Eigen::Vector dReal = d.cwiseArg() / qc::PI; - for (auto& x : dReal) { - if (x <= -0.5) { - x += 2.0; - } - } - dReal /= 2.0; - llvm::stable_sort(dReal, std::greater<>()); - int n = std::round(dReal.sum()); - for (int i = 0; i < n; ++i) { - dReal[i] -= 1.0; + rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; + helpers::print(dReal, "D_REAL", true); + dReal(3) = -dReal(0) - dReal(1) - dReal(2); + Eigen::Vector cs{}; + for (int i = 0; i < static_cast(cs.size()); ++i) { + assert(i < dReal.size()); + cs[i] = remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); } - decltype(dReal) origDReal = dReal; - for (int i = 0; i < dReal.size(); ++i) { - // roll(-n) - dReal[i] = origDReal[i >= n ? i - n : dReal.size() - (i - n)]; + helpers::print(cs, "CS (1)", true); + + decltype(cs) cstemp; + llvm::transform(cs, cstemp.begin(), [](auto&& x) { + auto tmp = remEuclid(x, qc::PI_2); + return std::min(tmp, qc::PI_2 - tmp); + }); + std::array order{ + 0, 1, 2, 3}; // TODO: needs to be adjusted depending on eigenvector + // order in eigen decomposition algorithm? + llvm::stable_sort(order, + [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); + // llvm::stable_sort(order, [&](fp a, fp b) { + // auto tmp1 = remEuclid(cs[a], qc::PI_2); + // tmp1 = std::min(tmp1, qc::PI_2 - tmp1); + // auto tmp2 = remEuclid(cs[b], qc::PI_2); + // tmp2 = std::min(tmp2, qc::PI_2 - tmp2); + // return tmp1 < tmp2; + // }); + std::tie(order[0], order[1], order[2], order[3]) = + std::tuple{order[3], order[1], order[2], order[0]}; + std::tie(cs[0], cs[1], cs[2], cs[3]) = + std::tuple{cs[order[0]], cs[order[1]], cs[order[2]], cs[order[3]]}; + std::tie(dReal(0), dReal(1), dReal(2), dReal(3)) = + std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2]), dReal(order[3])}; + helpers::print(dReal, "D_REAL (sorted)", true); + + // swap columns of p according to order + matrix4x4 pOrig = p; + for (int i = 0; i < static_cast(order.size()); ++i) { + p.col(i) = pOrig.col(order[i]); } - Eigen::Matrix M {{1, 1, 0}, {1, 0, 1}, {0, 1, 1}}; - cs = M * dReal.head(3); - if (cs[2] < 0.0) { - cs[0] = 1.0 - cs[0]; - cs[2] *= -1.0; + + if (p.determinant().real() < 0.0) { + std::cerr << "SECOND CORRECTION?\n"; + auto lastColumnIndex = p.cols() - 1; + p.col(lastColumnIndex) *= -1.0; + } else { + // p = -1.0 * p; + // p += matrix4x4::Constant(0.0); // ensure no -0.0 exists } - dReal *= qc::PI; - cs *= qc::PI; assert(std::abs(p.determinant() - 1.0) < 1e-13); matrix4x4 temp = dReal.asDiagonal(); temp *= IM; temp = temp.exp(); + // temp = temp.conjugate(); + // temp += matrix4x4::Constant(0.0); helpers::print(temp, "TEMP", true); helpers::print(p, "P", true); // https://threeplusone.com/pubs/on_gates.pdf @@ -990,7 +974,8 @@ struct GateDecompositionPattern final K1r = K1r * IPZ; globalPhase -= qc::PI_2; } - auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); + helpers::print(cs, "CS (2)", true); + auto [a, b, c] = std::tie(cs[2], cs[1], cs[3]); auto isClose = [&](fp ap, fp bp, fp cp) -> bool { auto da = a - ap; auto db = b - bp; From 3a800c6479a65d6884e9e994354a62a12dee642e Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 13 Nov 2025 00:33:03 +0100 Subject: [PATCH 055/237] undo ordering of all columns --- .../Transforms/GateDecompositionPattern.cpp | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index c1e4d003c6..24e02c46e9 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -852,7 +852,7 @@ struct GateDecompositionPattern final rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; helpers::print(dReal, "D_REAL", true); dReal(3) = -dReal(0) - dReal(1) - dReal(2); - Eigen::Vector cs{}; + Eigen::Vector cs{}; for (int i = 0; i < static_cast(cs.size()); ++i) { assert(i < dReal.size()); cs[i] = remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); @@ -865,7 +865,7 @@ struct GateDecompositionPattern final return std::min(tmp, qc::PI_2 - tmp); }); std::array order{ - 0, 1, 2, 3}; // TODO: needs to be adjusted depending on eigenvector + 0, 1, 2}; // TODO: needs to be adjusted depending on eigenvector // order in eigen decomposition algorithm? llvm::stable_sort(order, [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); @@ -876,12 +876,12 @@ struct GateDecompositionPattern final // tmp2 = std::min(tmp2, qc::PI_2 - tmp2); // return tmp1 < tmp2; // }); - std::tie(order[0], order[1], order[2], order[3]) = - std::tuple{order[3], order[1], order[2], order[0]}; - std::tie(cs[0], cs[1], cs[2], cs[3]) = - std::tuple{cs[order[0]], cs[order[1]], cs[order[2]], cs[order[3]]}; - std::tie(dReal(0), dReal(1), dReal(2), dReal(3)) = - std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2]), dReal(order[3])}; + std::tie(order[0], order[1], order[2]) = + std::tuple{order[1], order[2], order[0]}; + std::tie(cs[0], cs[1], cs[2]) = + std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; + std::tie(dReal(0), dReal(1), dReal(2)) = + std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2])}; helpers::print(dReal, "D_REAL (sorted)", true); // swap columns of p according to order @@ -898,7 +898,6 @@ struct GateDecompositionPattern final // p = -1.0 * p; // p += matrix4x4::Constant(0.0); // ensure no -0.0 exists } - assert(std::abs(p.determinant() - 1.0) < 1e-13); matrix4x4 temp = dReal.asDiagonal(); temp *= IM; @@ -907,6 +906,7 @@ struct GateDecompositionPattern final // temp += matrix4x4::Constant(0.0); helpers::print(temp, "TEMP", true); helpers::print(p, "P", true); + assert(std::abs(p.determinant() - 1.0) < 1e-13); // https://threeplusone.com/pubs/on_gates.pdf // uP = V, m2 = V^T*V, temp = D, p = Q1 matrix4x4 k1 = uP * p * temp; @@ -975,7 +975,7 @@ struct GateDecompositionPattern final globalPhase -= qc::PI_2; } helpers::print(cs, "CS (2)", true); - auto [a, b, c] = std::tie(cs[2], cs[1], cs[3]); + auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); auto isClose = [&](fp ap, fp bp, fp cp) -> bool { auto da = a - ap; auto db = b - bp; From c155100292aa8a096b47ab4c7790e5c9174015e7 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 13 Nov 2025 11:12:04 +0100 Subject: [PATCH 056/237] implement algorithm based on quantumflow.canonical_decomposition --- .../Transforms/GateDecompositionPattern.cpp | 155 ++++++++++++------ 1 file changed, 102 insertions(+), 53 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 24e02c46e9..1dd3b11bc1 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -16,8 +16,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -846,62 +846,109 @@ struct GateDecompositionPattern final // make sure determinant of sqrt(eigenvalues) is 1.0 assert(std::abs(matrix4x4{d.asDiagonal()}.determinant() - 1.0) < 1e-13); - // see - // https://github.com/mpham26uchicago/laughing-umbrella/blob/main/background/Full%20Two%20Qubit%20KAK%20Implementation.ipynb, - // Step 7 - rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; - helpers::print(dReal, "D_REAL", true); - dReal(3) = -dReal(0) - dReal(1) - dReal(2); - Eigen::Vector cs{}; - for (int i = 0; i < static_cast(cs.size()); ++i) { - assert(i < dReal.size()); - cs[i] = remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); - } - helpers::print(cs, "CS (1)", true); - - decltype(cs) cstemp; - llvm::transform(cs, cstemp.begin(), [](auto&& x) { - auto tmp = remEuclid(x, qc::PI_2); - return std::min(tmp, qc::PI_2 - tmp); - }); - std::array order{ - 0, 1, 2}; // TODO: needs to be adjusted depending on eigenvector - // order in eigen decomposition algorithm? - llvm::stable_sort(order, - [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); - // llvm::stable_sort(order, [&](fp a, fp b) { - // auto tmp1 = remEuclid(cs[a], qc::PI_2); - // tmp1 = std::min(tmp1, qc::PI_2 - tmp1); - // auto tmp2 = remEuclid(cs[b], qc::PI_2); - // tmp2 = std::min(tmp2, qc::PI_2 - tmp2); - // return tmp1 < tmp2; - // }); - std::tie(order[0], order[1], order[2]) = - std::tuple{order[1], order[2], order[0]}; - std::tie(cs[0], cs[1], cs[2]) = - std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; - std::tie(dReal(0), dReal(1), dReal(2)) = - std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2])}; - helpers::print(dReal, "D_REAL (sorted)", true); - - // swap columns of p according to order - matrix4x4 pOrig = p; - for (int i = 0; i < static_cast(order.size()); ++i) { - p.col(i) = pOrig.col(order[i]); + diagonal4x4 q = d.cwiseSqrt(); + auto det_q = q.prod(); + if (det_q.real() < 0.0) { + q[0] *= -1.0; } + // constrain to weyl + auto constrain_to_weyl = [](diagonal4x4 q) { + auto in_weyl = [](fp tx, fp ty, fp tz) { + return (0.5 >= tx && tx >= ty && ty >= tz && tz >= 0) || + (0.5 >= (1 - tx) && (1 - tx) >= ty && ty >= tz && tz > 0); + }; + auto lambdas_to_coords = [](diagonal4x4 lambdas) { + // [2, eq.11], but using [1]s coordinates. + constexpr fp TOLERANCE = 1e-13; + + auto [l1, l2, _, l4] = + std::array{lambdas[0], lambdas[1], lambdas[2], lambdas[3]}; + auto c1 = std::real(IM * std::log(l1 * l2)); + auto c2 = std::real(IM * std::log(l2 * l4)); + auto c3 = std::real(IM * std::log(l1 * l4)); + Eigen::Vector coords{c1, c2, c3}; + coords /= qc::PI; + + // if coords[i] == 1, then coords[i] = -1, else coords[i] = coords[i] + coords = (coords - decltype(coords)::Ones()) + .cwiseAbs() + .cwiseLess(TOLERANCE) + .select(-decltype(coords)::Ones(), coords) + .eval(); + if (coords.cwiseLess(0.0).all()) { + coords += decltype(coords)::Ones(); + } - if (p.determinant().real() < 0.0) { - std::cerr << "SECOND CORRECTION?\n"; - auto lastColumnIndex = p.cols() - 1; - p.col(lastColumnIndex) *= -1.0; - } else { - // p = -1.0 * p; - // p += matrix4x4::Constant(0.0); // ensure no -0.0 exists + // If we're close to the boundary, floating point errors can conspire + // to make it seem that we're never on the inside + // Fix: If near boundary, reset to boundary + + // Left + if (std::abs(coords[0] - coords[1]) < TOLERANCE) { + coords[1] = coords[0]; + } + + // Front + if (std::abs(coords[1] - coords[2]) < TOLERANCE) { + coords[2] = coords[1]; + } + + // Right + if (std::abs(coords[0] - coords[1] - 1.0 / 2.0) < TOLERANCE) { + coords[1] = coords[0] - 1.0 / 2.0; + } + + // Base + coords = + (coords.array() < 0).select(decltype(coords)::Zero(), coords); + + return coords; + }; + + auto permutation = std::array{0, 1, 2, 3}; + while (true) { + for (auto signs : + std::array, 4>{{{1, 1, 1, 1}, + {1, 1, -1, -1}, + {-1, 1, -1, 1}, + {1, -1, -1, 1}}}) { + decltype(q) signed_lambdas = q.cwiseProduct(signs); + // reorder according to permutation + decltype(signed_lambdas) lambdas_perm; + for (std::size_t i = 0; i < permutation.size(); ++i) { + lambdas_perm[i] = signed_lambdas[permutation[i]]; + } + + auto coords = lambdas_to_coords(lambdas_perm); + + if (in_weyl(coords[0], coords[1], coords[2])) { + return std::make_tuple(coords, permutation, signs); + } + } + if (!std::ranges::next_permutation(permutation).found) { + throw std::runtime_error{ + "Unable to find permutation to calculate Weyl coordinates!"}; + } + } + }; + + auto [cs, permutation, signs] = constrain_to_weyl(q); + + q = q.cwiseProduct(signs); + auto origQ = q; + matrix4x4 origP = signs.asDiagonal() * p.transpose(); + assert(permutation.size() == q.size()); + assert(permutation.size() == p.cols()); + for (std::size_t i = 0; i < permutation.size(); ++i) { + q[i] = origQ[permutation[i]]; + p.row(i) = origP.row(permutation[i]); } + p.transposeInPlace(); + matrix4x4 temp = q.asDiagonal(); + temp = temp.conjugate(); + + - matrix4x4 temp = dReal.asDiagonal(); - temp *= IM; - temp = temp.exp(); // temp = temp.conjugate(); // temp += matrix4x4::Constant(0.0); helpers::print(temp, "TEMP", true); @@ -912,10 +959,12 @@ struct GateDecompositionPattern final matrix4x4 k1 = uP * p * temp; helpers::print(k1, "K1 (1)", true); assert((k1.transpose() * k1).isIdentity()); // k1 must be orthogonal + assert(k1.determinant().real() > 0.0); k1 = magicBasisTransform(k1, MagicBasisTransform::Into); matrix4x4 k2 = p.transpose().conjugate(); helpers::print(k2, "K2 (1)", true); assert((k2.transpose() * k2).isIdentity()); // k2 must be orthogonal + assert(k2.determinant().real() > 0.0); k2 = magicBasisTransform(k2, MagicBasisTransform::Into); auto [K1l, K1r, phase_l] = decomposeTwoQubitProductGate(k1); From b6ff17fee834902f684d6d32e5fa6100c3f7fe8e Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 13 Nov 2025 18:51:08 +0100 Subject: [PATCH 057/237] sanity checks and stuff --- .../Transforms/GateDecompositionPattern.cpp | 211 ++++++++---------- 1 file changed, 96 insertions(+), 115 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 1dd3b11bc1..030bdc1be6 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -63,17 +63,7 @@ struct GateDecompositionPattern final return mlir::failure(); } - matrix4x4 unitaryMatrix = - helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); - int i{}; - for (auto&& gate : series.gates) { - auto gateMatrix = - getTwoQubitMatrix({.type = helpers::getQcType(gate.op), - .parameter = helpers::getParameters(gate.op), - .qubitId = gate.qubitIds}); - unitaryMatrix = gateMatrix * unitaryMatrix; - helpers::print(gateMatrix, "GATE MATRIX " + std::to_string(i++), true); - } + matrix4x4 unitaryMatrix = series.getUnitaryMatrix(); helpers::print(unitaryMatrix, "UNITARY MATRIX", true); auto decomposer = TwoQubitBasisDecomposer::newInner(); @@ -144,6 +134,19 @@ struct GateDecompositionPattern final return result; } + matrix4x4 getUnitaryMatrix() { + matrix4x4 unitaryMatrix = + helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); + for (auto&& gate : gates) { + auto gateMatrix = + getTwoQubitMatrix({.type = helpers::getQcType(gate.op), + .parameter = helpers::getParameters(gate.op), + .qubitId = gate.qubitIds}); + unitaryMatrix = gateMatrix * unitaryMatrix; + } + return unitaryMatrix; + } + private: explicit TwoQubitSeries(UnitaryInterface initialOperation) { auto&& in = initialOperation.getAllInQubits(); @@ -310,7 +313,12 @@ struct GateDecompositionPattern final } std::cerr << '\n'; std::cerr << "GATE SEQUENCE!: " << std::flush; + matrix4x4 unitaryMatrix = + helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); for (auto&& gate : sequence.gates) { + auto gateMatrix = getTwoQubitMatrix(gate); + unitaryMatrix = gateMatrix * unitaryMatrix; + std::cerr << qc::toString(gate.type) << ", "; if (gate.type == qc::X) { mlir::SmallVector inCtrlQubits; @@ -348,6 +356,9 @@ struct GateDecompositionPattern final throw std::runtime_error{"Unknown gate type!"}; } } + std::cerr << '\n'; + helpers::print(series.getUnitaryMatrix(), "ORIGINAL UNITARY", true); + helpers::print(unitaryMatrix, "RESULT UNITARY MATRIX", true); rewriter.replaceAllUsesWith(series.outQubits, inQubits); for (auto&& gate : llvm::reverse(series.gates)) { @@ -443,41 +454,9 @@ struct GateDecompositionPattern final decomposeTwoQubitProductGate(matrix4x4 specialUnitary) { // see pennylane math.decomposition.su2su2_to_tensor_products // or "7.1 Kronecker decomposition" in on_gates.pdf - helpers::print(specialUnitary, "SPECIAL_UNITARY"); - - // Step 1: Reshape and permute C like in Python - // C (4x4) is considered as (2,2,2,2) tensor, transpose(0,2,1,3) - matrix4x4 C; - - // Perform the same rearrangement as Python's transpose(0,2,1,3) - // This swaps certain 2x2 blocks in C_in. - // The pattern is determined by the tensor index mapping. - for (int i = 0; i < 2; ++i) - for (int j = 0; j < 2; ++j) - for (int k = 0; k < 2; ++k) - for (int l = 0; l < 2; ++l) { - int row_in = 2 * i + k; - int col_in = 2 * j + l; - int row_out = 2 * i + j; - int col_out = 2 * k + l; - C(row_out, col_out) = specialUnitary(row_in, col_in); - } - - // Step 2: SVD - Eigen::JacobiSVD svd(C, Eigen::ComputeFullU | Eigen::ComputeFullV); - Eigen::Vector4d singularValues = svd.singularValues(); - matrix4x4 U = svd.matrixU(); - matrix4x4 V = svd.matrixV(); - - // Step 3: Extract first singular component - double s = std::sqrt(singularValues(0)); - - matrix2x2 A = s * U.col(0).reshaped(2, 2); - matrix2x2 B = s * V.col(0).reshaped(2, 2).transpose(); // vh[0, :] in numpy == V^T.row(0) - - // return {A, B, s}; - + // or quantumflow.kronecker_decomposition + helpers::print(specialUnitary, "SPECIAL_UNITARY"); // first quadrant matrix2x2 r{{specialUnitary(0, 0), specialUnitary(0, 1)}, {specialUnitary(1, 0), specialUnitary(1, 1)}}; @@ -488,7 +467,7 @@ struct GateDecompositionPattern final r = matrix2x2{{specialUnitary(2, 0), specialUnitary(2, 1)}, {specialUnitary(3, 0), specialUnitary(3, 1)}}; detR = r.determinant(); - std::cerr << "DET_R CORRECTION: " << detR << '\n'; + std::cerr << "DET_R CORRECTION: " << detR << '\n'; } if (std::abs(detR) < 0.1) { throw std::runtime_error{ @@ -595,6 +574,16 @@ struct GateDecompositionPattern final {{0., -sinTheta}, C_ZERO, C_ZERO, cosTheta}}; } + static matrix4x4 ryyMatrix(const fp theta) { + const auto cosTheta = std::cos(theta / 2.); + const auto sinTheta = std::sin(theta / 2.); + + return matrix4x4{{{cosTheta, 0, 0, {0., sinTheta}}, + {0, cosTheta, {0., -sinTheta}, 0}, + {0, {0., -sinTheta}, cosTheta, 0}, + {std::complex{0., sinTheta}, 0, 0, cosTheta}}}; + } + static matrix4x4 rzzMatrix(const fp theta) { const auto cosTheta = std::cos(theta / 2.); const auto sinTheta = std::sin(theta / 2.); @@ -719,15 +708,13 @@ struct GateDecompositionPattern final {1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}, {0, 1, 0, 0}}; } } - if (gate.type == qc::RZ) { - throw std::invalid_argument{"RZ for two-qubit gate matrix"}; + if (gate.type == qc::RXX) { // TODO: check qubit order - return rzzMatrix(gate.parameter[0]); + return rxxMatrix(gate.parameter[0]); } - if (gate.type == qc::RX) { - throw std::invalid_argument{"RX for two-qubit gate matrix"}; + if (gate.type == qc::RYY) { // TODO: check qubit order - return rxxMatrix(gate.parameter[0]); + return ryyMatrix(gate.parameter[0]); } if (gate.type == qc::RZZ) { // TODO: check qubit order @@ -748,9 +735,9 @@ struct GateDecompositionPattern final fp c; fp globalPhase; /** - * - k2r - X - k1r - - * X - * - k2l - X - k1l - + * q1 - k2r - C - k1r - + * A + * q0 - k2l - N - k1l - */ matrix2x2 k1l; matrix2x2 k2l; @@ -809,8 +796,8 @@ struct GateDecompositionPattern final // using the same rng values rules out possible RNG differences // as the root cause of a test failure if (i == 0) { - randA = 1.2602066112249388; randB = 0.22317849046722027; + randA = 1.0 - randB; } else { randA = dist(state); randB = dist(state); @@ -826,7 +813,7 @@ struct GateDecompositionPattern final matrix4x4 compare = pInner * diagD * pInner.transpose(); helpers::print(compare, "COMPARE"); - found = (compare - m2).cwiseAbs().cwiseLessOrEqual(1.0e-13).all(); + found = compare.isApprox(m2, 1e-13); if (found) { // p are the eigenvectors which are decomposed into the // single-qubit gates surrounding the canonical gate @@ -933,6 +920,7 @@ struct GateDecompositionPattern final }; auto [cs, permutation, signs] = constrain_to_weyl(q); + cs *= -qc::PI_2; q = q.cwiseProduct(signs); auto origQ = q; @@ -941,14 +929,12 @@ struct GateDecompositionPattern final assert(permutation.size() == p.cols()); for (std::size_t i = 0; i < permutation.size(); ++i) { q[i] = origQ[permutation[i]]; - p.row(i) = origP.row(permutation[i]); + p.col(i) = origP.row(permutation[i]); } - p.transposeInPlace(); + // p.transposeInPlace(); matrix4x4 temp = q.asDiagonal(); temp = temp.conjugate(); - - // temp = temp.conjugate(); // temp += matrix4x4::Constant(0.0); helpers::print(temp, "TEMP", true); @@ -967,64 +953,59 @@ struct GateDecompositionPattern final assert(k2.determinant().real() > 0.0); k2 = magicBasisTransform(k2, MagicBasisTransform::Into); + helpers::print( + (k1 * + magicBasisTransform(temp.conjugate(), MagicBasisTransform::Into) * + k2) + .eval(), + "SANITY CHECK (1)", true); + assert((k1 * + magicBasisTransform(temp.conjugate(), MagicBasisTransform::Into) * + k2) + .isApprox(u, 1e-8)); + auto [K1l, K1r, phase_l] = decomposeTwoQubitProductGate(k1); auto [K2l, K2r, phase_r] = decomposeTwoQubitProductGate(k2); globalPhase += phase_l + phase_r; - // Flip into Weyl chamber - if (cs[0] > qc::PI_2) { - cs[0] -= 3.0 * qc::PI_2; - K1l = K1l * IPY; - K1r = K1r * IPY; - globalPhase += qc::PI_2; - } - if (cs[1] > qc::PI_2) { - cs[1] -= 3.0 * qc::PI_2; - K1l = K1l * IPX; - K1r = K1r * IPX; - globalPhase += qc::PI_2; - } - auto conjs = 0; - if (cs[0] > qc::PI_4) { - cs[0] = qc::PI_2 - cs[0]; - K1l = K1l * IPY; - K2r = IPY * K2r; - conjs += 1; - globalPhase -= qc::PI_2; - } - if (cs[1] > qc::PI_4) { - cs[1] = qc::PI_2 - cs[1]; - K1l = K1l * IPX; - K2r = IPX * K2r; - conjs += 1; - globalPhase += qc::PI_2; - if (conjs == 1) { - globalPhase -= qc::PI; - } - } - if (cs[2] > qc::PI_2) { - cs[2] -= 3.0 * qc::PI_2; - K1l = K1l * IPZ; - K1r = K1r * IPZ; - globalPhase += qc::PI_2; - if (conjs == 1) { - globalPhase -= qc::PI; - } - } - if (conjs == 1) { - cs[2] = qc::PI_2 - cs[2]; - K1l = K1l * IPZ; - K2r = IPZ * K2r; - globalPhase += qc::PI_2; - } - if (cs[2] > qc::PI_4) { - cs[2] -= qc::PI_2; - K1l = K1l * IPZ; - K1r = K1r * IPZ; - globalPhase -= qc::PI_2; - } + helpers::print(K1l, "K1l (1)", true); + helpers::print(K2l, "K2l (1)", true); + helpers::print(K1r, "K1r (1)", true); + helpers::print(K2r, "K2r (1)", true); + helpers::print(cs, "CS (2)", true); - auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); + helpers::print(K1l, "K1l (2)", true); + helpers::print(K2l, "K2l (2)", true); + helpers::print(K1r, "K1r (2)", true); + helpers::print(K2r, "K2r (2)", true); + auto [a, b, c] = std::tie(cs[0], cs[1], cs[2]); + auto getCanonicalMatrix = [](fp a, fp b, fp c) -> matrix4x4 { + auto xx = getTwoQubitMatrix({ + .type = qc::RXX, + .parameter = {a}, + .qubitId = {0, 1}, + }); + auto yy = getTwoQubitMatrix({ + .type = qc::RYY, + .parameter = {b}, + .qubitId = {0, 1}, + }); + auto zz = getTwoQubitMatrix({ + .type = qc::RZZ, + .parameter = {c}, + .qubitId = {0, 1}, + }); + return zz * yy * xx; + }; + helpers::print(getCanonicalMatrix(a * 2.0, b * 2.0, c * 2.0), + "SANITY CHECK (2.1)", true); + helpers::print( + magicBasisTransform(temp.conjugate(), MagicBasisTransform::Into), + "SANITY CHECK (2.2)", true); + // assert(getCanonicalMatrix(a * -2.0, b * -2.0, c * -2.0) + // .isApprox(magicBasisTransform(temp.conjugate(), + // MagicBasisTransform::Into), + // 1e-8)); auto isClose = [&](fp ap, fp bp, fp cp) -> bool { auto da = a - ap; auto db = b - bp; @@ -1609,7 +1590,7 @@ struct GateDecompositionPattern final << "; basis_fid: " << actualBasisFidelity << "; Traces: " << traces[0] << ", " << traces[1] << ", " << traces[2] << ", " << traces[3]; - std::cerr << "\nDecomposition:\n"; + std::cerr << "\nDecompositions:\n"; for (auto x : decomposition) { helpers::print(x, "", true); } From 83c089e4663b6ade0f14911ead24f3544a87c140 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 13 Nov 2025 19:29:41 +0100 Subject: [PATCH 058/237] add original implementation again which seems to kind of work? --- .../Transforms/GateDecompositionPattern.cpp | 112 +++++++++++++++++- 1 file changed, 109 insertions(+), 3 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 030bdc1be6..f649b86b44 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -319,7 +319,7 @@ struct GateDecompositionPattern final auto gateMatrix = getTwoQubitMatrix(gate); unitaryMatrix = gateMatrix * unitaryMatrix; - std::cerr << qc::toString(gate.type) << ", "; + std::cerr << qc::toString(gate.type) << "(" << (gate.parameter.empty() ? -1000.0 : gate.parameter[0]) << ")" << ", "; if (gate.type == qc::X) { mlir::SmallVector inCtrlQubits; if (gate.qubitId.size() > 1) { @@ -921,6 +921,7 @@ struct GateDecompositionPattern final auto [cs, permutation, signs] = constrain_to_weyl(q); cs *= -qc::PI_2; + matrix4x4 p2 = p; q = q.cwiseProduct(signs); auto origQ = q; @@ -929,12 +930,64 @@ struct GateDecompositionPattern final assert(permutation.size() == p.cols()); for (std::size_t i = 0; i < permutation.size(); ++i) { q[i] = origQ[permutation[i]]; - p.col(i) = origP.row(permutation[i]); + p2.col(i) = origP.row(permutation[i]); } // p.transposeInPlace(); matrix4x4 temp = q.asDiagonal(); temp = temp.conjugate(); + // see + // https://github.com/mpham26uchicago/laughing-umbrella/blob/main/background/Full%20Two%20Qubit%20KAK%20Implementation.ipynb, + // Step 7 + rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; + helpers::print(dReal, "D_REAL", true); + dReal(3) = -dReal(0) - dReal(1) - dReal(2); + for (int i = 0; i < static_cast(cs.size()); ++i) { + assert(i < dReal.size()); + cs[i] = remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); + } + helpers::print(cs, "CS (1)", true); + + decltype(cs) cstemp; + llvm::transform(cs, cstemp.begin(), [](auto&& x) { + auto tmp = remEuclid(x, qc::PI_2); + return std::min(tmp, qc::PI_2 - tmp); + }); + std::array order{ + 0, 1, 2}; // TODO: needs to be adjusted depending on eigenvector + // order in eigen decomposition algorithm? + llvm::stable_sort(order, + [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); + // llvm::stable_sort(order, [&](fp a, fp b) { + // auto tmp1 = remEuclid(cs[a], qc::PI_2); + // tmp1 = std::min(tmp1, qc::PI_2 - tmp1); + // auto tmp2 = remEuclid(cs[b], qc::PI_2); + // tmp2 = std::min(tmp2, qc::PI_2 - tmp2); + // return tmp1 < tmp2; + // }); + std::tie(order[0], order[1], order[2]) = + std::tuple{order[1], order[2], order[0]}; + std::tie(cs[0], cs[1], cs[2]) = + std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; + std::tie(dReal(0), dReal(1), dReal(2)) = + std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2])}; + helpers::print(dReal, "D_REAL (sorted)", true); + + // swap columns of p according to order + matrix4x4 pOrig = p; + for (int i = 0; i < static_cast(order.size()); ++i) { + p.col(i) = pOrig.col(order[i]); + } + if (p.determinant().real() < 0.0) { + std::cerr << "SECOND CORRECTION?\n"; + auto lastColumnIndex = p.cols() - 1; + p.col(lastColumnIndex) *= -1.0; + } + + temp = dReal.asDiagonal(); + temp *= IM; + temp = temp.exp(); + // temp = temp.conjugate(); // temp += matrix4x4::Constant(0.0); helpers::print(temp, "TEMP", true); @@ -968,6 +1021,59 @@ struct GateDecompositionPattern final auto [K2l, K2r, phase_r] = decomposeTwoQubitProductGate(k2); globalPhase += phase_l + phase_r; + // Flip into Weyl chamber + if (cs[0] > qc::PI_2) { + cs[0] -= 3.0 * qc::PI_2; + K1l = K1l * IPY; + K1r = K1r * IPY; + globalPhase += qc::PI_2; + } + if (cs[1] > qc::PI_2) { + cs[1] -= 3.0 * qc::PI_2; + K1l = K1l * IPX; + K1r = K1r * IPX; + globalPhase += qc::PI_2; + } + auto conjs = 0; + if (cs[0] > qc::PI_4) { + cs[0] = qc::PI_2 - cs[0]; + K1l = K1l * IPY; + K2r = IPY * K2r; + conjs += 1; + globalPhase -= qc::PI_2; + } + if (cs[1] > qc::PI_4) { + cs[1] = qc::PI_2 - cs[1]; + K1l = K1l * IPX; + K2r = IPX * K2r; + conjs += 1; + globalPhase += qc::PI_2; + if (conjs == 1) { + globalPhase -= qc::PI; + } + } + if (cs[2] > qc::PI_2) { + cs[2] -= 3.0 * qc::PI_2; + K1l = K1l * IPZ; + K1r = K1r * IPZ; + globalPhase += qc::PI_2; + if (conjs == 1) { + globalPhase -= qc::PI; + } + } + if (conjs == 1) { + cs[2] = qc::PI_2 - cs[2]; + K1l = K1l * IPZ; + K2r = IPZ * K2r; + globalPhase += qc::PI_2; + } + if (cs[2] > qc::PI_4) { + cs[2] -= qc::PI_2; + K1l = K1l * IPZ; + K1r = K1r * IPZ; + globalPhase -= qc::PI_2; + } + helpers::print(K1l, "K1l (1)", true); helpers::print(K2l, "K2l (1)", true); helpers::print(K1r, "K1r (1)", true); @@ -978,7 +1084,7 @@ struct GateDecompositionPattern final helpers::print(K2l, "K2l (2)", true); helpers::print(K1r, "K1r (2)", true); helpers::print(K2r, "K2r (2)", true); - auto [a, b, c] = std::tie(cs[0], cs[1], cs[2]); + auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); auto getCanonicalMatrix = [](fp a, fp b, fp c) -> matrix4x4 { auto xx = getTwoQubitMatrix({ .type = qc::RXX, From a94195f08741f625555adce1db8a453604c283b5 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 14 Nov 2025 12:16:54 +0100 Subject: [PATCH 059/237] generate openqasm code of generated sequence --- .../Transforms/GateDecompositionPattern.cpp | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index f649b86b44..0787abc808 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -312,21 +312,39 @@ struct GateDecompositionPattern final std::cerr << gate.op->getName().stripDialect().str() << ", "; } std::cerr << '\n'; - std::cerr << "GATE SEQUENCE!: " << std::flush; + std::cerr << "GATE SEQUENCE!: \n"; matrix4x4 unitaryMatrix = helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); for (auto&& gate : sequence.gates) { auto gateMatrix = getTwoQubitMatrix(gate); unitaryMatrix = gateMatrix * unitaryMatrix; - std::cerr << qc::toString(gate.type) << "(" << (gate.parameter.empty() ? -1000.0 : gate.parameter[0]) << ")" << ", "; + if (gate.type == qc::X && gate.qubitId.size() == 2) { + std::cerr << std::format("cx() q[{}], q[{}];", gate.qubitId[1], + gate.qubitId[0]); + } else if (gate.parameter.empty()) { + std::cerr << std::format( + "{}() q[{}] {};", qc::toString(gate.type), gate.qubitId[0], + (gate.qubitId.size() > 1 + ? (", q[" + std::to_string(gate.qubitId[1]) + "]") + : std::string{})); + } else { + std::cerr << std::format( + "{}({:.5}*pi) q[{}] {};", qc::toString(gate.type), + gate.parameter[0] / qc::PI, gate.qubitId[0], + (gate.qubitId.size() > 1 + ? (", q[" + std::to_string(gate.qubitId[1]) + "]") + : std::string{})); + } + std::cerr << '\n'; if (gate.type == qc::X) { mlir::SmallVector inCtrlQubits; if (gate.qubitId.size() > 1) { inCtrlQubits.push_back(inQubits[gate.qubitId[1]]); } - auto newGate = createGate(rewriter, location, {inQubits[0]}, - inCtrlQubits, gate.parameter); + auto newGate = + createGate(rewriter, location, {inQubits[gate.qubitId[0]]}, + inCtrlQubits, gate.parameter); updateInQubits(gate, newGate); } else if (gate.type == qc::RX) { mlir::SmallVector qubits; @@ -358,7 +376,8 @@ struct GateDecompositionPattern final } std::cerr << '\n'; helpers::print(series.getUnitaryMatrix(), "ORIGINAL UNITARY", true); - helpers::print(unitaryMatrix, "RESULT UNITARY MATRIX", true); + helpers::print((unitaryMatrix * std::exp(IM * sequence.globalPhase)).eval(), + "RESULT UNITARY MATRIX", true); rewriter.replaceAllUsesWith(series.outQubits, inQubits); for (auto&& gate : llvm::reverse(series.gates)) { From ab57b7d7bce4aff58705a371214be9f999741e2f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 14 Nov 2025 12:17:49 +0100 Subject: [PATCH 060/237] some more small fixes, maybe breakthrough? --- .../Transforms/GateDecompositionPattern.cpp | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 0787abc808..7f5e35fa3f 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -433,8 +433,7 @@ struct GateDecompositionPattern final // Wrap angle into interval [-π,π). If within atol of the endpoint, clamp // to -π - static fp mod2pi(fp angle, - fp angleZeroEpsilon = std::numeric_limits::epsilon()) { + static fp mod2pi(fp angle, fp angleZeroEpsilon = 1e-13) { // remEuclid() isn't exactly the same as Python's % operator, but // because the RHS here is a constant and positive it is effectively // equivalent for this case @@ -708,21 +707,21 @@ struct GateDecompositionPattern final } if (gate.qubitId.size() == 1) { if (gate.qubitId[0] == 0) { - return kroneckerProduct(IDENTITY_GATE, getSingleQubitMatrix(gate)); + return kroneckerProduct(getSingleQubitMatrix(gate), IDENTITY_GATE); } if (gate.qubitId[0] == 1) { - return kroneckerProduct(getSingleQubitMatrix(gate), IDENTITY_GATE); + return kroneckerProduct(IDENTITY_GATE, getSingleQubitMatrix(gate)); } throw std::logic_error{"Invalid qubit ID in getTwoQubitMatrix"}; } if (gate.qubitId.size() == 2) { if (gate.type == qc::X) { // controlled X (CX) - if (gate.qubitId == llvm::SmallVector{1, 0}) { + if (gate.qubitId == llvm::SmallVector{0, 1}) { return matrix4x4{ {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}}; } - if (gate.qubitId == llvm::SmallVector{0, 1}) { + if (gate.qubitId == llvm::SmallVector{1, 0}) { return matrix4x4{ {1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}, {0, 1, 0, 0}}; } @@ -771,7 +770,7 @@ struct GateDecompositionPattern final static TwoQubitWeylDecomposition newInner(matrix4x4 unitaryMatrix, std::optional fidelity, std::optional specialization) { - auto& u = unitaryMatrix; + auto u = unitaryMatrix; auto detU = u.determinant(); std::cerr << "DET_U: " << detU << '\n'; auto detPow = std::pow(detU, static_cast(-0.25)); @@ -815,8 +814,8 @@ struct GateDecompositionPattern final // using the same rng values rules out possible RNG differences // as the root cause of a test failure if (i == 0) { + randA = 1.2602066112249388; randB = 0.22317849046722027; - randA = 1.0 - randB; } else { randA = dist(state); randB = dist(state); @@ -997,7 +996,7 @@ struct GateDecompositionPattern final for (int i = 0; i < static_cast(order.size()); ++i) { p.col(i) = pOrig.col(order[i]); } - if (p.determinant().real() < 0.0) { + if (p.determinant().real() < 0.0) { std::cerr << "SECOND CORRECTION?\n"; auto lastColumnIndex = p.cols() - 1; p.col(lastColumnIndex) *= -1.0; @@ -1038,6 +1037,8 @@ struct GateDecompositionPattern final auto [K1l, K1r, phase_l] = decomposeTwoQubitProductGate(k1); auto [K2l, K2r, phase_r] = decomposeTwoQubitProductGate(k2); + assert(helpers::kroneckerProduct(K1l, K1r).isApprox(k1, 1e-9)); + assert(helpers::kroneckerProduct(K2l, K2r).isApprox(k2, 1e-9)); globalPhase += phase_l + phase_r; // Flip into Weyl chamber @@ -1122,15 +1123,24 @@ struct GateDecompositionPattern final }); return zz * yy * xx; }; - helpers::print(getCanonicalMatrix(a * 2.0, b * 2.0, c * 2.0), + helpers::print(getCanonicalMatrix(a * -2.0, b * -2.0, c * -2.0), "SANITY CHECK (2.1)", true); - helpers::print( - magicBasisTransform(temp.conjugate(), MagicBasisTransform::Into), - "SANITY CHECK (2.2)", true); - // assert(getCanonicalMatrix(a * -2.0, b * -2.0, c * -2.0) - // .isApprox(magicBasisTransform(temp.conjugate(), - // MagicBasisTransform::Into), - // 1e-8)); + helpers::print(helpers::kroneckerProduct(K1l, K1r), "SANITY CHECK (2.2)", + true); + helpers::print(helpers::kroneckerProduct(K2l, K2r), "SANITY CHECK (2.3)", + true); + helpers::print((helpers::kroneckerProduct(K1l, K1r) * + getCanonicalMatrix(a * -2.0, b * -2.0, c * -2.0) * + helpers::kroneckerProduct(K2l, K2r) * + std::exp(IM * globalPhase)) + .eval(), + "SANITY CHECK (2.x)", true); + std::cerr << "gphase: " << globalPhase << ", phase_l: " << phase_l + << ", phase_r: " << phase_r << '\n'; + assert((helpers::kroneckerProduct(K1l, K1r) * + getCanonicalMatrix(a * -2.0, b * -2.0, c * -2.0) * + helpers::kroneckerProduct(K2l, K2r) * std::exp(IM * globalPhase)) + .isApprox(unitaryMatrix, 1e-8)); auto isClose = [&](fp ap, fp bp, fp cp) -> bool { auto da = a - ap; auto db = b - bp; @@ -1563,10 +1573,8 @@ struct GateDecompositionPattern final auto basisDecomposer = TwoQubitWeylDecomposition::newInner( getTwoQubitMatrix(basisGate), DEFAULT_FIDELITY, std::nullopt); auto superControlled = - relativeEq(basisDecomposer.a, qc::PI_4, - std::numeric_limits::epsilon(), 1e-09) && - relativeEq(basisDecomposer.c, 0.0, std::numeric_limits::epsilon(), - 1e-09); + relativeEq(basisDecomposer.a, qc::PI_4, 1e-13, 1e-09) && + relativeEq(basisDecomposer.c, 0.0, 1e-13, 1e-09); // Create some useful matrices U1, U2, U3 are equivalent to the basis, // expand as Ui = Ki1.Ubasis.Ki2 From af6f4115236fd5d37c5dea09c642e400f127ecfd Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 14 Nov 2025 14:09:28 +0100 Subject: [PATCH 061/237] rework getTwoQubitSeries logic --- .../Transforms/GateDecompositionPattern.cpp | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 7f5e35fa3f..e50b838ff4 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -115,20 +115,34 @@ struct GateDecompositionPattern final return false; }; - bool isFirstQubitOngoing = result.outQubits[0] != mlir::Value{}; - bool isSecondQubitOngoing = result.outQubits[1] != mlir::Value{}; - while (isFirstQubitOngoing || isSecondQubitOngoing) { - // TODO: can cause issues; instead: per iteration, collect all - // single-qubit operations, then take one two-qubit operation; repeat - if (result.outQubits[0]) { - assert(result.outQubits[0].hasOneUse()); - isFirstQubitOngoing = - findNextInSeries(*result.outQubits[0].getUsers().begin()); + auto getUser = + [](mlir::Value qubit, auto&& filter) -> std::optional { + if (qubit) { + assert(qubit.hasOneUse()); + auto user = + mlir::dyn_cast(*qubit.getUsers().begin()); + if (user && filter(user)) { + return user; + } } - if (result.outQubits[1]) { - assert(result.outQubits[1].hasOneUse()); - isSecondQubitOngoing = - findNextInSeries(*result.outQubits[1].getUsers().begin()); + return std::nullopt; + }; + + bool foundGate = true; + while (foundGate) { + // collect all available single-qubit operations + for (std::size_t i = 0; i < result.outQubits.size(); ++i) { + while (auto user = getUser(result.outQubits[i], &helpers::isSingleQubitOperation)) { + result.appendSingleQubitGate(*user); + foundGate = true; + } + } + + for (std::size_t i = 0; i < result.outQubits.size(); ++i) { + while (auto user = getUser(result.outQubits[i], &helpers::isTwoQubitOperation)) { + result.appendTwoQubitGate(*user); + foundGate = true; + } } } return result; From 8a9447b8094d138c06f94be59a72372c94ed092b Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 14 Nov 2025 14:10:16 +0100 Subject: [PATCH 062/237] generate openqasm code of found series --- .../Transforms/GateDecompositionPattern.cpp | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index e50b838ff4..efae389413 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -323,7 +323,27 @@ struct GateDecompositionPattern final std::cerr << "SERIES: "; for (auto&& gate : series.gates) { - std::cerr << gate.op->getName().stripDialect().str() << ", "; + auto name = gate.op->getName().stripDialect().str(); + if (name == "x" && gate.qubitIds.size() == 2) { + // controls come first + std::cerr << std::format("cx() q[{}], q[{}];", gate.qubitIds[0], + gate.qubitIds[1]); + } else if (name == "i") { + } else if (gate.op.getParams().empty()) { + std::cerr << std::format( + "{}() q[{}] {};", name, gate.qubitIds[0], + (gate.qubitIds.size() > 1 + ? (", q[" + std::to_string(gate.qubitIds[1]) + "]") + : std::string{})); + } else { + auto parameter = helpers::getParameters(gate.op)[0]; + std::cerr << std::format( + "{}({:.5}*pi) q[{}] {};", name, parameter / qc::PI, + gate.qubitIds[0], + (gate.qubitIds.size() > 1 + ? (", q[" + std::to_string(gate.qubitIds[1]) + "]") + : std::string{})); + } } std::cerr << '\n'; std::cerr << "GATE SEQUENCE!: \n"; @@ -334,8 +354,9 @@ struct GateDecompositionPattern final unitaryMatrix = gateMatrix * unitaryMatrix; if (gate.type == qc::X && gate.qubitId.size() == 2) { - std::cerr << std::format("cx() q[{}], q[{}];", gate.qubitId[1], - gate.qubitId[0]); + // controls come first + std::cerr << std::format("cx() q[{}], q[{}];", gate.qubitId[0], + gate.qubitId[1]); } else if (gate.parameter.empty()) { std::cerr << std::format( "{}() q[{}] {};", qc::toString(gate.type), gate.qubitId[0], From e4b68d17ba3f644b7459605309c34c4d59ad0d54 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 14 Nov 2025 14:12:36 +0100 Subject: [PATCH 063/237] fix new getTwoQubitSeries logic --- .../Transforms/GateDecompositionPattern.cpp | 33 ++++++------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index efae389413..9b60002c07 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -100,23 +100,8 @@ struct GateDecompositionPattern final [[nodiscard]] static TwoQubitSeries getTwoQubitSeries(UnitaryInterface op) { TwoQubitSeries result(op); - auto findNextInSeries = [&](mlir::Operation* user) { - auto userUnitary = mlir::dyn_cast(user); - if (!userUnitary) { - return false; - } - - if (helpers::isSingleQubitOperation(userUnitary)) { - return result.appendSingleQubitGate(userUnitary); - } - if (helpers::isTwoQubitOperation(userUnitary)) { - return result.appendTwoQubitGate(userUnitary); - } - return false; - }; - - auto getUser = - [](mlir::Value qubit, auto&& filter) -> std::optional { + auto getUser = [](mlir::Value qubit, + auto&& filter) -> std::optional { if (qubit) { assert(qubit.hasOneUse()); auto user = @@ -130,18 +115,20 @@ struct GateDecompositionPattern final bool foundGate = true; while (foundGate) { + foundGate = false; // collect all available single-qubit operations for (std::size_t i = 0; i < result.outQubits.size(); ++i) { - while (auto user = getUser(result.outQubits[i], &helpers::isSingleQubitOperation)) { - result.appendSingleQubitGate(*user); - foundGate = true; + while (auto user = getUser(result.outQubits[i], + &helpers::isSingleQubitOperation)) { + foundGate = result.appendSingleQubitGate(*user); + assert(foundGate); // appending a single-qubit gate should not fail } } for (std::size_t i = 0; i < result.outQubits.size(); ++i) { - while (auto user = getUser(result.outQubits[i], &helpers::isTwoQubitOperation)) { - result.appendTwoQubitGate(*user); - foundGate = true; + if (auto user = + getUser(result.outQubits[i], &helpers::isTwoQubitOperation)) { + foundGate = result.appendTwoQubitGate(*user); } } } From 1eea4af5580011b523acc444bad923fd84727c98 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 15 Nov 2025 20:20:32 +0100 Subject: [PATCH 064/237] working except global phase? --- .../Transforms/GateDecompositionPattern.cpp | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 9b60002c07..4379511e11 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -207,14 +207,22 @@ struct GateDecompositionPattern final firstQubitIt = (firstQubitIt != outQubits.end()) ? firstQubitIt : it; secondQubitIt = (secondQubitIt != outQubits.end()) ? secondQubitIt : it; } - *firstQubitIt = nextGate->getResult(0); std::size_t firstQubitId = std::distance(outQubits.begin(), firstQubitIt); - *secondQubitIt = nextGate->getResult(1); std::size_t secondQubitId = std::distance(outQubits.begin(), secondQubitIt); - gates.push_back( - {.op = nextGate, .qubitIds = {firstQubitId, secondQubitId}}); + if (nextGate.isControlled()) { + // controls of a gate should come first, but are last in the qubit order + gates.push_back( + {.op = nextGate, .qubitIds = {secondQubitId, firstQubitId}}); + *firstQubitIt = nextGate->getResult(1); + *secondQubitIt = nextGate->getResult(0); + } else { + gates.push_back( + {.op = nextGate, .qubitIds = {firstQubitId, secondQubitId}}); + *firstQubitIt = nextGate->getResult(0); + *secondQubitIt = nextGate->getResult(1); + } complexity += 2; return true; } @@ -297,6 +305,7 @@ struct GateDecompositionPattern final auto updateInQubits = [&inQubits](const TwoQubitGateSequence::Gate& gateDescription, auto&& newGate) { + // TODO: need to handle controls differently? auto results = newGate.getAllOutQubits(); if (gateDescription.qubitId.size() == 2) { inQubits[gateDescription.qubitId[0]] = results[0]; @@ -362,10 +371,11 @@ struct GateDecompositionPattern final if (gate.type == qc::X) { mlir::SmallVector inCtrlQubits; if (gate.qubitId.size() > 1) { - inCtrlQubits.push_back(inQubits[gate.qubitId[1]]); + // controls come first + inCtrlQubits.push_back(inQubits[gate.qubitId[0]]); } auto newGate = - createGate(rewriter, location, {inQubits[gate.qubitId[0]]}, + createGate(rewriter, location, {inQubits[gate.qubitId[1]]}, inCtrlQubits, gate.parameter); updateInQubits(gate, newGate); } else if (gate.type == qc::RX) { @@ -729,21 +739,21 @@ struct GateDecompositionPattern final } if (gate.qubitId.size() == 1) { if (gate.qubitId[0] == 0) { - return kroneckerProduct(getSingleQubitMatrix(gate), IDENTITY_GATE); + return kroneckerProduct(IDENTITY_GATE, getSingleQubitMatrix(gate)); } if (gate.qubitId[0] == 1) { - return kroneckerProduct(IDENTITY_GATE, getSingleQubitMatrix(gate)); + return kroneckerProduct(getSingleQubitMatrix(gate), IDENTITY_GATE); } throw std::logic_error{"Invalid qubit ID in getTwoQubitMatrix"}; } if (gate.qubitId.size() == 2) { if (gate.type == qc::X) { // controlled X (CX) - if (gate.qubitId == llvm::SmallVector{0, 1}) { + if (gate.qubitId == llvm::SmallVector{1, 0}) { return matrix4x4{ {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}}; } - if (gate.qubitId == llvm::SmallVector{1, 0}) { + if (gate.qubitId == llvm::SmallVector{0, 1}) { return matrix4x4{ {1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}, {0, 1, 0, 0}}; } @@ -1785,14 +1795,18 @@ struct GateDecompositionPattern final }; for (std::size_t i = 0; i < bestNbasis; ++i) { - addEulerDecomposition(2 * i, 0); - addEulerDecomposition((2 * i) + 1, 1); + addEulerDecomposition(2 * i, basisGate.qubitId[0]); + addEulerDecomposition((2 * i) + 1, basisGate.qubitId[1]); gates.gates.push_back(basisGate); } - addEulerDecomposition(2UL * bestNbasis, 0); - addEulerDecomposition((2UL * bestNbasis) + 1, 1); + addEulerDecomposition(2UL * bestNbasis, basisGate.qubitId[0]); + addEulerDecomposition((2UL * bestNbasis) + 1, basisGate.qubitId[1]); + + // large global phases can be generated by the decomposition, thus limit + // it to [-2*pi, +2*pi) + gates.globalPhase = std::fmod(gates.globalPhase, qc::TAU); return gates; } @@ -1852,7 +1866,6 @@ struct GateDecompositionPattern final for (auto&& gate : sequence.gates) { matrix4x4 gateMatrix = getTwoQubitMatrix(gate); - matrix = gateMatrix * matrix; } return matrix; From bb4b796813a1690969d52338779a620bf959c84f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 15 Nov 2025 21:56:35 +0100 Subject: [PATCH 065/237] fix global phase issues --- .../Transforms/GateDecompositionPattern.cpp | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 4379511e11..f12756f3e4 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -48,10 +48,6 @@ struct GateDecompositionPattern final std::cerr << gate.op->getName().stripDialect().str() << ", "; } std::cerr << '\n'; - static int a{}; - if (a++ > 0) { - return mlir::failure(); - } if (series.gates.size() < 3) { // too short @@ -215,13 +211,13 @@ struct GateDecompositionPattern final // controls of a gate should come first, but are last in the qubit order gates.push_back( {.op = nextGate, .qubitIds = {secondQubitId, firstQubitId}}); - *firstQubitIt = nextGate->getResult(1); - *secondQubitIt = nextGate->getResult(0); + *firstQubitIt = nextGate->getResult(1); + *secondQubitIt = nextGate->getResult(0); } else { gates.push_back( {.op = nextGate, .qubitIds = {firstQubitId, secondQubitId}}); - *firstQubitIt = nextGate->getResult(0); - *secondQubitIt = nextGate->getResult(1); + *firstQubitIt = nextGate->getResult(0); + *secondQubitIt = nextGate->getResult(1); } complexity += 2; return true; @@ -1529,6 +1525,22 @@ struct GateDecompositionPattern final } } specialized.globalPhase += std::arg(tr); + + helpers::print( + (helpers::kroneckerProduct(specialized.k1l, specialized.k1r) * + getCanonicalMatrix(specialized.a * -2.0, specialized.b * -2.0, + specialized.c * -2.0) * + helpers::kroneckerProduct(specialized.k2l, specialized.k2r) * + std::exp(IM * specialized.globalPhase)) + .eval(), + "SANITY CHECK (3)", true); + assert((helpers::kroneckerProduct(specialized.k1l, specialized.k1r) * + getCanonicalMatrix(specialized.a * -2.0, specialized.b * -2.0, + specialized.c * -2.0) * + helpers::kroneckerProduct(specialized.k2l, specialized.k2r) * + std::exp(IM * specialized.globalPhase)) + .isApprox(unitaryMatrix, 1e-8)); + return specialized; } }; @@ -1734,6 +1746,7 @@ struct GateDecompositionPattern final } return minIndex; }; + // number of basis gates that need to be inserted auto bestNbasis = numBasisUses.value_or(getDefaultNbasis()); auto chooseDecomposition = [&]() { if (bestNbasis == 0) { @@ -1789,11 +1802,12 @@ struct GateDecompositionPattern final gates.gates.push_back({.type = gate.type, .parameter = gate.parameter, .qubitId = {qubitId}}); - gates.globalPhase += eulerDecomp->globalPhase; } + gates.globalPhase += eulerDecomp->globalPhase; } }; + // TODO: check if this actually uses the correct qubitIds for (std::size_t i = 0; i < bestNbasis; ++i) { addEulerDecomposition(2 * i, basisGate.qubitId[0]); addEulerDecomposition((2 * i) + 1, basisGate.qubitId[1]); From 837363fd90880b4dc08821fe91640e049d33e5c3 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 16 Nov 2025 19:16:52 +0100 Subject: [PATCH 066/237] first working snapshot --- .../Transforms/GateDecompositionPattern.cpp | 139 +++++++++++------- 1 file changed, 86 insertions(+), 53 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index f12756f3e4..8992bf66ba 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -27,6 +27,7 @@ #include #include #include +#include namespace mqt::ir::opt { @@ -35,9 +36,46 @@ namespace mqt::ir::opt { */ struct GateDecompositionPattern final : mlir::OpInterfaceRewritePattern { + enum class EulerBasis : std::uint8_t { + U3 = 0, + U321 = 1, + U = 2, + PSX = 3, + U1X = 4, + RR = 5, + ZYZ = 6, + ZXZ = 7, + XZX = 8, + XYX = 9, + ZSXX = 10, + ZSX = 11, + }; + + struct QubitGateSequence { + struct Gate { + qc::OpType type{qc::I}; + llvm::SmallVector parameter; + llvm::SmallVector qubitId = {0}; + }; + std::vector gates; + std::size_t complexity() { + std::size_t c{}; + for (auto&& gate : gates) { + c += getComplexity(gate.type, gate.qubitId.size()); + } + return c; + } + fp globalPhase{}; + }; + using OneQubitGateSequence = QubitGateSequence; + using TwoQubitGateSequence = QubitGateSequence; - explicit GateDecompositionPattern(mlir::MLIRContext* context) - : OpInterfaceRewritePattern(context) {} + explicit GateDecompositionPattern(mlir::MLIRContext* context, + QubitGateSequence::Gate basisGate, + EulerBasis eulerBasis) + : OpInterfaceRewritePattern(context), + decomposerBasisGate{std::move(basisGate)}, + decomposerEulerBasis{eulerBasis} {} mlir::LogicalResult matchAndRewrite(UnitaryInterface op, @@ -62,7 +100,8 @@ struct GateDecompositionPattern final matrix4x4 unitaryMatrix = series.getUnitaryMatrix(); helpers::print(unitaryMatrix, "UNITARY MATRIX", true); - auto decomposer = TwoQubitBasisDecomposer::newInner(); + auto decomposer = TwoQubitBasisDecomposer::newInner( + decomposerBasisGate, DEFAULT_FIDELITY, decomposerEulerBasis); auto sequence = decomposer.twoQubitDecompose( unitaryMatrix, DEFAULT_FIDELITY, true, std::nullopt); if (!sequence) { @@ -82,6 +121,16 @@ struct GateDecompositionPattern final return mlir::success(); } +protected: + [[nodiscard]] static std::size_t getComplexity(qc::OpType /*type*/, + std::size_t numOfQubits) { + if (numOfQubits > 1) { + constexpr std::size_t multiQubitFactor = 10; + return (numOfQubits - 1) * multiQubitFactor; + } + return 1; + } + struct TwoQubitSeries { std::size_t complexity{0}; std::array inQubits; @@ -128,6 +177,7 @@ struct GateDecompositionPattern final } } } + // TODO: need to search and apply for global phase? return result; } @@ -152,17 +202,22 @@ struct GateDecompositionPattern final inQubits = {in[0], mlir::Value{}}; outQubits = {out[0], mlir::Value{}}; gates.push_back({.op = initialOperation, .qubitIds = {0}}); - complexity += 1; } else if (helpers::isTwoQubitOperation(initialOperation)) { inQubits = {in[0], in[1]}; outQubits = {out[0], out[1]}; - gates.push_back({.op = initialOperation, .qubitIds = {0, 1}}); - complexity += 2; + if (initialOperation.isControlled()) { + gates.push_back({.op = initialOperation, .qubitIds = {1, 0}}); + } else { + gates.push_back({.op = initialOperation, .qubitIds = {0, 1}}); + } } + complexity += + getComplexity(helpers::getQcType(initialOperation), in.size()); } /** * @return true if series continues, otherwise false + * (will always return true) */ bool appendSingleQubitGate(UnitaryInterface nextGate) { auto operand = nextGate.getAllInQubits()[0]; @@ -175,7 +230,7 @@ struct GateDecompositionPattern final *it = nextGate->getResult(0); gates.push_back({.op = nextGate, .qubitIds = {qubitId}}); - complexity += 1; + complexity += getComplexity(helpers::getQcType(nextGate), 1); return true; } @@ -206,20 +261,18 @@ struct GateDecompositionPattern final std::size_t firstQubitId = std::distance(outQubits.begin(), firstQubitIt); std::size_t secondQubitId = std::distance(outQubits.begin(), secondQubitIt); + *firstQubitIt = nextGate->getResult(0); + *secondQubitIt = nextGate->getResult(1); if (nextGate.isControlled()) { // controls of a gate should come first, but are last in the qubit order gates.push_back( {.op = nextGate, .qubitIds = {secondQubitId, firstQubitId}}); - *firstQubitIt = nextGate->getResult(1); - *secondQubitIt = nextGate->getResult(0); } else { gates.push_back( {.op = nextGate, .qubitIds = {firstQubitId, secondQubitId}}); - *firstQubitIt = nextGate->getResult(0); - *secondQubitIt = nextGate->getResult(1); } - complexity += 2; + complexity += getComplexity(helpers::getQcType(nextGate), 2); return true; } }; @@ -264,25 +317,6 @@ struct GateDecompositionPattern final inQubits, ctrlQubits, mlir::ValueRange{}); } - struct QubitGateSequence { - struct Gate { - qc::OpType type{qc::I}; - llvm::SmallVector parameter; - llvm::SmallVector qubitId = {0}; - }; - std::vector gates; - std::size_t complexity() { - std::size_t c{}; - for (auto&& gate : gates) { - c += gate.qubitId.size(); - } - return c; - } - fp globalPhase{}; - }; - using OneQubitGateSequence = QubitGateSequence; - using TwoQubitGateSequence = QubitGateSequence; - static void applySeries(mlir::PatternRewriter& rewriter, TwoQubitSeries& series, const TwoQubitGateSequence& sequence) { @@ -433,21 +467,6 @@ struct GateDecompositionPattern final OutOf, }; - enum class EulerBasis : std::uint8_t { - U3 = 0, - U321 = 1, - U = 2, - PSX = 3, - U1X = 4, - RR = 5, - ZYZ = 6, - ZXZ = 7, - XZX = 8, - XYX = 9, - ZSXX = 10, - ZSX = 11, - }; - static constexpr auto SQRT2 = std::numbers::sqrt2_v; static constexpr auto FRAC1_SQRT2 = static_cast(0.707106781186547524400844362104849039); @@ -1578,7 +1597,8 @@ struct GateDecompositionPattern final newInner(const OneQubitGateSequence::Gate& basisGate = {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}, - fp basisFidelity = 1.0, EulerBasis eulerBasis = EulerBasis::ZYZ) { + fp basisFidelity = DEFAULT_FIDELITY, + EulerBasis eulerBasis = EulerBasis::ZYZ) { auto relativeEq = [](auto&& lhs, auto&& rhs, auto&& epsilon, auto&& maxRelative) { // Handle same infinities @@ -1809,14 +1829,14 @@ struct GateDecompositionPattern final // TODO: check if this actually uses the correct qubitIds for (std::size_t i = 0; i < bestNbasis; ++i) { - addEulerDecomposition(2 * i, basisGate.qubitId[0]); - addEulerDecomposition((2 * i) + 1, basisGate.qubitId[1]); + addEulerDecomposition(2 * i, 0); + addEulerDecomposition((2 * i) + 1, 1); gates.gates.push_back(basisGate); } - addEulerDecomposition(2UL * bestNbasis, basisGate.qubitId[0]); - addEulerDecomposition((2UL * bestNbasis) + 1, basisGate.qubitId[1]); + addEulerDecomposition(2UL * bestNbasis, 0); + addEulerDecomposition((2UL * bestNbasis) + 1, 1); // large global phases can be generated by the decomposition, thus limit // it to [-2*pi, +2*pi) @@ -2012,7 +2032,11 @@ struct GateDecompositionPattern final } return {gates, globalPhase}; } - }; // namespace mqt::ir::opt + }; + +private: + QubitGateSequence::Gate decomposerBasisGate; + EulerBasis decomposerEulerBasis; }; const matrix2x2 GateDecompositionPattern::IDENTITY_GATE = matrix2x2::Identity(); @@ -2028,7 +2052,16 @@ const matrix2x2 GateDecompositionPattern::IPX{{C_ZERO, IM}, {IM, C_ZERO}}; * decomposition. */ void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns) { - patterns.add(patterns.getContext()); + patterns.add( + patterns.getContext(), + GateDecompositionPattern::QubitGateSequence::Gate{ + .type = qc::X, .parameter = {}, .qubitId = {1, 0}}, + GateDecompositionPattern::EulerBasis::ZYZ); + patterns.add( + patterns.getContext(), + GateDecompositionPattern::QubitGateSequence::Gate{ + .type = qc::X, .parameter = {}, .qubitId = {0, 1}}, + GateDecompositionPattern::EulerBasis::ZYZ); } } // namespace mqt::ir::opt From 2582c66dd5d895e442e2e2f320f97d56b6cc40b9 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 16 Nov 2025 21:05:48 +0100 Subject: [PATCH 067/237] very minor cleanup --- .../Transforms/GateDecompositionPattern.cpp | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 8992bf66ba..6a06349448 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -58,7 +58,7 @@ struct GateDecompositionPattern final llvm::SmallVector qubitId = {0}; }; std::vector gates; - std::size_t complexity() { + [[nodiscard]] std::size_t complexity() const { std::size_t c{}; for (auto&& gate : gates) { c += getComplexity(gate.type, gate.qubitId.size()); @@ -691,7 +691,7 @@ struct GateDecompositionPattern final } static std::array paramsXyxInner(const matrix2x2& matrix) { - auto matZyz = matrix2x2{ + matrix2x2 matZyz{ {static_cast(0.5) * (matrix(0, 0) + matrix(0, 1) + matrix(1, 0) + matrix(1, 1)), static_cast(0.5) * @@ -1625,11 +1625,11 @@ struct GateDecompositionPattern final } return absDiff <= absLhs * maxRelative; }; - const auto k12RArr = matrix2x2{ + const matrix2x2 k12RArr{ {qfp(0., FRAC1_SQRT2), qfp(FRAC1_SQRT2, 0.)}, {qfp(-FRAC1_SQRT2, 0.), qfp(0., -FRAC1_SQRT2)}, }; - const auto k12LArr = matrix2x2{ + const matrix2x2 k12LArr{ {qfp(0.5, 0.5), qfp(0.5, 0.5)}, {qfp(-0.5, 0.5), qfp(0.5, -0.5)}, }; @@ -1644,42 +1644,41 @@ struct GateDecompositionPattern final // expand as Ui = Ki1.Ubasis.Ki2 auto b = basisDecomposer.b; auto temp = qfp(0.5, -0.5); - auto k11l = matrix2x2{ + matrix2x2 k11l{ {temp * (M_IM * std::exp(qfp(0., -b))), temp * std::exp(qfp(0., -b))}, - {temp * (M_IM * std::exp(qfp(0., b))), - temp * -(std::exp(qfp(0., b)))}}; - auto k11r = matrix2x2{{FRAC1_SQRT2 * std::exp((IM * qfp(0., -b))), - FRAC1_SQRT2 * -std::exp(qfp(0., -b))}, - {FRAC1_SQRT2 * std::exp(qfp(0., b)), - FRAC1_SQRT2 * (M_IM * std::exp(qfp(0., b)))}}; - auto k32lK21l = matrix2x2{{FRAC1_SQRT2 * std::cos(qfp(1., (2. * b))), - FRAC1_SQRT2 * (IM * std::sin((2. * b)))}, - {FRAC1_SQRT2 * (IM * std::sin(2. * b)), - FRAC1_SQRT2 * qfp(1., -std::cos(2. * b))}}; + {temp * (M_IM * std::exp(qfp(0., b))), temp * -std::exp(qfp(0., b))}}; + matrix2x2 k11r{{FRAC1_SQRT2 * std::exp((IM * qfp(0., -b))), + FRAC1_SQRT2 * -std::exp(qfp(0., -b))}, + {FRAC1_SQRT2 * std::exp(qfp(0., b)), + FRAC1_SQRT2 * (M_IM * std::exp(qfp(0., b)))}}; + matrix2x2 k32lK21l{{FRAC1_SQRT2 * std::cos(qfp(1., (2. * b))), + FRAC1_SQRT2 * (IM * std::sin((2. * b)))}, + {FRAC1_SQRT2 * (IM * std::sin(2. * b)), + FRAC1_SQRT2 * qfp(1., -std::cos(2. * b))}}; temp = qfp(0.5, 0.5); - auto k21r = matrix2x2{ + matrix2x2 k21r{ {temp * (M_IM * std::exp(qfp(0., -2. * b))), temp * std::exp(qfp(0., -2. * b))}, {temp * (IM * std::exp(qfp(0., 2. * b))), temp * std::exp(qfp(0., 2. * b))}, }; - const auto k22LArr = matrix2x2{ + const matrix2x2 k22LArr{ {qfp(FRAC1_SQRT2, 0.), qfp(-FRAC1_SQRT2, 0.)}, {qfp(FRAC1_SQRT2, 0.), qfp(FRAC1_SQRT2, 0.)}, }; - const auto k22RArr = matrix2x2{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; - auto k31l = matrix2x2{ + const matrix2x2 k22RArr{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; + matrix2x2 k31l{ {FRAC1_SQRT2 * std::exp(qfp(0., -b)), FRAC1_SQRT2 * std::exp(qfp(0., -b))}, {FRAC1_SQRT2 * -std::exp(qfp(0., b)), FRAC1_SQRT2 * std::exp(qfp(0., b))}, }; - auto k31r = matrix2x2{ + matrix2x2 k31r{ {IM * std::exp(qfp(0., b)), C_ZERO}, {C_ZERO, M_IM * std::exp(qfp(0., -b))}, }; temp = qfp(0.5, 0.5); - auto k32r = matrix2x2{ + matrix2x2 k32r{ {temp * std::exp(qfp(0., b)), temp * -std::exp(qfp(0., -b))}, {temp * (M_IM * std::exp(qfp(0., b))), temp * (M_IM * std::exp(qfp(0., -b)))}, @@ -1743,7 +1742,7 @@ struct GateDecompositionPattern final std::optional twoQubitDecompose(const matrix4x4& unitaryMatrix, std::optional basisFidelity, bool approximate, - std::optional numBasisUses) { + std::optional numBasisGateUses) { auto getBasisFidelity = [&]() { if (approximate) { return basisFidelity.value_or(this->basisFidelity); @@ -1767,7 +1766,7 @@ struct GateDecompositionPattern final return minIndex; }; // number of basis gates that need to be inserted - auto bestNbasis = numBasisUses.value_or(getDefaultNbasis()); + auto bestNbasis = numBasisGateUses.value_or(getDefaultNbasis()); auto chooseDecomposition = [&]() { if (bestNbasis == 0) { return decomp0Inner(targetDecomposed); From 8ec690b93a7ac184dceabd8684880d7c7801ae9a Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 16 Nov 2025 23:26:16 +0100 Subject: [PATCH 068/237] add more sanity checks for unitary --- .../Transforms/GateDecompositionPattern.cpp | 35 +++++++++++-------- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 19 ++++++---- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 6a06349448..f6d2f5c00b 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -66,6 +66,18 @@ struct GateDecompositionPattern final return c; } fp globalPhase{}; + + [[nodiscard]] matrix4x4 getUnitaryMatrix() const { + matrix4x4 unitaryMatrix = + helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); + for (auto&& gate : gates) { + auto gateMatrix = getTwoQubitMatrix(gate); + unitaryMatrix = gateMatrix * unitaryMatrix; + } + unitaryMatrix *= std::exp(IM * globalPhase); + assert(helpers::isUnitaryMatrix(unitaryMatrix)); + return unitaryMatrix; + } }; using OneQubitGateSequence = QubitGateSequence; using TwoQubitGateSequence = QubitGateSequence; @@ -191,6 +203,7 @@ struct GateDecompositionPattern final .qubitId = gate.qubitIds}); unitaryMatrix = gateMatrix * unitaryMatrix; } + assert(helpers::isUnitaryMatrix(unitaryMatrix)); return unitaryMatrix; } @@ -440,6 +453,7 @@ struct GateDecompositionPattern final helpers::print(series.getUnitaryMatrix(), "ORIGINAL UNITARY", true); helpers::print((unitaryMatrix * std::exp(IM * sequence.globalPhase)).eval(), "RESULT UNITARY MATRIX", true); + assert((unitaryMatrix * std::exp(IM * sequence.globalPhase)).isApprox(series.getUnitaryMatrix(), 1e-9)); rewriter.replaceAllUsesWith(series.outQubits, inQubits); for (auto&& gate : llvm::reverse(series.gates)) { @@ -1797,6 +1811,7 @@ struct GateDecompositionPattern final llvm::SmallVector, 8> eulerDecompositions; for (auto&& decomp : decomposition) { + assert(helpers::isUnitaryMatrix(decomp)); auto eulerDecomp = unitaryToGateSequenceInner( decomp, target1qBasisList, 0, {}, true, std::nullopt); eulerDecompositions.push_back(eulerDecomp); @@ -1891,19 +1906,6 @@ struct GateDecompositionPattern final }; } - static matrix4x4 computeUnitary(const TwoQubitGateSequence& sequence, - fp globalPhase) { - auto phase = std::exp(std::complex{0, globalPhase}); - matrix4x4 matrix{}; - matrix.diagonal().setConstant(phase); - - for (auto&& gate : sequence.gates) { - matrix4x4 gateMatrix = getTwoQubitMatrix(gate); - matrix = gateMatrix * matrix; - } - return matrix; - } - [[nodiscard]] std::array traces(TwoQubitWeylDecomposition target) const { return { @@ -1955,13 +1957,18 @@ struct GateDecompositionPattern final // independence bool simplify, std::optional atol) { auto calculateError = [](const OneQubitGateSequence& sequence) -> fp { - return static_cast(sequence.gates.size()); + return static_cast(sequence.complexity()); }; auto minError = std::numeric_limits::max(); OneQubitGateSequence bestCircuit; for (auto targetBasis : targetBasisList) { auto circuit = generateCircuit(targetBasis, unitaryMat, simplify, atol); + helpers::print(circuit.getUnitaryMatrix(), "SANITY CHECK (4.1)", true); + helpers::print(helpers::kroneckerProduct(IDENTITY_GATE, unitaryMat), + "SANITY CHECK (4.2)", true); + assert(circuit.getUnitaryMatrix().isApprox( + helpers::kroneckerProduct(IDENTITY_GATE, unitaryMat), 1e-9)); auto error = calculateError(circuit); if (error < minError) { bestCircuit = circuit; diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index 3849f66cc2..b12c436cc1 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -10,15 +10,15 @@ #pragma once -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" #include "ir/operations/OpType.hpp" +#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" #include #include #include #include // TODO: unstable -#include // TODO: remove -#include // TODO: remove +#include // TODO: remove +#include // TODO: remove #include #include @@ -55,7 +55,7 @@ void print(Eigen::Matrix matrix, const std::string& s = "", bool force = false) { if (!force) { return; -} + } if (!s.empty()) { llvm::errs() << "=== " << s << " ===\n"; } @@ -67,7 +67,7 @@ template void print(T matrix, const std::string& s = "", bool force = false) { if (!force) { return; -} + } if (!s.empty()) { llvm::errs() << "=== " << s << " ===\n"; } @@ -213,7 +213,7 @@ inline Eigen::Matrix4 kroneckerProduct(const Eigen::Matrix2& lhs, return result; } -template +template inline auto selfAdjointEvd(Eigen::Matrix a) { Eigen::SelfAdjointEigenSolver s; std::cerr << "=EigIN==\n" << a << "\n========\n" << '\n'; @@ -225,4 +225,11 @@ inline auto selfAdjointEvd(Eigen::Matrix a) { return std::make_pair(vecs, vals); } +template +[[nodiscard]] static bool +isUnitaryMatrix(const Eigen::Matrix& matrix) { + return (matrix.transpose().conjugate() * matrix) + .isApprox(Eigen::Matrix::Identity(), 1e-9); +} + } // namespace mqt::ir::opt::helpers From e26a374283a20ac905b2f98fcd0660b69dcb4d07 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 16 Nov 2025 23:29:23 +0100 Subject: [PATCH 069/237] find and fix minor formula mistake --- .../Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index f6d2f5c00b..e498342d92 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -1661,12 +1661,12 @@ struct GateDecompositionPattern final matrix2x2 k11l{ {temp * (M_IM * std::exp(qfp(0., -b))), temp * std::exp(qfp(0., -b))}, {temp * (M_IM * std::exp(qfp(0., b))), temp * -std::exp(qfp(0., b))}}; - matrix2x2 k11r{{FRAC1_SQRT2 * std::exp((IM * qfp(0., -b))), + matrix2x2 k11r{{FRAC1_SQRT2 * (IM * std::exp(qfp(0., -b))), FRAC1_SQRT2 * -std::exp(qfp(0., -b))}, {FRAC1_SQRT2 * std::exp(qfp(0., b)), FRAC1_SQRT2 * (M_IM * std::exp(qfp(0., b)))}}; - matrix2x2 k32lK21l{{FRAC1_SQRT2 * std::cos(qfp(1., (2. * b))), - FRAC1_SQRT2 * (IM * std::sin((2. * b)))}, + matrix2x2 k32lK21l{{FRAC1_SQRT2 * qfp(1., std::cos(2. * b)), + FRAC1_SQRT2 * (IM * std::sin(2. * b))}, {FRAC1_SQRT2 * (IM * std::sin(2. * b)), FRAC1_SQRT2 * qfp(1., -std::cos(2. * b))}}; temp = qfp(0.5, 0.5); From bc1475ebcc6b455fe7a70a97836226bb3384033d Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 16 Nov 2025 23:30:31 +0100 Subject: [PATCH 070/237] test update --- .../MQTOpt/Transforms/gate-decomposition.mlir | 95 +++++++++++++++++-- 1 file changed, 87 insertions(+), 8 deletions(-) diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index 402c791057..154e75709b 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -226,8 +226,8 @@ module { } module { - // CHECK-LABEL: func.func @testComplexSeries - func.func @testComplexSeries() { + // CHECK-LABEL: func.func @testTwoBasisGateDecomposition + func.func @testTwoBasisGateDecomposition() { // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit @@ -246,8 +246,9 @@ module { %q1_0 = mqtopt.allocQubit %q2_0 = mqtopt.allocQubit - %q0_1 = mqtopt.h() %q0_0: !mqtopt.Qubit - %q1_1, %q0_2 = mqtopt.x() %q1_0 ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_x, %q1_x = mqtopt.i() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_1 = mqtopt.h() %q0_x: !mqtopt.Qubit + %q1_1, %q0_2 = mqtopt.x() %q1_x ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit %q0_3, %q1_2 = mqtopt.rzz(%cst0) %q0_2, %q1_1: !mqtopt.Qubit, !mqtopt.Qubit %q1_3 = mqtopt.ry(%cst1) %q1_2: !mqtopt.Qubit %q0_4 = mqtopt.rx(%cst1) %q0_3: !mqtopt.Qubit @@ -293,17 +294,17 @@ module { %q2_0 = mqtopt.allocQubit %q0_1 = mqtopt.i() %q0_0: !mqtopt.Qubit - %q1_1 = mqtopt.i() %q1_0: !mqtopt.Qubit %q0_2 = mqtopt.i() %q0_1: !mqtopt.Qubit - %q0_3, %q1_2 = mqtopt.x() %q0_2 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_3 = mqtopt.i() %q1_2: !mqtopt.Qubit + %q0_3, %q1_1 = mqtopt.x() %q0_2 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit %q0_4 = mqtopt.i() %q0_3: !mqtopt.Qubit - %q1_4 = mqtopt.i() %q1_3: !mqtopt.Qubit %q0_5 = mqtopt.i() %q0_4: !mqtopt.Qubit %q0_6 = mqtopt.i() %q0_5: !mqtopt.Qubit %q0_7 = mqtopt.i() %q0_6: !mqtopt.Qubit %q0_8 = mqtopt.i() %q0_7: !mqtopt.Qubit %q0_9 = mqtopt.i() %q0_8: !mqtopt.Qubit + %q1_2 = mqtopt.i() %q1_1: !mqtopt.Qubit + %q1_3 = mqtopt.i() %q1_2: !mqtopt.Qubit + %q1_4 = mqtopt.i() %q1_3: !mqtopt.Qubit %q1_5 = mqtopt.i() %q1_4: !mqtopt.Qubit %q1_6 = mqtopt.i() %q1_5: !mqtopt.Qubit %q1_7 = mqtopt.i() %q1_6: !mqtopt.Qubit @@ -317,3 +318,81 @@ module { return } } + +// ----- +// This test checks if two single-qubit series (connected by an identity) remain separate without the insertion of a basis gate. + +module { + // CHECK-LABEL: func.func @testSingleQubitSeries + func.func @testSingleQubitSeries() { + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit + + // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] + + // CHECK: mqtopt.deallocQubit %[[Q0_1]] + // CHECK: mqtopt.deallocQubit %[[Q1_1]] + + %cst0 = arith.constant 2.5 : f64 + %cst1 = arith.constant 1.2 : f64 + + %q0_0 = mqtopt.allocQubit + %q1_0 = mqtopt.allocQubit + + %q0_1, %q1_1 = mqtopt.i() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_2 = mqtopt.ry(%cst0) %q0_1: !mqtopt.Qubit + %q1_2 = mqtopt.rx(%cst1) %q1_1: !mqtopt.Qubit + %q1_3 = mqtopt.rx(%cst1) %q1_2: !mqtopt.Qubit + + mqtopt.deallocQubit %q0_2 + mqtopt.deallocQubit %q1_3 + + return + } +} + +// ----- +// This test checks if two single-qubit series (connected by an identity) remain separate without the insertion of a basis gate. + +module { + // CHECK-LABEL: func.func @testThreeBasisGateDecomposition + func.func @testThreeBasisGateDecomposition() { + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit + + // CHECK-NOT: mqtopt.gphase(%[[ANY:.*]]) + + // CHECK: mqtopt.deallocQubit %[[Q0_5]] + // CHECK: mqtopt.deallocQubit %[[Q1_4]] + // CHECK: mqtopt.deallocQubit %[[Q2_1]] + + %cst0 = arith.constant 2.5 : f64 + %cst1 = arith.constant 1.2 : f64 + %cst2 = arith.constant 0.5 : f64 + + %q0_0 = mqtopt.allocQubit + %q1_0 = mqtopt.allocQubit + %q2_0 = mqtopt.allocQubit + + %q0_x, %q1_x = mqtopt.i() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_1 = mqtopt.h() %q0_x: !mqtopt.Qubit + %q1_1, %q0_2 = mqtopt.x() %q1_x ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_3, %q1_2 = mqtopt.rzz(%cst0) %q0_2, %q1_1: !mqtopt.Qubit, !mqtopt.Qubit + %q1_3 = mqtopt.ry(%cst1) %q1_2: !mqtopt.Qubit + %q0_4 = mqtopt.rx(%cst1) %q0_3: !mqtopt.Qubit + %q0_5, %q1_4 = mqtopt.x() %q0_4 ctrl %q1_3: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_6 = mqtopt.rz(%cst2) %q0_5: !mqtopt.Qubit + %q0_7, %q1_5 = mqtopt.rxx(%cst0) %q0_6, %q1_4: !mqtopt.Qubit, !mqtopt.Qubit + %q0_8, %q1_6 = mqtopt.ryy(%cst2) %q0_7, %q1_5: !mqtopt.Qubit, !mqtopt.Qubit + // make series longer to enforce decomposition + %q0_9, %q1_7 = mqtopt.i() %q0_8 ctrl %q1_6: !mqtopt.Qubit ctrl !mqtopt.Qubit + + mqtopt.deallocQubit %q0_9 + mqtopt.deallocQubit %q1_7 + mqtopt.deallocQubit %q2_0 + + return + } +} + From 4048bf6ed2415e7b7042ef48d028df3d93bdf708 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 17 Nov 2025 19:48:20 +0100 Subject: [PATCH 071/237] make calculations exact without approximation --- .../Transforms/GateDecompositionPattern.cpp | 52 +++++++++++-------- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 3 +- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index e498342d92..90c8863ce4 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -113,9 +113,9 @@ struct GateDecompositionPattern final helpers::print(unitaryMatrix, "UNITARY MATRIX", true); auto decomposer = TwoQubitBasisDecomposer::newInner( - decomposerBasisGate, DEFAULT_FIDELITY, decomposerEulerBasis); + decomposerBasisGate, 1.0, decomposerEulerBasis); auto sequence = decomposer.twoQubitDecompose( - unitaryMatrix, DEFAULT_FIDELITY, true, std::nullopt); + unitaryMatrix, DEFAULT_FIDELITY, false, std::nullopt); if (!sequence) { llvm::errs() << "NO SEQUENCE GENERATED!\n"; return mlir::failure(); @@ -134,6 +134,7 @@ struct GateDecompositionPattern final } protected: + static constexpr fp SANITY_CHECK_PRECISION = 1e-12; [[nodiscard]] static std::size_t getComplexity(qc::OpType /*type*/, std::size_t numOfQubits) { if (numOfQubits > 1) { @@ -377,15 +378,15 @@ struct GateDecompositionPattern final } else { auto parameter = helpers::getParameters(gate.op)[0]; std::cerr << std::format( - "{}({:.5}*pi) q[{}] {};", name, parameter / qc::PI, - gate.qubitIds[0], + "{}({}*pi) q[{}] {};", name, parameter / qc::PI, gate.qubitIds[0], (gate.qubitIds.size() > 1 ? (", q[" + std::to_string(gate.qubitIds[1]) + "]") : std::string{})); } } std::cerr << '\n'; - std::cerr << "GATE SEQUENCE!: \n"; + std::cerr << "GATE SEQUENCE!: gphase(" << sequence.globalPhase / qc::PI + << "*pi); \n"; matrix4x4 unitaryMatrix = helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); for (auto&& gate : sequence.gates) { @@ -404,7 +405,7 @@ struct GateDecompositionPattern final : std::string{})); } else { std::cerr << std::format( - "{}({:.5}*pi) q[{}] {};", qc::toString(gate.type), + "{}({}*pi) q[{}] {};", qc::toString(gate.type), gate.parameter[0] / qc::PI, gate.qubitId[0], (gate.qubitId.size() > 1 ? (", q[" + std::to_string(gate.qubitId[1]) + "]") @@ -453,7 +454,8 @@ struct GateDecompositionPattern final helpers::print(series.getUnitaryMatrix(), "ORIGINAL UNITARY", true); helpers::print((unitaryMatrix * std::exp(IM * sequence.globalPhase)).eval(), "RESULT UNITARY MATRIX", true); - assert((unitaryMatrix * std::exp(IM * sequence.globalPhase)).isApprox(series.getUnitaryMatrix(), 1e-9)); + assert((unitaryMatrix * std::exp(IM * sequence.globalPhase)) + .isApprox(series.getUnitaryMatrix(), SANITY_CHECK_PRECISION)); rewriter.replaceAllUsesWith(series.outQubits, inQubits); for (auto&& gate : llvm::reverse(series.gates)) { @@ -908,9 +910,10 @@ struct GateDecompositionPattern final "TwoQubitWeylDecomposition: failed to diagonalize M2."}; } // check that p is in SO(4) - assert((p.transpose() * p).isIdentity()); + assert((p.transpose() * p).isIdentity(SANITY_CHECK_PRECISION)); // make sure determinant of sqrt(eigenvalues) is 1.0 - assert(std::abs(matrix4x4{d.asDiagonal()}.determinant() - 1.0) < 1e-13); + assert(std::abs(matrix4x4{d.asDiagonal()}.determinant() - 1.0) < + SANITY_CHECK_PRECISION); diagonal4x4 q = d.cwiseSqrt(); auto det_q = q.prod(); @@ -1071,7 +1074,7 @@ struct GateDecompositionPattern final // temp += matrix4x4::Constant(0.0); helpers::print(temp, "TEMP", true); helpers::print(p, "P", true); - assert(std::abs(p.determinant() - 1.0) < 1e-13); + assert(std::abs(p.determinant() - 1.0) < SANITY_CHECK_PRECISION); // https://threeplusone.com/pubs/on_gates.pdf // uP = V, m2 = V^T*V, temp = D, p = Q1 matrix4x4 k1 = uP * p * temp; @@ -1094,12 +1097,14 @@ struct GateDecompositionPattern final assert((k1 * magicBasisTransform(temp.conjugate(), MagicBasisTransform::Into) * k2) - .isApprox(u, 1e-8)); + .isApprox(u, SANITY_CHECK_PRECISION)); auto [K1l, K1r, phase_l] = decomposeTwoQubitProductGate(k1); auto [K2l, K2r, phase_r] = decomposeTwoQubitProductGate(k2); - assert(helpers::kroneckerProduct(K1l, K1r).isApprox(k1, 1e-9)); - assert(helpers::kroneckerProduct(K2l, K2r).isApprox(k2, 1e-9)); + assert(helpers::kroneckerProduct(K1l, K1r).isApprox( + k1, SANITY_CHECK_PRECISION)); + assert(helpers::kroneckerProduct(K2l, K2r).isApprox( + k2, SANITY_CHECK_PRECISION)); globalPhase += phase_l + phase_r; // Flip into Weyl chamber @@ -1201,7 +1206,7 @@ struct GateDecompositionPattern final assert((helpers::kroneckerProduct(K1l, K1r) * getCanonicalMatrix(a * -2.0, b * -2.0, c * -2.0) * helpers::kroneckerProduct(K2l, K2r) * std::exp(IM * globalPhase)) - .isApprox(unitaryMatrix, 1e-8)); + .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); auto isClose = [&](fp ap, fp bp, fp cp) -> bool { auto da = a - ap; auto db = b - bp; @@ -1572,7 +1577,7 @@ struct GateDecompositionPattern final specialized.c * -2.0) * helpers::kroneckerProduct(specialized.k2l, specialized.k2r) * std::exp(IM * specialized.globalPhase)) - .isApprox(unitaryMatrix, 1e-8)); + .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); return specialized; } @@ -1649,7 +1654,7 @@ struct GateDecompositionPattern final }; auto basisDecomposer = TwoQubitWeylDecomposition::newInner( - getTwoQubitMatrix(basisGate), DEFAULT_FIDELITY, std::nullopt); + getTwoQubitMatrix(basisGate), basisFidelity, std::nullopt); auto superControlled = relativeEq(basisDecomposer.a, qc::PI_4, 1e-13, 1e-09) && relativeEq(basisDecomposer.c, 0.0, 1e-13, 1e-09); @@ -1765,7 +1770,7 @@ struct GateDecompositionPattern final }; fp actualBasisFidelity = getBasisFidelity(); auto targetDecomposed = TwoQubitWeylDecomposition::newInner( - unitaryMatrix, DEFAULT_FIDELITY, std::nullopt); + unitaryMatrix, actualBasisFidelity, std::nullopt); auto traces = this->traces(targetDecomposed); auto getDefaultNbasis = [&]() { auto minValue = std::numeric_limits::min(); @@ -1813,7 +1818,7 @@ struct GateDecompositionPattern final for (auto&& decomp : decomposition) { assert(helpers::isUnitaryMatrix(decomp)); auto eulerDecomp = unitaryToGateSequenceInner( - decomp, target1qBasisList, 0, {}, true, std::nullopt); + decomp, target1qBasisList, 0, {}, true, 1.0 - actualBasisFidelity); eulerDecompositions.push_back(eulerDecomp); } TwoQubitGateSequence gates{.globalPhase = targetDecomposed.globalPhase}; @@ -1968,7 +1973,8 @@ struct GateDecompositionPattern final helpers::print(helpers::kroneckerProduct(IDENTITY_GATE, unitaryMat), "SANITY CHECK (4.2)", true); assert(circuit.getUnitaryMatrix().isApprox( - helpers::kroneckerProduct(IDENTITY_GATE, unitaryMat), 1e-9)); + helpers::kroneckerProduct(IDENTITY_GATE, unitaryMat), + SANITY_CHECK_PRECISION)); auto error = calculateError(circuit); if (error < minError) { bestCircuit = circuit; @@ -2004,7 +2010,7 @@ struct GateDecompositionPattern final fp globalPhase = phase - ((phi + lambda) / 2.); std::vector gates; - if (std::abs(theta) < angleZeroEpsilon) { + if (std::abs(theta) <= angleZeroEpsilon) { lambda += phi; lambda = mod2pi(lambda); if (std::abs(lambda) > angleZeroEpsilon) { @@ -2014,13 +2020,13 @@ struct GateDecompositionPattern final return {gates, globalPhase}; } - if (std::abs(theta - qc::PI) < angleZeroEpsilon) { + if (std::abs(theta - qc::PI) <= angleZeroEpsilon) { globalPhase += phi; lambda -= phi; phi = 0.0; } - if (std::abs(mod2pi(lambda + qc::PI)) < angleZeroEpsilon || - std::abs(mod2pi(phi + qc::PI)) < angleZeroEpsilon) { + if (std::abs(mod2pi(lambda + qc::PI)) <= angleZeroEpsilon || + std::abs(mod2pi(phi + qc::PI)) <= angleZeroEpsilon) { lambda += qc::PI; theta = -theta; phi += qc::PI; diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index b12c436cc1..5180a7ea9c 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -228,8 +228,7 @@ inline auto selfAdjointEvd(Eigen::Matrix a) { template [[nodiscard]] static bool isUnitaryMatrix(const Eigen::Matrix& matrix) { - return (matrix.transpose().conjugate() * matrix) - .isApprox(Eigen::Matrix::Identity(), 1e-9); + return (matrix.transpose().conjugate() * matrix).isIdentity(); } } // namespace mqt::ir::opt::helpers From cff036c6161f0ea77e9a6bf8b0f52e2fc8ab1707 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 17 Nov 2025 19:48:55 +0100 Subject: [PATCH 072/237] respect gphase of circuit and fix series collection bug --- .../MQTOpt/Transforms/GateDecompositionPattern.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 90c8863ce4..34d6fa639b 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -148,6 +148,7 @@ struct GateDecompositionPattern final std::size_t complexity{0}; std::array inQubits; std::array outQubits; + fp globalPhase{}; struct Gate { UnitaryInterface op; @@ -187,10 +188,10 @@ struct GateDecompositionPattern final if (auto user = getUser(result.outQubits[i], &helpers::isTwoQubitOperation)) { foundGate = result.appendTwoQubitGate(*user); + break; // go back to single-qubit collection } } } - // TODO: need to search and apply for global phase? return result; } @@ -204,6 +205,8 @@ struct GateDecompositionPattern final .qubitId = gate.qubitIds}); unitaryMatrix = gateMatrix * unitaryMatrix; } + unitaryMatrix *= std::exp(IM * globalPhase); + assert(helpers::isUnitaryMatrix(unitaryMatrix)); return unitaryMatrix; } @@ -227,6 +230,11 @@ struct GateDecompositionPattern final } complexity += getComplexity(helpers::getQcType(initialOperation), in.size()); + + // TODO: necessary when using non-global phase gates? + for (auto&& globalPhaseOp : initialOperation->getBlock()->getOps()) { + globalPhase += helpers::getParameters(globalPhaseOp)[0]; + } } /** @@ -1858,7 +1866,8 @@ struct GateDecompositionPattern final addEulerDecomposition((2UL * bestNbasis) + 1, 1); // large global phases can be generated by the decomposition, thus limit - // it to [-2*pi, +2*pi) + // it to (-2*pi, +2*pi); TODO: can be removed, should be done by something + // like constant folding gates.globalPhase = std::fmod(gates.globalPhase, qc::TAU); return gates; From 21701e86ef444dee334056f45f399db2d1abc5a0 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 17 Nov 2025 23:41:51 +0100 Subject: [PATCH 073/237] change global phase to local phase --- .../Transforms/GateDecompositionPattern.cpp | 58 +++++++++++++------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 34d6fa639b..e3dc20d365 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -63,9 +63,17 @@ struct GateDecompositionPattern final for (auto&& gate : gates) { c += getComplexity(gate.type, gate.qubitId.size()); } + if (hasGlobalPhase()) { + // need to add two phase gates if a global phase needs to be applied + c += 2 * getComplexity(qc::P, 1); + } return c; } + fp globalPhase{}; + [[nodiscard]] bool hasGlobalPhase() const { + return std::abs(globalPhase) > std::numeric_limits::epsilon(); + } [[nodiscard]] matrix4x4 getUnitaryMatrix() const { matrix4x4 unitaryMatrix = @@ -120,7 +128,9 @@ struct GateDecompositionPattern final llvm::errs() << "NO SEQUENCE GENERATED!\n"; return mlir::failure(); } - if (sequence->complexity() >= series.complexity) { + // only accept new sequence if it shortens existing series by more than two + // gates; this prevents an oscillation with phase gates + if (sequence->complexity() >= series.complexity + 2) { // TODO: add more sophisticated metric to determine complexity of // series/sequence llvm::errs() << "SEQUENCE LONGER THAN INPUT (" << sequence->gates.size() @@ -232,7 +242,8 @@ struct GateDecompositionPattern final getComplexity(helpers::getQcType(initialOperation), in.size()); // TODO: necessary when using non-global phase gates? - for (auto&& globalPhaseOp : initialOperation->getBlock()->getOps()) { + for (auto&& globalPhaseOp : + initialOperation->getBlock()->getOps()) { globalPhase += helpers::getParameters(globalPhaseOp)[0]; } } @@ -346,29 +357,31 @@ struct GateDecompositionPattern final auto location = lastSeriesOp->getLoc(); rewriter.setInsertionPointAfter(lastSeriesOp); - if (sequence.globalPhase != 0.0) { - // TODO: use POp instead and apply negative globalPhase after insertion of - // new gates to "undo" the phase shift - createOneParameterGate(rewriter, location, sequence.globalPhase, - {}); - } - auto inQubits = series.inQubits; auto updateInQubits = - [&inQubits](const TwoQubitGateSequence::Gate& gateDescription, + [&inQubits](const llvm::SmallVector& qubitIds, auto&& newGate) { // TODO: need to handle controls differently? auto results = newGate.getAllOutQubits(); - if (gateDescription.qubitId.size() == 2) { - inQubits[gateDescription.qubitId[0]] = results[0]; - inQubits[gateDescription.qubitId[1]] = results[1]; - } else if (gateDescription.qubitId.size() == 1) { - inQubits[gateDescription.qubitId[0]] = results[0]; + if (qubitIds.size() == 2) { + inQubits[qubitIds[0]] = results[0]; + inQubits[qubitIds[1]] = results[1]; + } else if (qubitIds.size() == 1) { + inQubits[qubitIds[0]] = results[0]; } else { throw std::logic_error{"Invalid number of qubit IDs!"}; } }; + if (sequence.hasGlobalPhase()) { + auto newGate = createOneParameterGate( + rewriter, location, sequence.globalPhase, {inQubits[0]}); + updateInQubits({0}, newGate); + newGate = createOneParameterGate( + rewriter, location, sequence.globalPhase, {inQubits[1]}); + updateInQubits({1}, newGate); + } + std::cerr << "SERIES: "; for (auto&& gate : series.gates) { auto name = gate.op->getName().stripDialect().str(); @@ -429,7 +442,7 @@ struct GateDecompositionPattern final auto newGate = createGate(rewriter, location, {inQubits[gate.qubitId[1]]}, inCtrlQubits, gate.parameter); - updateInQubits(gate, newGate); + updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RX) { mlir::SmallVector qubits; for (auto&& x : gate.qubitId) { @@ -437,7 +450,7 @@ struct GateDecompositionPattern final } auto newGate = createGate(rewriter, location, qubits, {}, gate.parameter); - updateInQubits(gate, newGate); + updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RY) { mlir::SmallVector qubits; for (auto&& x : gate.qubitId) { @@ -445,7 +458,7 @@ struct GateDecompositionPattern final } auto newGate = createGate(rewriter, location, qubits, {}, gate.parameter); - updateInQubits(gate, newGate); + updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RZ) { mlir::SmallVector qubits; for (auto&& x : gate.qubitId) { @@ -453,7 +466,7 @@ struct GateDecompositionPattern final } auto newGate = createGate(rewriter, location, qubits, {}, gate.parameter); - updateInQubits(gate, newGate); + updateInQubits(gate.qubitId, newGate); } else { throw std::runtime_error{"Unknown gate type!"}; } @@ -683,6 +696,10 @@ struct GateDecompositionPattern final {C_ZERO, C_ZERO, C_ZERO, {cosTheta, -sinTheta}}}; } + static matrix2x2 pMatrix(const fp lambda) { + return matrix2x2{{1, 0}, {0, {std::cos(lambda), std::sin(lambda)}}}; + } + static std::array anglesFromUnitary(const matrix2x2& matrix, EulerBasis basis) { if (basis == EulerBasis::XYX) { @@ -760,6 +777,9 @@ struct GateDecompositionPattern final if (gate.type == qc::I) { return IDENTITY_GATE; } + if (gate.type == qc::P) { + return pMatrix(gate.parameter[0]); + } if (gate.type == qc::H) { static constexpr fp SQRT2_2 = static_cast( 0.707106781186547524400844362104849039284835937688474036588L); From 85c1f86c8dd1ddc2103b837d593366e972e3ea59 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 18 Nov 2025 14:33:58 +0100 Subject: [PATCH 074/237] revert to global phase, fix controlled gate qubit order --- .../Transforms/GateDecompositionPattern.cpp | 38 ++++++------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index e3dc20d365..592443e0b6 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -130,7 +130,7 @@ struct GateDecompositionPattern final } // only accept new sequence if it shortens existing series by more than two // gates; this prevents an oscillation with phase gates - if (sequence->complexity() >= series.complexity + 2) { + if (sequence->complexity() + 2 >= series.complexity) { // TODO: add more sophisticated metric to determine complexity of // series/sequence llvm::errs() << "SEQUENCE LONGER THAN INPUT (" << sequence->gates.size() @@ -232,11 +232,7 @@ struct GateDecompositionPattern final } else if (helpers::isTwoQubitOperation(initialOperation)) { inQubits = {in[0], in[1]}; outQubits = {out[0], out[1]}; - if (initialOperation.isControlled()) { - gates.push_back({.op = initialOperation, .qubitIds = {1, 0}}); - } else { - gates.push_back({.op = initialOperation, .qubitIds = {0, 1}}); - } + gates.push_back({.op = initialOperation, .qubitIds = {0, 1}}); } complexity += getComplexity(helpers::getQcType(initialOperation), in.size()); @@ -297,14 +293,8 @@ struct GateDecompositionPattern final *firstQubitIt = nextGate->getResult(0); *secondQubitIt = nextGate->getResult(1); - if (nextGate.isControlled()) { - // controls of a gate should come first, but are last in the qubit order - gates.push_back( - {.op = nextGate, .qubitIds = {secondQubitId, firstQubitId}}); - } else { - gates.push_back( - {.op = nextGate, .qubitIds = {firstQubitId, secondQubitId}}); - } + gates.push_back( + {.op = nextGate, .qubitIds = {firstQubitId, secondQubitId}}); complexity += getComplexity(helpers::getQcType(nextGate), 2); return true; } @@ -374,12 +364,8 @@ struct GateDecompositionPattern final }; if (sequence.hasGlobalPhase()) { - auto newGate = createOneParameterGate( - rewriter, location, sequence.globalPhase, {inQubits[0]}); - updateInQubits({0}, newGate); - newGate = createOneParameterGate( - rewriter, location, sequence.globalPhase, {inQubits[1]}); - updateInQubits({1}, newGate); + createOneParameterGate(rewriter, location, sequence.globalPhase, + {}); } std::cerr << "SERIES: "; @@ -416,8 +402,8 @@ struct GateDecompositionPattern final if (gate.type == qc::X && gate.qubitId.size() == 2) { // controls come first - std::cerr << std::format("cx() q[{}], q[{}];", gate.qubitId[0], - gate.qubitId[1]); + std::cerr << std::format("cx() q[{}], q[{}];", gate.qubitId[1], + gate.qubitId[0]); } else if (gate.parameter.empty()) { std::cerr << std::format( "{}() q[{}] {};", qc::toString(gate.type), gate.qubitId[0], @@ -437,10 +423,10 @@ struct GateDecompositionPattern final mlir::SmallVector inCtrlQubits; if (gate.qubitId.size() > 1) { // controls come first - inCtrlQubits.push_back(inQubits[gate.qubitId[0]]); + inCtrlQubits.push_back(inQubits[gate.qubitId[1]]); } auto newGate = - createGate(rewriter, location, {inQubits[gate.qubitId[1]]}, + createGate(rewriter, location, {inQubits[gate.qubitId[0]]}, inCtrlQubits, gate.parameter); updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RX) { @@ -808,11 +794,11 @@ struct GateDecompositionPattern final if (gate.qubitId.size() == 2) { if (gate.type == qc::X) { // controlled X (CX) - if (gate.qubitId == llvm::SmallVector{1, 0}) { + if (gate.qubitId == llvm::SmallVector{0, 1}) { return matrix4x4{ {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}}; } - if (gate.qubitId == llvm::SmallVector{0, 1}) { + if (gate.qubitId == llvm::SmallVector{1, 0}) { return matrix4x4{ {1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}, {0, 1, 0, 0}}; } From 9c0ecb40f5f0c50d6e153630c608e6442643d62f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 18 Nov 2025 14:34:20 +0100 Subject: [PATCH 075/237] fix first couple of tests --- .../MQTOpt/Transforms/gate-decomposition.mlir | 201 ++++++++++++------ 1 file changed, 137 insertions(+), 64 deletions(-) diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index 154e75709b..c9292b9145 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -9,11 +9,11 @@ // RUN: quantum-opt %s -split-input-file --gate-decomposition | FileCheck %s // ----- -// This test checks if an even number of CNOT gates will be cancelled out. +// This test checks if a series equal to the identity will be cancelled out. module { - // CHECK-LABEL: func.func @testEvenNegationSeries - func.func @testEvenNegationSeries() { + // CHECK-LABEL: func.func @testIdentitySeries + func.func @testIdentitySeries() { // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit @@ -93,20 +93,57 @@ module { } } +// ----- +// This test checks if an odd number of CNOT gates with ctrl/target swapped will be reduced to a single CNOT. + +module { + // CHECK-LABEL: func.func @testCNotOtherDirection + func.func @testCNotOtherDirection() { + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit + + // CHECK-NOT: mqtopt.i(%[[ANY:.*]]) + // CHECK: %[[Q0_0]], %[[Q1_0]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] + + // CHECK: mqtopt.deallocQubit %[[Q0_1]] + // CHECK: mqtopt.deallocQubit %[[Q1_1]] + + // ensure no other operations are inserted + // CHECK-NOT: mqtopt.[[ANY:.*]] + + %q0_0 = mqtopt.allocQubit + %q1_0 = mqtopt.allocQubit + + %q1_1, %q0_1 = mqtopt.i() %q1_0 ctrl %q0_0: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q1_2, %q0_2 = mqtopt.x() %q1_1 ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q1_3, %q0_3 = mqtopt.i() %q1_2 ctrl %q0_2: !mqtopt.Qubit ctrl !mqtopt.Qubit + + mqtopt.deallocQubit %q0_3 + mqtopt.deallocQubit %q1_3 + + return + } +} + // ----- // This test checks if a two-qubit series containing a single-qubit gate is decomposed correctly. module { // CHECK-LABEL: func.func @testSeriesOneQubitOpInbetween func.func @testSeriesOneQubitOpInbetween() { + // CHECK: %[[C2:.*]] = arith.constant -1.5707 + // CHECK: %[[C1:.*]] = arith.constant 3.14159 + // CHECK: %[[C0:.*]] = arith.constant 1.57079 + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit // CHECK: mqtopt.gphase(%[[C0:.*]]) - // CHECK: %[[Q0_1:.*]] = mqtopt.ry(%[[C1:.*]]) %[[Q0_0]] - // CHECK: %[[Q0_2:.*]] = mqtopt.rz(%[[C1]]) %[[Q0_1]] + // CHECK: %[[Q0_1:.*]] = mqtopt.rz(%[[C0]]) %[[Q0_0]] + // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C1:.*]]) %[[Q0_1]] + // CHECK: %[[Q0_3:.*]] = mqtopt.rz(%[[C2:.*]]) %[[Q0_2]] - // CHECK: mqtopt.deallocQubit %[[Q0_2]] + // CHECK: mqtopt.deallocQubit %[[Q0_3]] // CHECK: mqtopt.deallocQubit %[[Q1_0]] %q0_0 = mqtopt.allocQubit @@ -129,14 +166,19 @@ module { module { // CHECK-LABEL: func.func @testSeriesStartingOneQubitOp func.func @testSeriesStartingOneQubitOp() { + // CHECK: %[[C2:.*]] = arith.constant -1.5707 + // CHECK: %[[C1:.*]] = arith.constant 3.14159 + // CHECK: %[[C0:.*]] = arith.constant 1.57079 + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit // CHECK: mqtopt.gphase(%[[C0:.*]]) - // CHECK: %[[Q0_1:.*]] = mqtopt.ry(%[[C1:.*]]) %[[Q0_0]] - // CHECK: %[[Q0_2:.*]] = mqtopt.rz(%[[C1]]) %[[Q0_1]] + // CHECK: %[[Q0_1:.*]] = mqtopt.rz(%[[C0]]) %[[Q0_0]] + // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C1:.*]]) %[[Q0_1]] + // CHECK: %[[Q0_3:.*]] = mqtopt.rz(%[[C2:.*]]) %[[Q0_2]] - // CHECK: mqtopt.deallocQubit %[[Q0_2]] + // CHECK: mqtopt.deallocQubit %[[Q0_3]] // CHECK: mqtopt.deallocQubit %[[Q1_0]] %q0_0 = mqtopt.allocQubit @@ -159,14 +201,19 @@ module { module { // CHECK-LABEL: func.func @testSeriesEndingOneQubitOp func.func @testSeriesEndingOneQubitOp() { + // CHECK: %[[C2:.*]] = arith.constant -1.5707 + // CHECK: %[[C1:.*]] = arith.constant 3.14159 + // CHECK: %[[C0:.*]] = arith.constant 1.57079 + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit // CHECK: mqtopt.gphase(%[[C0:.*]]) - // CHECK: %[[Q0_1:.*]] = mqtopt.ry(%[[C1:.*]]) %[[Q0_0]] - // CHECK: %[[Q0_2:.*]] = mqtopt.rz(%[[C1]]) %[[Q0_1]] + // CHECK: %[[Q0_1:.*]] = mqtopt.rz(%[[C0]]) %[[Q0_0]] + // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C1:.*]]) %[[Q0_1]] + // CHECK: %[[Q0_3:.*]] = mqtopt.rz(%[[C2:.*]]) %[[Q0_2]] - // CHECK: mqtopt.deallocQubit %[[Q0_2]] + // CHECK: mqtopt.deallocQubit %[[Q0_3]] // CHECK: mqtopt.deallocQubit %[[Q1_0]] %q0_0 = mqtopt.allocQubit @@ -228,15 +275,46 @@ module { module { // CHECK-LABEL: func.func @testTwoBasisGateDecomposition func.func @testTwoBasisGateDecomposition() { + // CHECK: %[[C0:.*]] = arith.constant -2.356194490 + // CHECK: %[[C1:.*]] = arith.constant -1.070796326 + // CHECK: %[[C2:.*]] = arith.constant -0.370796326 + // CHECK: %[[C3:.*]] = arith.constant -2.526112944 + // CHECK: %[[C4:.*]] = arith.constant 1.0471975511 + // CHECK: %[[C5:.*]] = arith.constant 0.6154797086 + // CHECK: %[[C6:.*]] = arith.constant -3.141592653 + // CHECK: %[[C7:.*]] = arith.constant 2.7707963267 + // CHECK: %[[C8:.*]] = arith.constant -1.570796326 + // CHECK: %[[C9:.*]] = arith.constant 0.7853981633 + // CHECK: %[[C10:.*]] = arith.constant 2.5000 + // CHECK: %[[C11:.*]] = arith.constant 1.570796326 + // CHECK: %[[C12:.*]] = arith.constant -1.57079632 + // CHECK: %[[C13:.*]] = arith.constant 8.881784197 + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit - // CHECK-NOT: mqtopt.gphase(%[[ANY:.*]]) - - // CHECK: mqtopt.deallocQubit %[[Q0_5]] - // CHECK: mqtopt.deallocQubit %[[Q1_4]] - // CHECK: mqtopt.deallocQubit %[[Q2_1]] + // CHECK: mqtopt.gphase(%[[C13]]) + // CHECK: %[[Q0_1:.*]] = mqtopt.rz(%[[C12]]) %[[Q0_0]] + // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C11]]) %[[Q0_1]] + // CHECK: %[[Q0_3:.*]] = mqtopt.rz(%[[C11]]) %[[Q0_2]] + // CHECK: %[[Q1_1:.*]] = mqtopt.rz(%[[C10]]) %[[Q1_0]] + // CHECK: %[[Q1_2:.*]] = mqtopt.ry(%[[C9]]) %[[Q1_1]] + // CHECK: %[[Q1_3:.*]] = mqtopt.rz(%[[C8]]) %[[Q1_2]] + // CHECK: %[[Q1_4:.*]], %[[Q0_4:.*]] = mqtopt.x() %[[Q1_3]] ctrl %[[Q0_3]] + // CHECK: %[[Q0_5:.*]] = mqtopt.rz(%[[C11]]) %[[Q0_4]] + // CHECK: %[[Q0_6:.*]] = mqtopt.ry(%[[C7]]) %[[Q0_5]] + // CHECK: %[[Q0_7:.*]] = mqtopt.rz(%[[C6]]) %[[Q0_6]] + // CHECK: %[[Q1_5:.*]] = mqtopt.rz(%[[C5]]) %[[Q1_4]] + // CHECK: %[[Q1_6:.*]] = mqtopt.ry(%[[C4]]) %[[Q1_5]] + // CHECK: %[[Q1_7:.*]] = mqtopt.rz(%[[C3]]) %[[Q1_6]] + // CHECK: %[[Q1_8:.*]], %[[Q0_8:.*]] = mqtopt.x() %[[Q1_7]] ctrl %[[Q0_7]] + // CHECK: %[[Q0_9:.*]] = mqtopt.ry(%[[C2]]) %[[Q0_8]] + // CHECK: %[[Q0_10:.*]] = mqtopt.rz(%[[C1]]) %[[Q0_9]] + // CHECK: %[[Q1_9:.*]] = mqtopt.rz(%[[C12]]) %[[Q1_8]] + // CHECK: %[[Q1_10:.*]] = mqtopt.ry(%[[C0]]) %[[Q1_9]] + + // CHECK: mqtopt.deallocQubit %[[Q0_10]] + // CHECK: mqtopt.deallocQubit %[[Q1_10]] %cst0 = arith.constant 2.5 : f64 %cst1 = arith.constant 1.2 : f64 @@ -244,7 +322,6 @@ module { %q0_0 = mqtopt.allocQubit %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit %q0_x, %q1_x = mqtopt.i() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit %q0_1 = mqtopt.h() %q0_x: !mqtopt.Qubit @@ -266,54 +343,38 @@ module { mqtopt.deallocQubit %q0_9 mqtopt.deallocQubit %q1_9 - mqtopt.deallocQubit %q2_0 return } } +// ----- +// This test checks if two single-qubit series (connected by an identity) remain separate without the insertion of a basis gate. + module { - // CHECK-LABEL: func.func @testCNotOtherDirection - func.func @testCNotOtherDirection() { + // CHECK-LABEL: func.func @testSingleQubitSeries + func.func @testSingleQubitSeries() { // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit - // CHECK-NOT: mqtopt.gphase(%[[ANY:.*]]) + // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] - // CHECK: mqtopt.deallocQubit %[[Q0_5]] - // CHECK: mqtopt.deallocQubit %[[Q1_4]] - // CHECK: mqtopt.deallocQubit %[[Q2_1]] + // CHECK: mqtopt.deallocQubit %[[Q0_1]] + // CHECK: mqtopt.deallocQubit %[[Q1_1]] %cst0 = arith.constant 2.5 : f64 %cst1 = arith.constant 1.2 : f64 - %cst2 = arith.constant 0.5 : f64 %q0_0 = mqtopt.allocQubit %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - %q0_1 = mqtopt.i() %q0_0: !mqtopt.Qubit - %q0_2 = mqtopt.i() %q0_1: !mqtopt.Qubit - %q0_3, %q1_1 = mqtopt.x() %q0_2 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_4 = mqtopt.i() %q0_3: !mqtopt.Qubit - %q0_5 = mqtopt.i() %q0_4: !mqtopt.Qubit - %q0_6 = mqtopt.i() %q0_5: !mqtopt.Qubit - %q0_7 = mqtopt.i() %q0_6: !mqtopt.Qubit - %q0_8 = mqtopt.i() %q0_7: !mqtopt.Qubit - %q0_9 = mqtopt.i() %q0_8: !mqtopt.Qubit - %q1_2 = mqtopt.i() %q1_1: !mqtopt.Qubit - %q1_3 = mqtopt.i() %q1_2: !mqtopt.Qubit - %q1_4 = mqtopt.i() %q1_3: !mqtopt.Qubit - %q1_5 = mqtopt.i() %q1_4: !mqtopt.Qubit - %q1_6 = mqtopt.i() %q1_5: !mqtopt.Qubit - %q1_7 = mqtopt.i() %q1_6: !mqtopt.Qubit - %q1_8 = mqtopt.i() %q1_7: !mqtopt.Qubit - %q1_9 = mqtopt.i() %q1_8: !mqtopt.Qubit + %q0_1, %q1_1 = mqtopt.i() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_2 = mqtopt.ry(%cst0) %q0_1: !mqtopt.Qubit + %q1_2 = mqtopt.rx(%cst1) %q1_1: !mqtopt.Qubit + %q1_3 = mqtopt.rx(%cst1) %q1_2: !mqtopt.Qubit - mqtopt.deallocQubit %q0_9 - mqtopt.deallocQubit %q1_9 - mqtopt.deallocQubit %q2_0 + mqtopt.deallocQubit %q0_2 + mqtopt.deallocQubit %q1_3 return } @@ -323,40 +384,53 @@ module { // This test checks if two single-qubit series (connected by an identity) remain separate without the insertion of a basis gate. module { - // CHECK-LABEL: func.func @testSingleQubitSeries - func.func @testSingleQubitSeries() { + // CHECK-LABEL: func.func @testThreeBasisGateDecomposition + func.func @testThreeBasisGateDecomposition() { // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] + // CHECK-NOT: mqtopt.gphase(%[[ANY:.*]]) - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q1_1]] + // CHECK: mqtopt.deallocQubit %[[Q0_5]] + // CHECK: mqtopt.deallocQubit %[[Q1_4]] + // CHECK: mqtopt.deallocQubit %[[Q2_1]] %cst0 = arith.constant 2.5 : f64 %cst1 = arith.constant 1.2 : f64 + %cst2 = arith.constant 0.5 : f64 %q0_0 = mqtopt.allocQubit %q1_0 = mqtopt.allocQubit + %q2_0 = mqtopt.allocQubit - %q0_1, %q1_1 = mqtopt.i() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_2 = mqtopt.ry(%cst0) %q0_1: !mqtopt.Qubit - %q1_2 = mqtopt.rx(%cst1) %q1_1: !mqtopt.Qubit - %q1_3 = mqtopt.rx(%cst1) %q1_2: !mqtopt.Qubit + %q0_x, %q1_x = mqtopt.i() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_1 = mqtopt.h() %q0_x: !mqtopt.Qubit + %q1_1, %q0_2 = mqtopt.x() %q1_x ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_3, %q1_2 = mqtopt.rzz(%cst0) %q0_2, %q1_1: !mqtopt.Qubit, !mqtopt.Qubit + %q1_3 = mqtopt.ry(%cst1) %q1_2: !mqtopt.Qubit + %q0_4 = mqtopt.rx(%cst1) %q0_3: !mqtopt.Qubit + %q0_5, %q1_4 = mqtopt.x() %q0_4 ctrl %q1_3: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_6 = mqtopt.rz(%cst2) %q0_5: !mqtopt.Qubit + %q0_7, %q1_5 = mqtopt.rxx(%cst0) %q0_6, %q1_4: !mqtopt.Qubit, !mqtopt.Qubit + %q0_8, %q1_6 = mqtopt.ryy(%cst2) %q0_7, %q1_5: !mqtopt.Qubit, !mqtopt.Qubit + // make series longer to enforce decomposition + %q0_9, %q1_7 = mqtopt.i() %q0_8 ctrl %q1_6: !mqtopt.Qubit ctrl !mqtopt.Qubit - mqtopt.deallocQubit %q0_2 - mqtopt.deallocQubit %q1_3 + mqtopt.deallocQubit %q0_9 + mqtopt.deallocQubit %q1_7 + mqtopt.deallocQubit %q2_0 return } } // ----- -// This test checks if two single-qubit series (connected by an identity) remain separate without the insertion of a basis gate. +// This test checks if the repeated application of the decomposition works by "interrupting" the first series and then having a second one. module { - // CHECK-LABEL: func.func @testThreeBasisGateDecomposition - func.func @testThreeBasisGateDecomposition() { + // CHECK-LABEL: func.func @testRepeatedDecomposition + func.func @testRepeatedDecomposition() { // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit @@ -395,4 +469,3 @@ module { return } } - From 00986dd425b9208bd563278c51710e6c71161111 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 18 Nov 2025 23:27:11 +0100 Subject: [PATCH 076/237] start working on basis set and remove dead code --- .../Transforms/GateDecompositionPattern.cpp | 142 +++--------------- 1 file changed, 19 insertions(+), 123 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 592443e0b6..752fa54616 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -90,12 +90,13 @@ struct GateDecompositionPattern final using OneQubitGateSequence = QubitGateSequence; using TwoQubitGateSequence = QubitGateSequence; - explicit GateDecompositionPattern(mlir::MLIRContext* context, - QubitGateSequence::Gate basisGate, - EulerBasis eulerBasis) + explicit GateDecompositionPattern( + mlir::MLIRContext* context, + llvm::SmallVector basisGate, + llvm::SmallVector eulerBasis) : OpInterfaceRewritePattern(context), decomposerBasisGate{std::move(basisGate)}, - decomposerEulerBasis{eulerBasis} {} + decomposerEulerBasis{std::move(eulerBasis)} {} mlir::LogicalResult matchAndRewrite(UnitaryInterface op, @@ -121,7 +122,7 @@ struct GateDecompositionPattern final helpers::print(unitaryMatrix, "UNITARY MATRIX", true); auto decomposer = TwoQubitBasisDecomposer::newInner( - decomposerBasisGate, 1.0, decomposerEulerBasis); + decomposerBasisGate[0], 1.0, decomposerEulerBasis[0]); auto sequence = decomposer.twoQubitDecompose( unitaryMatrix, DEFAULT_FIDELITY, false, std::nullopt); if (!sequence) { @@ -373,8 +374,8 @@ struct GateDecompositionPattern final auto name = gate.op->getName().stripDialect().str(); if (name == "x" && gate.qubitIds.size() == 2) { // controls come first - std::cerr << std::format("cx() q[{}], q[{}];", gate.qubitIds[0], - gate.qubitIds[1]); + std::cerr << std::format("cx() q[{}], q[{}];", gate.qubitIds[1], + gate.qubitIds[0]); } else if (name == "i") { } else if (gate.op.getParams().empty()) { std::cerr << std::format( @@ -929,112 +930,10 @@ struct GateDecompositionPattern final assert(std::abs(matrix4x4{d.asDiagonal()}.determinant() - 1.0) < SANITY_CHECK_PRECISION); - diagonal4x4 q = d.cwiseSqrt(); - auto det_q = q.prod(); - if (det_q.real() < 0.0) { - q[0] *= -1.0; - } - // constrain to weyl - auto constrain_to_weyl = [](diagonal4x4 q) { - auto in_weyl = [](fp tx, fp ty, fp tz) { - return (0.5 >= tx && tx >= ty && ty >= tz && tz >= 0) || - (0.5 >= (1 - tx) && (1 - tx) >= ty && ty >= tz && tz > 0); - }; - auto lambdas_to_coords = [](diagonal4x4 lambdas) { - // [2, eq.11], but using [1]s coordinates. - constexpr fp TOLERANCE = 1e-13; - - auto [l1, l2, _, l4] = - std::array{lambdas[0], lambdas[1], lambdas[2], lambdas[3]}; - auto c1 = std::real(IM * std::log(l1 * l2)); - auto c2 = std::real(IM * std::log(l2 * l4)); - auto c3 = std::real(IM * std::log(l1 * l4)); - Eigen::Vector coords{c1, c2, c3}; - coords /= qc::PI; - - // if coords[i] == 1, then coords[i] = -1, else coords[i] = coords[i] - coords = (coords - decltype(coords)::Ones()) - .cwiseAbs() - .cwiseLess(TOLERANCE) - .select(-decltype(coords)::Ones(), coords) - .eval(); - if (coords.cwiseLess(0.0).all()) { - coords += decltype(coords)::Ones(); - } - - // If we're close to the boundary, floating point errors can conspire - // to make it seem that we're never on the inside - // Fix: If near boundary, reset to boundary - - // Left - if (std::abs(coords[0] - coords[1]) < TOLERANCE) { - coords[1] = coords[0]; - } - - // Front - if (std::abs(coords[1] - coords[2]) < TOLERANCE) { - coords[2] = coords[1]; - } - - // Right - if (std::abs(coords[0] - coords[1] - 1.0 / 2.0) < TOLERANCE) { - coords[1] = coords[0] - 1.0 / 2.0; - } - - // Base - coords = - (coords.array() < 0).select(decltype(coords)::Zero(), coords); - - return coords; - }; - - auto permutation = std::array{0, 1, 2, 3}; - while (true) { - for (auto signs : - std::array, 4>{{{1, 1, 1, 1}, - {1, 1, -1, -1}, - {-1, 1, -1, 1}, - {1, -1, -1, 1}}}) { - decltype(q) signed_lambdas = q.cwiseProduct(signs); - // reorder according to permutation - decltype(signed_lambdas) lambdas_perm; - for (std::size_t i = 0; i < permutation.size(); ++i) { - lambdas_perm[i] = signed_lambdas[permutation[i]]; - } - - auto coords = lambdas_to_coords(lambdas_perm); - - if (in_weyl(coords[0], coords[1], coords[2])) { - return std::make_tuple(coords, permutation, signs); - } - } - if (!std::ranges::next_permutation(permutation).found) { - throw std::runtime_error{ - "Unable to find permutation to calculate Weyl coordinates!"}; - } - } - }; - - auto [cs, permutation, signs] = constrain_to_weyl(q); - cs *= -qc::PI_2; - matrix4x4 p2 = p; - - q = q.cwiseProduct(signs); - auto origQ = q; - matrix4x4 origP = signs.asDiagonal() * p.transpose(); - assert(permutation.size() == q.size()); - assert(permutation.size() == p.cols()); - for (std::size_t i = 0; i < permutation.size(); ++i) { - q[i] = origQ[permutation[i]]; - p2.col(i) = origP.row(permutation[i]); - } - // p.transposeInPlace(); - matrix4x4 temp = q.asDiagonal(); - temp = temp.conjugate(); - // see // https://github.com/mpham26uchicago/laughing-umbrella/blob/main/background/Full%20Two%20Qubit%20KAK%20Implementation.ipynb, // Step 7 + Eigen::Vector cs; rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; helpers::print(dReal, "D_REAL", true); dReal(3) = -dReal(0) - dReal(1) - dReal(2); @@ -1080,7 +979,7 @@ struct GateDecompositionPattern final p.col(lastColumnIndex) *= -1.0; } - temp = dReal.asDiagonal(); + matrix4x4 temp = dReal.asDiagonal(); temp *= IM; temp = temp.exp(); @@ -2062,8 +1961,8 @@ struct GateDecompositionPattern final }; private: - QubitGateSequence::Gate decomposerBasisGate; - EulerBasis decomposerEulerBasis; + llvm::SmallVector decomposerBasisGate; + llvm::SmallVector decomposerEulerBasis; }; const matrix2x2 GateDecompositionPattern::IDENTITY_GATE = matrix2x2::Identity(); @@ -2079,16 +1978,13 @@ const matrix2x2 GateDecompositionPattern::IPX{{C_ZERO, IM}, {IM, C_ZERO}}; * decomposition. */ void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns) { - patterns.add( - patterns.getContext(), - GateDecompositionPattern::QubitGateSequence::Gate{ - .type = qc::X, .parameter = {}, .qubitId = {1, 0}}, - GateDecompositionPattern::EulerBasis::ZYZ); - patterns.add( - patterns.getContext(), - GateDecompositionPattern::QubitGateSequence::Gate{ - .type = qc::X, .parameter = {}, .qubitId = {0, 1}}, - GateDecompositionPattern::EulerBasis::ZYZ); + llvm::SmallVector basisGates; + llvm::SmallVector eulerBases; + basisGates.push_back({.type = qc::X, .parameter = {}, .qubitId = {0, 1}}); + basisGates.push_back({.type = qc::X, .parameter = {}, .qubitId = {1, 0}}); + eulerBases.push_back(GateDecompositionPattern::EulerBasis::ZYZ); + patterns.add(patterns.getContext(), basisGates, + eulerBases); } } // namespace mqt::ir::opt From bcaf7986f96771c964713c4c6a2f4e7a02824dc8 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 19 Nov 2025 00:21:57 +0100 Subject: [PATCH 077/237] decompose for all bases, choose best --- .../Transforms/GateDecompositionPattern.cpp | 111 ++++++++++-------- 1 file changed, 64 insertions(+), 47 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 752fa54616..f1345877d7 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -96,7 +96,12 @@ struct GateDecompositionPattern final llvm::SmallVector eulerBasis) : OpInterfaceRewritePattern(context), decomposerBasisGate{std::move(basisGate)}, - decomposerEulerBasis{std::move(eulerBasis)} {} + decomposerEulerBases{std::move(eulerBasis)} { + for (auto&& basisGate : decomposerBasisGate) { + basisDecomposers.push_back( + TwoQubitBasisDecomposer::newInner(basisGate, DEFAULT_FIDELITY)); + } + } mlir::LogicalResult matchAndRewrite(UnitaryInterface op, @@ -121,25 +126,38 @@ struct GateDecompositionPattern final matrix4x4 unitaryMatrix = series.getUnitaryMatrix(); helpers::print(unitaryMatrix, "UNITARY MATRIX", true); - auto decomposer = TwoQubitBasisDecomposer::newInner( - decomposerBasisGate[0], 1.0, decomposerEulerBasis[0]); - auto sequence = decomposer.twoQubitDecompose( - unitaryMatrix, DEFAULT_FIDELITY, false, std::nullopt); - if (!sequence) { + auto targetDecomposition = TwoQubitWeylDecomposition::newInner( + unitaryMatrix, DEFAULT_FIDELITY, std::nullopt); + + std::optional bestSequence; + for (const auto& decomposer : basisDecomposers) { + auto sequence = decomposer.twoQubitDecompose( + targetDecomposition, decomposerEulerBases, DEFAULT_FIDELITY, false, + std::nullopt); + if (sequence) { + if (!bestSequence || + sequence->complexity() < bestSequence->complexity()) { + bestSequence = sequence; + } + } + } + if (!bestSequence) { llvm::errs() << "NO SEQUENCE GENERATED!\n"; return mlir::failure(); } // only accept new sequence if it shortens existing series by more than two // gates; this prevents an oscillation with phase gates - if (sequence->complexity() + 2 >= series.complexity) { + if (bestSequence->complexity() + 2 >= series.complexity) { // TODO: add more sophisticated metric to determine complexity of // series/sequence - llvm::errs() << "SEQUENCE LONGER THAN INPUT (" << sequence->gates.size() + llvm::errs() << "SEQUENCE LONGER THAN INPUT (" + << bestSequence->gates.size() << "; " + << bestSequence->complexity() << " vs " << series.complexity << ")\n"; return mlir::failure(); } - applySeries(rewriter, series, *sequence); + applySeries(rewriter, series, *bestSequence); return mlir::success(); } @@ -826,24 +844,25 @@ struct GateDecompositionPattern final } struct TwoQubitWeylDecomposition { - fp a; - fp b; - fp c; - fp globalPhase; + // a, b, c are the parameters of the canonical gate (CAN) + fp a; // rotation of RXX gate in CAN + fp b; // rotation of RYY gate in CAN + fp c; // rotation of RZZ gate in CAN + fp globalPhase; // global phase adjustment /** * q1 - k2r - C - k1r - * A * q0 - k2l - N - k1l - */ - matrix2x2 k1l; - matrix2x2 k2l; - matrix2x2 k1r; - matrix2x2 k2r; - Specialization specialization; - EulerBasis defaultEulerBasis; - std::optional requestedFidelity; - fp calculatedFidelity; - matrix4x4 unitaryMatrix; + matrix2x2 k1l; // "left" qubit after canonical gate + matrix2x2 k2l; // "left" qubit before canonical gate + matrix2x2 k1r; // "right" qubit after canonical gate + matrix2x2 k2r; // "right" qubit before canonical gate + Specialization specialization; // detected symmetries in the matrix + EulerBasis defaultEulerBasis; // recommended euler basis for k1l/k2l/k1r/k2r + std::optional requestedFidelity; // desired fidelity + fp calculatedFidelity; // actual fidelity of decomposition + matrix4x4 unitaryMatrix; // original matrix for this decomposition static TwoQubitWeylDecomposition newInner(matrix4x4 unitaryMatrix, std::optional fidelity, @@ -1496,12 +1515,11 @@ struct GateDecompositionPattern final } }; - static constexpr auto DEFAULT_FIDELITY = 1.0 - 1.0e-9; + static constexpr auto DEFAULT_FIDELITY = 1.0 - 1e-15; struct TwoQubitBasisDecomposer { QubitGateSequence::Gate basisGate; fp basisFidelity; - EulerBasis eulerBasis; TwoQubitWeylDecomposition basisDecomposer; bool superControlled; matrix2x2 u0l; @@ -1529,8 +1547,7 @@ struct GateDecompositionPattern final newInner(const OneQubitGateSequence::Gate& basisGate = {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}, - fp basisFidelity = DEFAULT_FIDELITY, - EulerBasis eulerBasis = EulerBasis::ZYZ) { + fp basisFidelity = DEFAULT_FIDELITY) { auto relativeEq = [](auto&& lhs, auto&& rhs, auto&& epsilon, auto&& maxRelative) { // Handle same infinities @@ -1646,7 +1663,6 @@ struct GateDecompositionPattern final return TwoQubitBasisDecomposer{ .basisGate = basisGate, .basisFidelity = basisFidelity, - .eulerBasis = eulerBasis, .basisDecomposer = basisDecomposer, .superControlled = superControlled, .u0l = u0l, @@ -1671,10 +1687,11 @@ struct GateDecompositionPattern final }; } - std::optional - twoQubitDecompose(const matrix4x4& unitaryMatrix, - std::optional basisFidelity, bool approximate, - std::optional numBasisGateUses) { + [[nodiscard]] std::optional twoQubitDecompose( + const TwoQubitWeylDecomposition& targetDecomposition, + const llvm::SmallVector& target1qEulerBasisList, + std::optional basisFidelity, bool approximate, + std::optional numBasisGateUses) const { auto getBasisFidelity = [&]() { if (approximate) { return basisFidelity.value_or(this->basisFidelity); @@ -1682,9 +1699,7 @@ struct GateDecompositionPattern final return static_cast(1.0); }; fp actualBasisFidelity = getBasisFidelity(); - auto targetDecomposed = TwoQubitWeylDecomposition::newInner( - unitaryMatrix, actualBasisFidelity, std::nullopt); - auto traces = this->traces(targetDecomposed); + auto traces = this->traces(targetDecomposition); auto getDefaultNbasis = [&]() { auto minValue = std::numeric_limits::min(); auto minIndex = -1; @@ -1701,16 +1716,16 @@ struct GateDecompositionPattern final auto bestNbasis = numBasisGateUses.value_or(getDefaultNbasis()); auto chooseDecomposition = [&]() { if (bestNbasis == 0) { - return decomp0Inner(targetDecomposed); + return decomp0Inner(targetDecomposition); } if (bestNbasis == 1) { - return decomp1Inner(targetDecomposed); + return decomp1Inner(targetDecomposition); } if (bestNbasis == 2) { - return decomp2SupercontrolledInner(targetDecomposed); + return decomp2SupercontrolledInner(targetDecomposition); } if (bestNbasis == 3) { - return decomp3SupercontrolledInner(targetDecomposed); + return decomp3SupercontrolledInner(targetDecomposition); } throw std::logic_error{"Invalid basis to use"}; }; @@ -1723,18 +1738,17 @@ struct GateDecompositionPattern final for (auto x : decomposition) { helpers::print(x, "", true); } - std::vector target1qBasisList; // TODO: simplify because list - // only has one element? - target1qBasisList.push_back(eulerBasis); llvm::SmallVector, 8> eulerDecompositions; for (auto&& decomp : decomposition) { assert(helpers::isUnitaryMatrix(decomp)); - auto eulerDecomp = unitaryToGateSequenceInner( - decomp, target1qBasisList, 0, {}, true, 1.0 - actualBasisFidelity); + auto eulerDecomp = + unitaryToGateSequenceInner(decomp, target1qEulerBasisList, 0, {}, + true, 1.0 - actualBasisFidelity); eulerDecompositions.push_back(eulerDecomp); } - TwoQubitGateSequence gates{.globalPhase = targetDecomposed.globalPhase}; + TwoQubitGateSequence gates{.globalPhase = + targetDecomposition.globalPhase}; // Worst case length is 5x 1q gates for each 1q decomposition + 1x 2q // gate We might overallocate a bit if the euler basis is different but // the worst case is just 16 extra elements with just a String and 2 @@ -1869,7 +1883,8 @@ struct GateDecompositionPattern final } static OneQubitGateSequence unitaryToGateSequenceInner( - matrix2x2 unitaryMat, const std::vector& targetBasisList, + matrix2x2 unitaryMat, + const llvm::SmallVector& targetBasisList, std::size_t /*qubit*/, const std::vector>& /*error_map*/, // TODO: remove error_map+qubit for platform @@ -1962,7 +1977,8 @@ struct GateDecompositionPattern final private: llvm::SmallVector decomposerBasisGate; - llvm::SmallVector decomposerEulerBasis; + llvm::SmallVector basisDecomposers; + llvm::SmallVector decomposerEulerBases; }; const matrix2x2 GateDecompositionPattern::IDENTITY_GATE = matrix2x2::Identity(); @@ -1978,7 +1994,8 @@ const matrix2x2 GateDecompositionPattern::IPX{{C_ZERO, IM}, {IM, C_ZERO}}; * decomposition. */ void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns) { - llvm::SmallVector basisGates; + llvm::SmallVector + basisGates; llvm::SmallVector eulerBases; basisGates.push_back({.type = qc::X, .parameter = {}, .qubitId = {0, 1}}); basisGates.push_back({.type = qc::X, .parameter = {}, .qubitId = {1, 0}}); From 83868c09b952b40f25593eef53a1fa3b7909a678 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 19 Nov 2025 00:27:31 +0100 Subject: [PATCH 078/237] add checks for most tests --- .../MQTOpt/Transforms/gate-decomposition.mlir | 97 ++++++++++++++++--- 1 file changed, 85 insertions(+), 12 deletions(-) diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index c9292b9145..f66b0d333e 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -76,6 +76,9 @@ module { // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] + // ensure no other operations are inserted + // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) + // CHECK: mqtopt.deallocQubit %[[Q0_1]] // CHECK: mqtopt.deallocQubit %[[Q1_1]] @@ -143,6 +146,9 @@ module { // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C1:.*]]) %[[Q0_1]] // CHECK: %[[Q0_3:.*]] = mqtopt.rz(%[[C2:.*]]) %[[Q0_2]] + // ensure no other operations are inserted + // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) + // CHECK: mqtopt.deallocQubit %[[Q0_3]] // CHECK: mqtopt.deallocQubit %[[Q1_0]] @@ -178,6 +184,9 @@ module { // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C1:.*]]) %[[Q0_1]] // CHECK: %[[Q0_3:.*]] = mqtopt.rz(%[[C2:.*]]) %[[Q0_2]] + // ensure no other operations are inserted + // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) + // CHECK: mqtopt.deallocQubit %[[Q0_3]] // CHECK: mqtopt.deallocQubit %[[Q1_0]] @@ -213,6 +222,9 @@ module { // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C1:.*]]) %[[Q0_1]] // CHECK: %[[Q0_3:.*]] = mqtopt.rz(%[[C2:.*]]) %[[Q0_2]] + // ensure no other operations are inserted + // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) + // CHECK: mqtopt.deallocQubit %[[Q0_3]] // CHECK: mqtopt.deallocQubit %[[Q1_0]] @@ -246,9 +258,8 @@ module { // CHECK: %[[Q1_3:.*]], %[[Q0_4:.*]] = mqtopt.x() %[[Q1_2]] ctrl %[[Q0_3]] // CHECK: %[[Q0_5:.*]], %[[Q1_4:.*]] = mqtopt.x() %[[Q0_4]] ctrl %[[Q1_3]] - // CHECK-NOT: mqtopt.ry - // CHECK-NOT: mqtopt.rz - // CHECK-NOT: mqtopt.gphase(%[[ANY:.*]]) + // ensure no other operations are inserted + // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) // CHECK: mqtopt.deallocQubit %[[Q0_5]] // CHECK: mqtopt.deallocQubit %[[Q1_4]] @@ -313,6 +324,9 @@ module { // CHECK: %[[Q1_9:.*]] = mqtopt.rz(%[[C12]]) %[[Q1_8]] // CHECK: %[[Q1_10:.*]] = mqtopt.ry(%[[C0]]) %[[Q1_9]] + // ensure no other operations are inserted + // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) + // CHECK: mqtopt.deallocQubit %[[Q0_10]] // CHECK: mqtopt.deallocQubit %[[Q1_10]] @@ -354,13 +368,24 @@ module { module { // CHECK-LABEL: func.func @testSingleQubitSeries func.func @testSingleQubitSeries() { + // CHECK: %[[C0:.*]] = arith.constant -1.570796326 + // CHECK: %[[C1:.*]] = arith.constant 2.400000e+00 + // CHECK: %[[C2:.*]] = arith.constant 1.5707963267 + // CHECK: %[[C3:.*]] = arith.constant 2.500000e+00 + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] + // CHECK: %[[Q0_1:.*]] = mqtopt.ry(%[[C3]]) %[[Q0_0]] + // CHECK: %[[Q1_1:.*]] = mqtopt.rz(%[[C2]]) %[[Q1_0]] + // CHECK: %[[Q1_2:.*]] = mqtopt.ry(%[[C1]]) %[[Q1_1]] + // CHECK: %[[Q1_3:.*]] = mqtopt.rz(%[[C0]]) %[[Q1_2]] + + // ensure no other operations are inserted + // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q1_1]] + // CHECK: mqtopt.deallocQubit %[[Q1_3]] %cst0 = arith.constant 2.5 : f64 %cst1 = arith.constant 1.2 : f64 @@ -386,15 +411,63 @@ module { module { // CHECK-LABEL: func.func @testThreeBasisGateDecomposition func.func @testThreeBasisGateDecomposition() { + // CHECK: %[[C0:.*]] = arith.constant 2.5762316133983267 : f64 + // CHECK: %[[C1:.*]] = arith.constant 0.7853981633974495 : f64 + // CHECK: %[[C2:.*]] = arith.constant -1.5707963267948981 : f64 + // CHECK: %[[C3:.*]] = arith.constant -2.2830967446466035 : f64 + // CHECK: %[[C4:.*]] = arith.constant 2.7053466041231213 : f64 + // CHECK: %[[C5:.*]] = arith.constant -2.4341917193679428 : f64 + // CHECK: %[[C6:.*]] = arith.constant -0.61547970867038693 : f64 + // CHECK: %[[C7:.*]] = arith.constant 2.0943951023931953 : f64 + // CHECK: %[[C8:.*]] = arith.constant 0.61547970867038737 : f64 + // CHECK: %[[C9:.*]] = arith.constant 0.25938051218841107 : f64 + // CHECK: %[[C10:.*]] = arith.constant -2.5261129449194062 : f64 + // CHECK: %[[C11:.*]] = arith.constant 1.0471975511965979 : f64 + // CHECK: %[[C12:.*]] = arith.constant -2.5261129449194053 : f64 + // CHECK: %[[C13:.*]] = arith.constant -1.5707963267948966 : f64 + // CHECK: %[[C14:.*]] = arith.constant 0.6948042173197494 : f64 + // CHECK: %[[C15:.*]] = arith.constant -1.5707963267948968 : f64 + // CHECK: %[[C16:.*]] = arith.constant 0.45501497985473183 : f64 + // CHECK: %[[C17:.*]] = arith.constant 1.7692140485614463 : f64 + // CHECK: %[[C18:.*]] = arith.constant -1.508911464303158 : f64 + // CHECK: %[[C19:.*]] = arith.constant -0.82190477627456371 : f64 + // CHECK: %[[C20:.*]] = arith.constant 1.5707963267948963 : f64 + // CHECK: %[[C21:.*]] = arith.constant 1.5707963267948966 : f64 + // CHECK: %[[C22:.*]] = arith.constant -0.78539816339744739 : f64 + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit - - // CHECK-NOT: mqtopt.gphase(%[[ANY:.*]]) - // CHECK: mqtopt.deallocQubit %[[Q0_5]] - // CHECK: mqtopt.deallocQubit %[[Q1_4]] - // CHECK: mqtopt.deallocQubit %[[Q2_1]] + // CHECK: mqtopt.gphase(%[[C22]]) + // CHECK: %[[Q0_1:.*]] = mqtopt.rz(%[[C21]]) %[[Q0_0]] : !mqtopt.Qubit + // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C20]]) %[[Q0_1]] : !mqtopt.Qubit + // CHECK: %[[Q0_3:.*]] = mqtopt.rz(%[[C19]]) %[[Q0_2]] : !mqtopt.Qubit + // CHECK: %[[Q1_1:.*]] = mqtopt.rz(%[[C18]]) %[[Q1_0]] : !mqtopt.Qubit + // CHECK: %[[Q1_2:.*]] = mqtopt.ry(%[[C17]]) %[[Q1_1]] : !mqtopt.Qubit + // CHECK: %[[Q1_3:.*]] = mqtopt.rz(%[[C16]]) %[[Q1_2]] : !mqtopt.Qubit + // CHECK: %[[Q1_4:.*]], %[[Q0_4:.*]] = mqtopt.x() %[[Q1_3]] ctrl %[[Q0_3]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[Q0_5:.*]] = mqtopt.rz(%[[C15]]) %[[Q0_4]] : !mqtopt.Qubit + // CHECK: %[[Q0_6:.*]] = mqtopt.ry(%[[C14]]) %[[Q0_5]] : !mqtopt.Qubit + // CHECK: %[[Q0_7:.*]] = mqtopt.rz(%[[C13]]) %[[Q0_6]] : !mqtopt.Qubit + // CHECK: %[[Q1_5:.*]] = mqtopt.rz(%[[C12]]) %[[Q1_4]] : !mqtopt.Qubit + // CHECK: %[[Q1_6:.*]] = mqtopt.ry(%[[C11]]) %[[Q1_5]] : !mqtopt.Qubit + // CHECK: %[[Q1_7:.*]] = mqtopt.rz(%[[C10]]) %[[Q1_6]] : !mqtopt.Qubit + // CHECK: %[[Q1_8:.*]], %[[Q0_8:.*]] = mqtopt.x() %[[Q1_7]] ctrl %[[Q0_7]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[Q0_9:.*]] = mqtopt.rz(%[[C21]]) %[[Q0_8]] : !mqtopt.Qubit + // CHECK: %[[Q0_10:.*]] = mqtopt.ry(%[[C9]]) %[[Q0_9]] : !mqtopt.Qubit + // CHECK: %[[Q1_9:.*]] = mqtopt.rz(%[[C8]]) %[[Q1_8]] : !mqtopt.Qubit + // CHECK: %[[Q1_10:.*]] = mqtopt.ry(%[[C7]]) %[[Q1_9]] : !mqtopt.Qubit + // CHECK: %[[Q1_11:.*]] = mqtopt.rz(%[[C6]]) %[[Q1_10]] : !mqtopt.Qubit + // CHECK: %[[Q1_12:.*]], %[[Q0_11:.*]] = mqtopt.x() %[[Q1_11]] ctrl %[[Q0_10]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[Q0_12:.*]] = mqtopt.rz(%[[C5]]) %[[Q0_11]] : !mqtopt.Qubit + // CHECK: %[[Q0_13:.*]] = mqtopt.ry(%[[C4]]) %[[Q0_12]] : !mqtopt.Qubit + // CHECK: %[[Q0_14:.*]] = mqtopt.rz(%[[C3]]) %[[Q0_13]] : !mqtopt.Qubit + // CHECK: %[[Q1_13:.*]] = mqtopt.rz(%[[C2]]) %[[Q1_12]] : !mqtopt.Qubit + // CHECK: %[[Q1_14:.*]] = mqtopt.ry(%[[C1]]) %[[Q1_13]] : !mqtopt.Qubit + // CHECK: %[[Q1_15:.*]] = mqtopt.rz(%[[C0]]) %[[Q1_14]] : !mqtopt.Qubit + + // CHECK: mqtopt.deallocQubit %[[Q0_14]] + // CHECK: mqtopt.deallocQubit %[[Q1_15]] %cst0 = arith.constant 2.5 : f64 %cst1 = arith.constant 1.2 : f64 @@ -435,7 +508,7 @@ module { // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit - // CHECK-NOT: mqtopt.gphase(%[[ANY:.*]]) + // TODO // CHECK: mqtopt.deallocQubit %[[Q0_5]] // CHECK: mqtopt.deallocQubit %[[Q1_4]] From b16bc9ad04c74ef256a425d7dd18c86a4e47cdbb Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 19 Nov 2025 00:28:11 +0100 Subject: [PATCH 079/237] TMP, needs to be removed; allow OpenQASM export with round-trip pass --- .../Dialect/MQTOpt/Transforms/MQTCoreRoundTrip.cpp | 5 ++++- .../MQTOpt/Transforms/ToQuantumComputationPattern.cpp | 11 +++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/MQTCoreRoundTrip.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/MQTCoreRoundTrip.cpp index 276b315024..37a7f20297 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/MQTCoreRoundTrip.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/MQTCoreRoundTrip.cpp @@ -33,12 +33,15 @@ struct MQTCoreRoundTrip final : impl::MQTCoreRoundTripBase { // Define the set of patterns to use. mlir::RewritePatternSet patterns(ctx); populateToQuantumComputationPatterns(patterns, circuit); - populateFromQuantumComputationPatterns(patterns, circuit); + // populateFromQuantumComputationPatterns(patterns, circuit); // Apply patterns in an iterative and greedy manner. if (mlir::failed(mlir::applyPatternsGreedily(op, std::move(patterns)))) { signalPassFailure(); } + std::cerr << "======================\n"; + circuit.dumpOpenQASM(std::cerr, false); + std::cerr << "======================\n"; } }; diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/ToQuantumComputationPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/ToQuantumComputationPattern.cpp index 3387ec26b3..7461645b0c 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/ToQuantumComputationPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/ToQuantumComputationPattern.cpp @@ -8,6 +8,7 @@ * Licensed under the MIT License */ +#include "Helpers.h" #include "ir/Definitions.hpp" #include "ir/QuantumComputation.hpp" #include "ir/operations/Control.hpp" @@ -210,7 +211,9 @@ struct ToQuantumComputationPattern final findQubitIndex(val, currentQubitVariables)); } // Get the qubit index of the target qubit (if already collected). + if (!in.empty()) { targetIndex[0] = findQubitIndex(in[0], currentQubitVariables); + } if (in.size() > 1) { targetIndex[1] = findQubitIndex(in[1], currentQubitVariables); } @@ -231,7 +234,9 @@ struct ToQuantumComputationPattern final currentQubitVariables[negCtrlInsIndices[i]] = outs[i + 1 + posCtrlInsIndices.size()]; } - currentQubitVariables[targetIndex[0]] = outs[0]; + if (!op.getOutQubits().empty()) { + currentQubitVariables[targetIndex[0]] = outs[0]; + } if (op.getOutQubits().size() > 1) { currentQubitVariables[targetIndex[1]] = outs[1]; } @@ -253,6 +258,8 @@ struct ToQuantumComputationPattern final parameters.emplace_back(param); } } + auto x = helpers::getParameters(op); + parameters.insert(parameters.end(), x.begin(), x.end()); if (op.getOutQubits().size() > 1) { circuit.emplace_back( @@ -379,7 +386,7 @@ struct ToQuantumComputationPattern final std::string regName; llvm::raw_string_ostream os(regName); - op.getResult().print(os); + os << "q"; circuit.addQubitRegister(numQubits, regName); circuit.addClassicalRegister(numQubits); From 8cdce4455b27208941e307570b8c7da8f0b99c8f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 19 Nov 2025 16:49:58 +0100 Subject: [PATCH 080/237] add single-qubit backtracking --- .../Transforms/GateDecompositionPattern.cpp | 51 +++++++++++++++++-- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index f1345877d7..b3ec45bf56 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -59,6 +59,7 @@ struct GateDecompositionPattern final }; std::vector gates; [[nodiscard]] std::size_t complexity() const { + // TODO: caching mechanism std::size_t c{}; for (auto&& gate : gates) { c += getComplexity(gate.type, gate.qubitId.size()); @@ -117,8 +118,7 @@ struct GateDecompositionPattern final // too short return mlir::failure(); } - if (llvm::is_contained(series.inQubits, mlir::Value{}) || - llvm::is_contained(series.outQubits, mlir::Value{})) { + if (series.isSingleQubitSeries()) { // only a single-qubit series return mlir::failure(); } @@ -224,7 +224,7 @@ struct GateDecompositionPattern final return result; } - matrix4x4 getUnitaryMatrix() { + [[nodiscard]] matrix4x4 getUnitaryMatrix() const { matrix4x4 unitaryMatrix = helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); for (auto&& gate : gates) { @@ -240,6 +240,11 @@ struct GateDecompositionPattern final return unitaryMatrix; } + [[nodiscard]] bool isSingleQubitSeries() const { + return llvm::is_contained(inQubits, mlir::Value{}) || + llvm::is_contained(outQubits, mlir::Value{}); + } + private: explicit TwoQubitSeries(UnitaryInterface initialOperation) { auto&& in = initialOperation.getAllInQubits(); @@ -298,13 +303,25 @@ struct GateDecompositionPattern final if (it == outQubits.end()) { return false; } + // iterator in the operation input of "old" qubit that already has + // previous single-qubit gates in this series auto it2 = llvm::find(opInQubits, firstQubitIt != outQubits.end() ? *firstQubitIt : *secondQubitIt); - inQubits[std::distance(outQubits.begin(), it)] = - opInQubits[1 - std::distance(opInQubits.begin(), it2)]; + // new qubit ID based on position in outQubits + auto newInQubitId = std::distance(outQubits.begin(), it); + // position in operation input; since there are only two qubits, it must + // be the "not old" one + auto newOpInQubitId = 1 - std::distance(opInQubits.begin(), it2); + + // update inQubit and update dangling iterator, then proceed as usual + inQubits[newInQubitId] = opInQubits[newOpInQubitId]; firstQubitIt = (firstQubitIt != outQubits.end()) ? firstQubitIt : it; secondQubitIt = (secondQubitIt != outQubits.end()) ? secondQubitIt : it; + + // before proceeding as usual, see if backtracking on the "new" qubit is + // possible to collect other single-qubit operations + backtrackSingleQubitSeries(newInQubitId); } std::size_t firstQubitId = std::distance(outQubits.begin(), firstQubitIt); std::size_t secondQubitId = @@ -317,6 +334,30 @@ struct GateDecompositionPattern final complexity += getComplexity(helpers::getQcType(nextGate), 2); return true; } + + /** + * Traverse single-qubit series back from a given qubit. + * This is used when a series starts with single-qubit gates and then + * encounters a two-qubit gate. The second qubit involved in the two-qubit + * gate could have previous single-qubit operations that can be incorporated + * in the series. + */ + void backtrackSingleQubitSeries(std::size_t qubitId) { + auto prependSingleQubitGate = [&](UnitaryInterface op) { + inQubits[qubitId] = op.getAllInQubits()[0]; + gates.insert(gates.begin(), {.op = op, .qubitIds = {qubitId}}); + // outQubits do not need to be updated because the final out qubit is + // already fixed + }; + while (auto* op = inQubits[qubitId].getDefiningOp()) { + auto unitaryOp = mlir::dyn_cast(op); + if (unitaryOp && helpers::isSingleQubitOperation(unitaryOp)) { + prependSingleQubitGate(unitaryOp); + } else { + break; + } + } + } }; /** From e919bcd9ef386af735f1ffe9e128aa345cc65703 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 19 Nov 2025 16:50:20 +0100 Subject: [PATCH 081/237] fix atol issues --- .../MQTOpt/Transforms/GateDecompositionPattern.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index b3ec45bf56..08fb0014c9 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -1557,6 +1557,7 @@ struct GateDecompositionPattern final }; static constexpr auto DEFAULT_FIDELITY = 1.0 - 1e-15; + static constexpr auto DEFAULT_ATOL = 1e-12; struct TwoQubitBasisDecomposer { QubitGateSequence::Gate basisGate; @@ -1783,9 +1784,8 @@ struct GateDecompositionPattern final eulerDecompositions; for (auto&& decomp : decomposition) { assert(helpers::isUnitaryMatrix(decomp)); - auto eulerDecomp = - unitaryToGateSequenceInner(decomp, target1qEulerBasisList, 0, {}, - true, 1.0 - actualBasisFidelity); + auto eulerDecomp = unitaryToGateSequenceInner( + decomp, target1qEulerBasisList, 0, {}, true, std::nullopt); eulerDecompositions.push_back(eulerDecomp); } TwoQubitGateSequence gates{.globalPhase = @@ -1972,7 +1972,7 @@ struct GateDecompositionPattern final calculateRotationGates(fp theta, fp phi, fp lambda, fp phase, qc::OpType kGate, qc::OpType aGate, bool simplify, std::optional atol) { - fp angleZeroEpsilon = atol.value_or(1e-12); + fp angleZeroEpsilon = atol.value_or(DEFAULT_ATOL); if (!simplify) { angleZeroEpsilon = -1.0; } From 1f91271d88174b0b2d75066c3a118e174c2abcc5 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 19 Nov 2025 19:00:35 +0100 Subject: [PATCH 082/237] add more euler bases to pattern --- mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 08fb0014c9..7ccf7af45f 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -2041,6 +2041,8 @@ void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns) { basisGates.push_back({.type = qc::X, .parameter = {}, .qubitId = {0, 1}}); basisGates.push_back({.type = qc::X, .parameter = {}, .qubitId = {1, 0}}); eulerBases.push_back(GateDecompositionPattern::EulerBasis::ZYZ); + eulerBases.push_back(GateDecompositionPattern::EulerBasis::XYX); + eulerBases.push_back(GateDecompositionPattern::EulerBasis::ZXZ); patterns.add(patterns.getContext(), basisGates, eulerBases); } From d4f1b494eccd60607337ee9c9739ac579366f223 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 19 Nov 2025 19:01:35 +0100 Subject: [PATCH 083/237] minor cleanup (renaming, comments, disable printing) --- .../Transforms/GateDecompositionPattern.cpp | 195 +++++++++++------- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 3 - 2 files changed, 122 insertions(+), 76 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 7ccf7af45f..cb051b1478 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -100,7 +100,7 @@ struct GateDecompositionPattern final decomposerEulerBases{std::move(eulerBasis)} { for (auto&& basisGate : decomposerBasisGate) { basisDecomposers.push_back( - TwoQubitBasisDecomposer::newInner(basisGate, DEFAULT_FIDELITY)); + TwoQubitBasisDecomposer::create(basisGate, DEFAULT_FIDELITY)); } } @@ -124,9 +124,9 @@ struct GateDecompositionPattern final } matrix4x4 unitaryMatrix = series.getUnitaryMatrix(); - helpers::print(unitaryMatrix, "UNITARY MATRIX", true); + helpers::print(unitaryMatrix, "UNITARY MATRIX"); - auto targetDecomposition = TwoQubitWeylDecomposition::newInner( + auto targetDecomposition = TwoQubitWeylDecomposition::create( unitaryMatrix, DEFAULT_FIDELITY, std::nullopt); std::optional bestSequence; @@ -518,9 +518,9 @@ struct GateDecompositionPattern final } } std::cerr << '\n'; - helpers::print(series.getUnitaryMatrix(), "ORIGINAL UNITARY", true); + helpers::print(series.getUnitaryMatrix(), "ORIGINAL UNITARY"); helpers::print((unitaryMatrix * std::exp(IM * sequence.globalPhase)).eval(), - "RESULT UNITARY MATRIX", true); + "RESULT UNITARY MATRIX"); assert((unitaryMatrix * std::exp(IM * sequence.globalPhase)) .isApprox(series.getUnitaryMatrix(), SANITY_CHECK_PRECISION)); @@ -609,13 +609,11 @@ struct GateDecompositionPattern final matrix2x2 r{{specialUnitary(0, 0), specialUnitary(0, 1)}, {specialUnitary(1, 0), specialUnitary(1, 1)}}; auto detR = r.determinant(); - std::cerr << "DET_R: " << detR << '\n'; if (std::abs(detR) < 0.1) { // third quadrant r = matrix2x2{{specialUnitary(2, 0), specialUnitary(2, 1)}, {specialUnitary(3, 0), specialUnitary(3, 1)}}; detR = r.determinant(); - std::cerr << "DET_R CORRECTION: " << detR << '\n'; } if (std::abs(detR) < 0.1) { throw std::runtime_error{ @@ -906,22 +904,19 @@ struct GateDecompositionPattern final matrix4x4 unitaryMatrix; // original matrix for this decomposition static TwoQubitWeylDecomposition - newInner(matrix4x4 unitaryMatrix, std::optional fidelity, - std::optional specialization) { + create(matrix4x4 unitaryMatrix, std::optional fidelity, + std::optional specialization) { auto u = unitaryMatrix; auto detU = u.determinant(); - std::cerr << "DET_U: " << detU << '\n'; auto detPow = std::pow(detU, static_cast(-0.25)); u *= detPow; - helpers::print(u, "U", true); + helpers::print(u, "U"); auto globalPhase = std::arg(detU) / 4.; auto uP = magicBasisTransform(u, MagicBasisTransform::OutOf); - helpers::print(uP, "U_P", true); + helpers::print(uP, "U_P"); matrix4x4 m2 = uP.transpose() * uP; auto defaultEulerBasis = EulerBasis::ZYZ; - helpers::print(m2, "M2", true); - - std::cerr << "DET_U after division: " << u.determinant() << '\n'; + helpers::print(m2, "M2"); // M2 is a symmetric complex matrix. We need to decompose it as M2 = P D // P^T where P ∈ SO(4), D is diagonal with unit-magnitude elements. @@ -963,8 +958,8 @@ struct GateDecompositionPattern final matrix4x4 pInner = pInnerReal; diagonal4x4 dInner = (pInner.transpose() * m2 * pInner).diagonal(); - helpers::print(dInner, "D_INNER", true); - helpers::print(pInner, "P_INNER", true); + helpers::print(dInner, "D_INNER"); + helpers::print(pInner, "P_INNER"); matrix4x4 diagD = dInner.asDiagonal(); matrix4x4 compare = pInner * diagD * pInner.transpose(); @@ -995,13 +990,13 @@ struct GateDecompositionPattern final // Step 7 Eigen::Vector cs; rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; - helpers::print(dReal, "D_REAL", true); + helpers::print(dReal, "D_REAL"); dReal(3) = -dReal(0) - dReal(1) - dReal(2); for (int i = 0; i < static_cast(cs.size()); ++i) { assert(i < dReal.size()); cs[i] = remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); } - helpers::print(cs, "CS (1)", true); + helpers::print(cs, "CS (1)"); decltype(cs) cstemp; llvm::transform(cs, cstemp.begin(), [](auto&& x) { @@ -1026,7 +1021,7 @@ struct GateDecompositionPattern final std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; std::tie(dReal(0), dReal(1), dReal(2)) = std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2])}; - helpers::print(dReal, "D_REAL (sorted)", true); + helpers::print(dReal, "D_REAL (sorted)"); // swap columns of p according to order matrix4x4 pOrig = p; @@ -1045,18 +1040,18 @@ struct GateDecompositionPattern final // temp = temp.conjugate(); // temp += matrix4x4::Constant(0.0); - helpers::print(temp, "TEMP", true); - helpers::print(p, "P", true); + helpers::print(temp, "TEMP"); + helpers::print(p, "P"); assert(std::abs(p.determinant() - 1.0) < SANITY_CHECK_PRECISION); // https://threeplusone.com/pubs/on_gates.pdf // uP = V, m2 = V^T*V, temp = D, p = Q1 matrix4x4 k1 = uP * p * temp; - helpers::print(k1, "K1 (1)", true); + helpers::print(k1, "K1 (1)"); assert((k1.transpose() * k1).isIdentity()); // k1 must be orthogonal assert(k1.determinant().real() > 0.0); k1 = magicBasisTransform(k1, MagicBasisTransform::Into); matrix4x4 k2 = p.transpose().conjugate(); - helpers::print(k2, "K2 (1)", true); + helpers::print(k2, "K2 (1)"); assert((k2.transpose() * k2).isIdentity()); // k2 must be orthogonal assert(k2.determinant().real() > 0.0); k2 = magicBasisTransform(k2, MagicBasisTransform::Into); @@ -1066,7 +1061,7 @@ struct GateDecompositionPattern final magicBasisTransform(temp.conjugate(), MagicBasisTransform::Into) * k2) .eval(), - "SANITY CHECK (1)", true); + "SANITY CHECK (1)"); assert((k1 * magicBasisTransform(temp.conjugate(), MagicBasisTransform::Into) * k2) @@ -1133,16 +1128,16 @@ struct GateDecompositionPattern final globalPhase -= qc::PI_2; } - helpers::print(K1l, "K1l (1)", true); - helpers::print(K2l, "K2l (1)", true); - helpers::print(K1r, "K1r (1)", true); - helpers::print(K2r, "K2r (1)", true); + helpers::print(K1l, "K1l (1)"); + helpers::print(K2l, "K2l (1)"); + helpers::print(K1r, "K1r (1)"); + helpers::print(K2r, "K2r (1)"); - helpers::print(cs, "CS (2)", true); - helpers::print(K1l, "K1l (2)", true); - helpers::print(K2l, "K2l (2)", true); - helpers::print(K1r, "K1r (2)", true); - helpers::print(K2r, "K2r (2)", true); + helpers::print(cs, "CS (2)"); + helpers::print(K1l, "K1l (2)"); + helpers::print(K2l, "K2l (2)"); + helpers::print(K1r, "K1r (2)"); + helpers::print(K2r, "K2r (2)"); auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); auto getCanonicalMatrix = [](fp a, fp b, fp c) -> matrix4x4 { auto xx = getTwoQubitMatrix({ @@ -1163,19 +1158,15 @@ struct GateDecompositionPattern final return zz * yy * xx; }; helpers::print(getCanonicalMatrix(a * -2.0, b * -2.0, c * -2.0), - "SANITY CHECK (2.1)", true); - helpers::print(helpers::kroneckerProduct(K1l, K1r), "SANITY CHECK (2.2)", - true); - helpers::print(helpers::kroneckerProduct(K2l, K2r), "SANITY CHECK (2.3)", - true); + "SANITY CHECK (2.1)"); + helpers::print(helpers::kroneckerProduct(K1l, K1r), "SANITY CHECK (2.2)"); + helpers::print(helpers::kroneckerProduct(K2l, K2r), "SANITY CHECK (2.3)"); helpers::print((helpers::kroneckerProduct(K1l, K1r) * getCanonicalMatrix(a * -2.0, b * -2.0, c * -2.0) * helpers::kroneckerProduct(K2l, K2r) * std::exp(IM * globalPhase)) .eval(), - "SANITY CHECK (2.x)", true); - std::cerr << "gphase: " << globalPhase << ", phase_l: " << phase_l - << ", phase_r: " << phase_r << '\n'; + "SANITY CHECK (2.x)"); assert((helpers::kroneckerProduct(K1l, K1r) * getCanonicalMatrix(a * -2.0, b * -2.0, c * -2.0) * helpers::kroneckerProduct(K2l, K2r) * std::exp(IM * globalPhase)) @@ -1544,7 +1535,7 @@ struct GateDecompositionPattern final helpers::kroneckerProduct(specialized.k2l, specialized.k2r) * std::exp(IM * specialized.globalPhase)) .eval(), - "SANITY CHECK (3)", true); + "SANITY CHECK (3)"); assert((helpers::kroneckerProduct(specialized.k1l, specialized.k1r) * getCanonicalMatrix(specialized.a * -2.0, specialized.b * -2.0, specialized.c * -2.0) * @@ -1586,10 +1577,10 @@ struct GateDecompositionPattern final public: static TwoQubitBasisDecomposer - newInner(const OneQubitGateSequence::Gate& basisGate = {.type = qc::X, - .parameter = {}, - .qubitId = {0, 1}}, - fp basisFidelity = DEFAULT_FIDELITY) { + create(const OneQubitGateSequence::Gate& basisGate = {.type = qc::X, + .parameter = {}, + .qubitId = {0, 1}}, + fp basisFidelity = DEFAULT_FIDELITY) { auto relativeEq = [](auto&& lhs, auto&& rhs, auto&& epsilon, auto&& maxRelative) { // Handle same infinities @@ -1625,7 +1616,7 @@ struct GateDecompositionPattern final {qfp(-0.5, 0.5), qfp(0.5, -0.5)}, }; - auto basisDecomposer = TwoQubitWeylDecomposition::newInner( + auto basisDecomposer = TwoQubitWeylDecomposition::create( getTwoQubitMatrix(basisGate), basisFidelity, std::nullopt); auto superControlled = relativeEq(basisDecomposer.a, qc::PI_4, 1e-13, 1e-09) && @@ -1729,11 +1720,11 @@ struct GateDecompositionPattern final }; } - [[nodiscard]] std::optional twoQubitDecompose( - const TwoQubitWeylDecomposition& targetDecomposition, - const llvm::SmallVector& target1qEulerBasisList, - std::optional basisFidelity, bool approximate, - std::optional numBasisGateUses) const { + [[nodiscard]] std::optional + twoQubitDecompose(const TwoQubitWeylDecomposition& targetDecomposition, + const llvm::SmallVector& target1qEulerBases, + std::optional basisFidelity, bool approximate, + std::optional numBasisGateUses) const { auto getBasisFidelity = [&]() { if (approximate) { return basisFidelity.value_or(this->basisFidelity); @@ -1746,6 +1737,8 @@ struct GateDecompositionPattern final auto minValue = std::numeric_limits::min(); auto minIndex = -1; for (int i = 0; i < static_cast(traces.size()); ++i) { + // lower fidelity means it becomes easier to choose a lower number of + // basis gates auto value = traceToFid(traces[i]) * std::pow(actualBasisFidelity, i); if (value > minValue) { minIndex = i; @@ -1758,34 +1751,26 @@ struct GateDecompositionPattern final auto bestNbasis = numBasisGateUses.value_or(getDefaultNbasis()); auto chooseDecomposition = [&]() { if (bestNbasis == 0) { - return decomp0Inner(targetDecomposition); + return decomp0(targetDecomposition); } if (bestNbasis == 1) { - return decomp1Inner(targetDecomposition); + return decomp1(targetDecomposition); } if (bestNbasis == 2) { - return decomp2SupercontrolledInner(targetDecomposition); + return decomp2Supercontrolled(targetDecomposition); } if (bestNbasis == 3) { - return decomp3SupercontrolledInner(targetDecomposition); + return decomp3Supercontrolled(targetDecomposition); } throw std::logic_error{"Invalid basis to use"}; }; auto decomposition = chooseDecomposition(); - std::cerr << "NBasis: " << static_cast(bestNbasis) - << "; basis_fid: " << actualBasisFidelity - << "; Traces: " << traces[0] << ", " << traces[1] << ", " - << traces[2] << ", " << traces[3]; - std::cerr << "\nDecompositions:\n"; - for (auto x : decomposition) { - helpers::print(x, "", true); - } llvm::SmallVector, 8> eulerDecompositions; for (auto&& decomp : decomposition) { assert(helpers::isUnitaryMatrix(decomp)); auto eulerDecomp = unitaryToGateSequenceInner( - decomp, target1qEulerBasisList, 0, {}, true, std::nullopt); + decomp, target1qEulerBases, 0, {}, true, std::nullopt); eulerDecompositions.push_back(eulerDecomp); } TwoQubitGateSequence gates{.globalPhase = @@ -1814,7 +1799,6 @@ struct GateDecompositionPattern final } }; - // TODO: check if this actually uses the correct qubitIds for (std::size_t i = 0; i < bestNbasis; ++i) { addEulerDecomposition(2 * i, 0); addEulerDecomposition((2 * i) + 1, 1); @@ -1834,16 +1818,43 @@ struct GateDecompositionPattern final } private: + /** + * Calculate decompositions when no basis gate is required. + * + * Decompose target :math:`\sim U_d(x, y, z)` with :math:`0` uses of the + * basis gate. Result :math:`U_r` has trace: + * + * .. math:: + * + * \Big\vert\text{Tr}(U_r\cdot U_\text{target}^{\dag})\Big\vert = + * 4\Big\vert (\cos(x)\cos(y)\cos(z)+ j \sin(x)\sin(y)\sin(z)\Big\vert + * + * which is optimal for all targets and bases + */ [[nodiscard]] static std::vector - decomp0Inner(const TwoQubitWeylDecomposition& target) { + decomp0(const TwoQubitWeylDecomposition& target) { return { target.k1r * target.k2r, target.k1l * target.k2l, }; } + /** + * Calculate decompositions when one basis gate is required. + * + * Decompose target :math:`\sim U_d(x, y, z)` with :math:`1` use of the + * basis gate math:`\sim U_d(a, b, c)`. Result :math:`U_r` has trace: + * + * .. math:: + * + * \Big\vert\text{Tr}(U_r \cdot U_\text{target}^{\dag})\Big\vert = + * 4\Big\vert \cos(x-a)\cos(y-b)\cos(z-c) + j + * \sin(x-a)\sin(y-b)\sin(z-c)\Big\vert + * + * which is optimal for all targets and bases with ``z==0`` or ``c==0``. + */ [[nodiscard]] std::vector - decomp1Inner(const TwoQubitWeylDecomposition& target) const { + decomp1(const TwoQubitWeylDecomposition& target) const { // FIXME: fix for z!=0 and c!=0 using closest reflection (not always in // the Weyl chamber) return { @@ -1854,8 +1865,31 @@ struct GateDecompositionPattern final }; } + /** + * Calculate decompositions when two basis gates are required. + * + * Decompose target :math:`\sim U_d(x, y, z)` with :math:`2` uses of the + * basis gate. + * + * For supercontrolled basis :math:`\sim U_d(\pi/4, b, 0)`, all b, result + * :math:`U_r` has trace + * + * .. math:: + * + * \Big\vert\text{Tr}(U_r \cdot U_\text{target}^\dag) \Big\vert = + * 4\cos(z) + * + * which is the optimal approximation for basis of CNOT-class :math:`\sim + * U_d(\pi/4, 0, 0)` or DCNOT-class :math:`\sim U_d(\pi/4, \pi/4, 0)` and + * any target. It may be sub-optimal for :math:`b \neq 0` (i.e. there exists + * an exact decomposition for any target using + * :math:`B \sim U_d(\pi/4, \pi/8, 0)`, but it may not be this + * decomposition). This is an exact decomposition for supercontrolled basis + * and target :math:`\sim U_d(x, y, 0)`. No guarantees for + * non-supercontrolled basis. + */ [[nodiscard]] std::vector - decomp2SupercontrolledInner(const TwoQubitWeylDecomposition& target) const { + decomp2Supercontrolled(const TwoQubitWeylDecomposition& target) const { return { q2r * target.k2r, q2l * target.k2l, @@ -1866,8 +1900,17 @@ struct GateDecompositionPattern final }; } + /** + * Calculate decompositions when three basis gates are required. + * + * Decompose target with :math:`3` uses of the basis. + * + * This is an exact decomposition for supercontrolled basis + * :math:`\sim U_d(\pi/4, b, 0)`, all b, and any target. No guarantees for + * non-supercontrolled basis. + */ [[nodiscard]] std::vector - decomp3SupercontrolledInner(const TwoQubitWeylDecomposition& target) const { + decomp3Supercontrolled(const TwoQubitWeylDecomposition& target) const { return { u3r * target.k2r, u3l * target.k2l, @@ -1880,6 +1923,12 @@ struct GateDecompositionPattern final }; } + /** + * Calculate traces for a combination of the parameters of the canonical + * gates of the target and basis decompositions. + * This can be used to determine the smallest number of basis gates that are + * necessary to construct an equivalent to the canonical gate. + */ [[nodiscard]] std::array traces(TwoQubitWeylDecomposition target) const { return { @@ -1939,9 +1988,9 @@ struct GateDecompositionPattern final OneQubitGateSequence bestCircuit; for (auto targetBasis : targetBasisList) { auto circuit = generateCircuit(targetBasis, unitaryMat, simplify, atol); - helpers::print(circuit.getUnitaryMatrix(), "SANITY CHECK (4.1)", true); + helpers::print(circuit.getUnitaryMatrix(), "SANITY CHECK (4.1)"); helpers::print(helpers::kroneckerProduct(IDENTITY_GATE, unitaryMat), - "SANITY CHECK (4.2)", true); + "SANITY CHECK (4.2)"); assert(circuit.getUnitaryMatrix().isApprox( helpers::kroneckerProduct(IDENTITY_GATE, unitaryMat), SANITY_CHECK_PRECISION)); diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index 5180a7ea9c..d8ab373462 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -216,12 +216,9 @@ inline Eigen::Matrix4 kroneckerProduct(const Eigen::Matrix2& lhs, template inline auto selfAdjointEvd(Eigen::Matrix a) { Eigen::SelfAdjointEigenSolver s; - std::cerr << "=EigIN==\n" << a << "\n========\n" << '\n'; s.compute(a); // TODO: computeDirect is faster auto vecs = s.eigenvectors().eval(); auto vals = s.eigenvalues(); - std::cerr << "=Eigen==\n" << vecs << "\n========\n" << '\n'; - std::cerr << "=Eigen==\n" << vals << "\n========\n" << '\n'; return std::make_pair(vecs, vals); } From 5620b9e476f7fec3d7025a0e132d49212944541b Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 19 Nov 2025 20:19:24 +0100 Subject: [PATCH 084/237] Revert "TMP, needs to be removed; allow OpenQASM export with round-trip pass" This reverts commit 4b54f872a7b934f9d1ed5ae1fe528bcf5af243e3. --- .../Dialect/MQTOpt/Transforms/MQTCoreRoundTrip.cpp | 5 +---- .../MQTOpt/Transforms/ToQuantumComputationPattern.cpp | 11 ++--------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/MQTCoreRoundTrip.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/MQTCoreRoundTrip.cpp index 37a7f20297..276b315024 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/MQTCoreRoundTrip.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/MQTCoreRoundTrip.cpp @@ -33,15 +33,12 @@ struct MQTCoreRoundTrip final : impl::MQTCoreRoundTripBase { // Define the set of patterns to use. mlir::RewritePatternSet patterns(ctx); populateToQuantumComputationPatterns(patterns, circuit); - // populateFromQuantumComputationPatterns(patterns, circuit); + populateFromQuantumComputationPatterns(patterns, circuit); // Apply patterns in an iterative and greedy manner. if (mlir::failed(mlir::applyPatternsGreedily(op, std::move(patterns)))) { signalPassFailure(); } - std::cerr << "======================\n"; - circuit.dumpOpenQASM(std::cerr, false); - std::cerr << "======================\n"; } }; diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/ToQuantumComputationPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/ToQuantumComputationPattern.cpp index 7461645b0c..3387ec26b3 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/ToQuantumComputationPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/ToQuantumComputationPattern.cpp @@ -8,7 +8,6 @@ * Licensed under the MIT License */ -#include "Helpers.h" #include "ir/Definitions.hpp" #include "ir/QuantumComputation.hpp" #include "ir/operations/Control.hpp" @@ -211,9 +210,7 @@ struct ToQuantumComputationPattern final findQubitIndex(val, currentQubitVariables)); } // Get the qubit index of the target qubit (if already collected). - if (!in.empty()) { targetIndex[0] = findQubitIndex(in[0], currentQubitVariables); - } if (in.size() > 1) { targetIndex[1] = findQubitIndex(in[1], currentQubitVariables); } @@ -234,9 +231,7 @@ struct ToQuantumComputationPattern final currentQubitVariables[negCtrlInsIndices[i]] = outs[i + 1 + posCtrlInsIndices.size()]; } - if (!op.getOutQubits().empty()) { - currentQubitVariables[targetIndex[0]] = outs[0]; - } + currentQubitVariables[targetIndex[0]] = outs[0]; if (op.getOutQubits().size() > 1) { currentQubitVariables[targetIndex[1]] = outs[1]; } @@ -258,8 +253,6 @@ struct ToQuantumComputationPattern final parameters.emplace_back(param); } } - auto x = helpers::getParameters(op); - parameters.insert(parameters.end(), x.begin(), x.end()); if (op.getOutQubits().size() > 1) { circuit.emplace_back( @@ -386,7 +379,7 @@ struct ToQuantumComputationPattern final std::string regName; llvm::raw_string_ostream os(regName); - os << "q"; + op.getResult().print(os); circuit.addQubitRegister(numQubits, regName); circuit.addClassicalRegister(numQubits); From 82ba25d043b3d5d9aea55c9d69699e281d9941a1 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 20 Nov 2025 13:05:17 +0100 Subject: [PATCH 085/237] remove armadillo in cmake --- CMakeLists.txt | 18 ------------------ .../Dialect/MQTOpt/Transforms/CMakeLists.txt | 2 +- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 651ea24dbe..86f93ccc8e 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,24 +24,6 @@ include(PackageAddTest) include(Cache) include(AddMQTCoreLibrary) -# include(FetchContent) -# FetchContent_Declare( -# armadillo -# GIT_REPOSITORY https://gitlab.com/conradsnicta/armadillo-code.git -# GIT_TAG 15.0.1 -# ) -# FetchContent_MakeAvailable(armadillo) -find_package(Armadillo 15 REQUIRED) -if(Armadillo_FOUND AND NOT TARGET Armadillo::Armadillo) - add_library(Armadillo::Armadillo INTERFACE IMPORTED) - set_target_properties( - Armadillo::Armadillo - PROPERTIES - INTERFACE_LINK_LIBRARIES "${ARMADILLO_LIBRARIES}" - INTERFACE_INCLUDE_DIRECTORIES "${ARMADILLO_INCLUDE_DIRS}" - ) -endif() - option(BUILD_MQT_CORE_BINDINGS "Build the MQT Core Python bindings" OFF) if(BUILD_MQT_CORE_BINDINGS) # ensure that the BINDINGS option is set diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt b/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt index 2098ff4af0..6badce698e 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt +++ b/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt @@ -7,7 +7,7 @@ # Licensed under the MIT License get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) -set(LIBRARIES ${dialect_libs} MQT::CoreIR MQT::CoreDD Armadillo::Armadillo) +set(LIBRARIES ${dialect_libs} MQT::CoreIR MQT::CoreDD) add_compile_options(-fexceptions) file(GLOB_RECURSE TRANSFORMS_SOURCES *.cpp) From 3d1dc8ab44c57669de0eb3be852960126c3ee5c5 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 20 Nov 2025 13:07:16 +0100 Subject: [PATCH 086/237] more cleanup --- .../Dialect/MQTOpt/Transforms/CMakeLists.txt | 2 +- .../MQTOpt/Transforms/GateDecomposition.cpp | 3 +- .../Transforms/GateDecompositionPattern.cpp | 120 ++- mlir/lib/Dialect/MQTOpt/Transforms/a.cpp | 904 ------------------ 4 files changed, 96 insertions(+), 933 deletions(-) delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/a.cpp diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt b/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt index 6badce698e..e7f1f55dac 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt +++ b/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt @@ -7,7 +7,7 @@ # Licensed under the MIT License get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) -set(LIBRARIES ${dialect_libs} MQT::CoreIR MQT::CoreDD) +set(LIBRARIES ${dialect_libs} MQT::CoreIR) add_compile_options(-fexceptions) file(GLOB_RECURSE TRANSFORMS_SOURCES *.cpp) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp index a5c039568d..7c60ab2597 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp @@ -40,7 +40,8 @@ struct GateDecomposition final config.setUseTopDownTraversal(true); // Apply patterns in an iterative and greedy manner. - if (mlir::failed(mlir::applyPatternsGreedily(op, std::move(patterns), config))) { + if (mlir::failed( + mlir::applyPatternsGreedily(op, std::move(patterns), config))) { signalPassFailure(); } } diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index cb051b1478..8e01af1ddf 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -51,31 +51,49 @@ struct GateDecompositionPattern final ZSX = 11, }; + using QubitId = std::size_t; + /** + * Gate sequence of single-qubit and/or two-qubit gates. + */ struct QubitGateSequence { + /** + * Gate description which should be able to represent every possible + * one-qubit or two-qubit operation. + */ struct Gate { qc::OpType type{qc::I}; llvm::SmallVector parameter; - llvm::SmallVector qubitId = {0}; + llvm::SmallVector qubitId = {0}; }; + /** + * Container sorting the gate sequence in order. + */ std::vector gates; + + fp globalPhase{}; + [[nodiscard]] bool hasGlobalPhase() const { + return std::abs(globalPhase) > DEFAULT_ATOL; + } + + /** + * Calculate complexity of sequence according to getComplexity(). + */ [[nodiscard]] std::size_t complexity() const { - // TODO: caching mechanism + // TODO: caching mechanism? std::size_t c{}; for (auto&& gate : gates) { c += getComplexity(gate.type, gate.qubitId.size()); } if (hasGlobalPhase()) { - // need to add two phase gates if a global phase needs to be applied - c += 2 * getComplexity(qc::P, 1); + // need to add a global phase gate if a global phase needs to be applied + c += getComplexity(qc::GPhase, 1); } return c; } - fp globalPhase{}; - [[nodiscard]] bool hasGlobalPhase() const { - return std::abs(globalPhase) > std::numeric_limits::epsilon(); - } - + /** + * Calculate overall unitary matrix of the sequence. + */ [[nodiscard]] matrix4x4 getUnitaryMatrix() const { matrix4x4 unitaryMatrix = helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); @@ -91,6 +109,11 @@ struct GateDecompositionPattern final using OneQubitGateSequence = QubitGateSequence; using TwoQubitGateSequence = QubitGateSequence; + /** + * Initialize pattern with a set of basis gates and euler bases. + * The best combination of (basis gate, euler basis) will be evaluated for + * each decomposition. + */ explicit GateDecompositionPattern( mlir::MLIRContext* context, llvm::SmallVector basisGate, @@ -119,13 +142,12 @@ struct GateDecompositionPattern final return mlir::failure(); } if (series.isSingleQubitSeries()) { - // only a single-qubit series + // only a single-qubit series; + // single-qubit euler decomposition is more efficient return mlir::failure(); } matrix4x4 unitaryMatrix = series.getUnitaryMatrix(); - helpers::print(unitaryMatrix, "UNITARY MATRIX"); - auto targetDecomposition = TwoQubitWeylDecomposition::create( unitaryMatrix, DEFAULT_FIDELITY, std::nullopt); @@ -174,14 +196,29 @@ struct GateDecompositionPattern final } struct TwoQubitSeries { + /** + * Complexity of series using getComplexity() for each gate. + */ std::size_t complexity{0}; + /** + * Qubits that are the input for the series. + * First qubit will always be set, second qubit may be equal to + * mlir::Value{} if the series consists of only single-qubit gates. + * + * All + */ std::array inQubits; + /** + * Qubits that are the input for the series. + * First qubit will always be set, second qubit may be equal to + * mlir::Value{} if the series consists of only single-qubit gates. + */ std::array outQubits; fp globalPhase{}; struct Gate { UnitaryInterface op; - llvm::SmallVector qubitIds; + llvm::SmallVector qubitIds; }; llvm::SmallVector gates; @@ -261,7 +298,7 @@ struct GateDecompositionPattern final complexity += getComplexity(helpers::getQcType(initialOperation), in.size()); - // TODO: necessary when using non-global phase gates? + // TODO: necessary? for (auto&& globalPhaseOp : initialOperation->getBlock()->getOps()) { globalPhase += helpers::getParameters(globalPhaseOp)[0]; @@ -279,7 +316,7 @@ struct GateDecompositionPattern final throw std::logic_error{"Operand of single-qubit op and user of " "qubit is not in current outQubits"}; } - std::size_t qubitId = std::distance(outQubits.begin(), it); + QubitId qubitId = std::distance(outQubits.begin(), it); *it = nextGate->getResult(0); gates.push_back({.op = nextGate, .qubitIds = {qubitId}}); @@ -309,10 +346,10 @@ struct GateDecompositionPattern final ? *firstQubitIt : *secondQubitIt); // new qubit ID based on position in outQubits - auto newInQubitId = std::distance(outQubits.begin(), it); + QubitId newInQubitId = std::distance(outQubits.begin(), it); // position in operation input; since there are only two qubits, it must // be the "not old" one - auto newOpInQubitId = 1 - std::distance(opInQubits.begin(), it2); + QubitId newOpInQubitId = 1 - std::distance(opInQubits.begin(), it2); // update inQubit and update dangling iterator, then proceed as usual inQubits[newInQubitId] = opInQubits[newOpInQubitId]; @@ -323,9 +360,8 @@ struct GateDecompositionPattern final // possible to collect other single-qubit operations backtrackSingleQubitSeries(newInQubitId); } - std::size_t firstQubitId = std::distance(outQubits.begin(), firstQubitIt); - std::size_t secondQubitId = - std::distance(outQubits.begin(), secondQubitIt); + QubitId firstQubitId = std::distance(outQubits.begin(), firstQubitIt); + QubitId secondQubitId = std::distance(outQubits.begin(), secondQubitIt); *firstQubitIt = nextGate->getResult(0); *secondQubitIt = nextGate->getResult(1); @@ -342,7 +378,7 @@ struct GateDecompositionPattern final * gate could have previous single-qubit operations that can be incorporated * in the series. */ - void backtrackSingleQubitSeries(std::size_t qubitId) { + void backtrackSingleQubitSeries(QubitId qubitId) { auto prependSingleQubitGate = [&](UnitaryInterface op) { inQubits[qubitId] = op.getAllInQubits()[0]; gates.insert(gates.begin(), {.op = op, .qubitIds = {qubitId}}); @@ -409,7 +445,7 @@ struct GateDecompositionPattern final auto inQubits = series.inQubits; auto updateInQubits = - [&inQubits](const llvm::SmallVector& qubitIds, + [&inQubits](const llvm::SmallVector& qubitIds, auto&& newGate) { // TODO: need to handle controls differently? auto results = newGate.getAllOutQubits(); @@ -852,11 +888,11 @@ struct GateDecompositionPattern final if (gate.qubitId.size() == 2) { if (gate.type == qc::X) { // controlled X (CX) - if (gate.qubitId == llvm::SmallVector{0, 1}) { + if (gate.qubitId == llvm::SmallVector{0, 1}) { return matrix4x4{ {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}}; } - if (gate.qubitId == llvm::SmallVector{1, 0}) { + if (gate.qubitId == llvm::SmallVector{1, 0}) { return matrix4x4{ {1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}, {0, 1, 0, 0}}; } @@ -903,6 +939,17 @@ struct GateDecompositionPattern final fp calculatedFidelity; // actual fidelity of decomposition matrix4x4 unitaryMatrix; // original matrix for this decomposition + /** + * Create Weyl decomposition. + * + * @param unitaryMatrix Matrix of the two-qubit operation/series to be + * decomposed. + * @param fidelity Tolerance to assume a specialization which is used to + * reduce the number of parameters required by the canonical + * gate and thus potentially decreasing the number of basis + * gates. + * @param specialization Force the use this specialization. + */ static TwoQubitWeylDecomposition create(matrix4x4 unitaryMatrix, std::optional fidelity, std::optional specialization) { @@ -1550,6 +1597,10 @@ struct GateDecompositionPattern final static constexpr auto DEFAULT_FIDELITY = 1.0 - 1e-15; static constexpr auto DEFAULT_ATOL = 1e-12; + /** + * Decomposer that must be initialized with a two-qubit basis gate that will + * be used to generate a circuit equivalent to a canonical gate (RXX+RYY+RZZ). + */ struct TwoQubitBasisDecomposer { QubitGateSequence::Gate basisGate; fp basisFidelity; @@ -1720,6 +1771,22 @@ struct GateDecompositionPattern final }; } + /** + * Perform decomposition using the basis gate of this decomposer. + * + * @param targetDecomposition Prepared Weyl decomposition of unitary matrix + * to be decomposed. + * @param target1qEulerBases List of euler bases that should be tried out to + * find the best one for each euler decomposition. + * All bases will be mixed to get the best overall + * result. + * @param basisFidelity Fidelity for lowering the number of basis gates + * required + * @param approximate If true, use basisFidelity or, if std::nullopt, use + * basisFidelity of this decomposer. If false, fidelity + * of 1.0 will be assumed. + * @param numBasisGateUses Force use of given number of basis gates. + */ [[nodiscard]] std::optional twoQubitDecompose(const TwoQubitWeylDecomposition& targetDecomposition, const llvm::SmallVector& target1qEulerBases, @@ -1788,7 +1855,7 @@ struct GateDecompositionPattern final gates.globalPhase += qc::PI; } - auto addEulerDecomposition = [&](std::size_t index, std::size_t qubitId) { + auto addEulerDecomposition = [&](std::size_t index, QubitId qubitId) { if (auto&& eulerDecomp = eulerDecompositions[index]) { for (auto&& gate : eulerDecomp->gates) { gates.gates.push_back({.type = gate.type, @@ -1974,8 +2041,7 @@ struct GateDecompositionPattern final static OneQubitGateSequence unitaryToGateSequenceInner( matrix2x2 unitaryMat, - const llvm::SmallVector& targetBasisList, - std::size_t /*qubit*/, + const llvm::SmallVector& targetBasisList, QubitId /*qubit*/, const std::vector>& /*error_map*/, // TODO: remove error_map+qubit for platform // independence diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/a.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/a.cpp deleted file mode 100644 index a4da79e473..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/a.cpp +++ /dev/null @@ -1,904 +0,0 @@ -#include -#include -#include -#include -#include - -using fp = long double; -using qfp = std::complex; -using diagonal4x4 = std::array; -using vector2d = std::vector; -using matrix2x2 = std::array; -using matrix4x4 = std::array; - -using logical = bool; -using integer = int; -using doublecomplex = qfp; - -matrix4x4 zgemm2(matrix4x4 a, matrix4x4 b) { - matrix4x4 c__; - // a_dim1 = *lda; - // a_offset = 1 + a_dim1; - // a -= a_offset; - // b_dim1 = *ldb; - // b_offset = 1 + b_dim1; - // b -= b_offset; - // c_dim1 = *ldc; - // c_offset = 1 + c_dim1; - // c__ -= c_offset; - - int i__1 = 4; - int i__3{}; - qfp z__1; - qfp z__2; - qfp temp; - qfp alpha {1.0, 0.0}; - for (auto j = 0; j < i__1; ++j) { - for (auto i__ = 0; i__ < 4; ++i__) { - auto i__3 = i__ + j * 4; - c__[i__3].real(0.), c__[i__3].imag(0.); - } - int i__2 = 4; - for (auto l = 0; l < i__2; ++l) { - i__3 = l + j * 4; - if (b[i__3].real() != 0. || b[i__3].imag() != 0.) { - i__3 = l + j * 4; - z__1.real(alpha.real() * b[i__3].real() - - alpha.imag() * b[i__3].imag()), - z__1.imag(alpha.real() * b[i__3].imag() + - alpha.imag() * b[i__3].real()); - temp.real(z__1.real()), temp.imag(z__1.imag()); - i__3 = 4; - for (auto i__ = 0; i__ < i__3; ++i__) { - auto i__4 = i__ + j * 4; - auto i__5 = i__ + j * 4; - auto i__6 = i__ + l * 4; - z__2.real(temp.real() * a[i__6].real() - - temp.imag() * a[i__6].imag()), - z__2.imag(temp.real() * a[i__6].imag() + - temp.imag() * a[i__6].real()); - z__1.real(c__[i__5].real() + z__2.real()), - z__1.imag(c__[i__5].imag() + z__2.imag()); - c__[i__4].real(z__1.real()), c__[i__4].imag(z__1.imag()); - /* L70: */ - } - } - /* L80: */ - } - /* L90: */ - } - return c__; -} - -int zgemm_(char* transa, char* transb, integer* m, integer* n, integer* k, - doublecomplex* alpha, matrix4x4 a, integer* lda, - matrix4x4 b, integer* ldb, doublecomplex* beta, - matrix4x4& c__, integer* ldc); -matrix4x4 zgemm(matrix4x4 a, matrix4x4 b) { - qfp alpha{1.0, 0.0}; - qfp beta{1.0, 0.0}; - int dimension = 4; - matrix4x4 result{}; - // zgemm_("n", "n", &dimension, &dimension, &dimension, &alpha, a.data(), - // &dimension, b.data(), &dimension, &beta, result.data(), &dimension); - zgemm_("N", "N", &dimension, &dimension, &dimension, &alpha, a, - &dimension, b, &dimension, &beta, result, &dimension); - return result; -} - -void d_cnjg(doublecomplex* r, doublecomplex* z) { *r = std::conj(*z); } - -/* Subroutine */ int zgemm_(char* transa, char* transb, integer* m, integer* n, - integer* k, doublecomplex* alpha, matrix4x4 a, - integer* lda, matrix4x4 b, integer* ldb, - doublecomplex* beta, matrix4x4& c__, - integer* ldc) { - /* System generated locals */ - integer a_dim1, a_offset, b_dim1, b_offset, c_dim1, c_offset, i__1, i__2, - i__3, i__4, i__5, i__6; - doublecomplex z__1, z__2, z__3, z__4; - - /* Local variables */ - static integer i__, j, l, info; - static logical nota, notb; - static doublecomplex temp; - static logical conja, conjb; - static integer nrowa, nrowb; - - /* - Purpose - ======= - - ZGEMM performs one of the matrix-matrix operations - - C := alpha*op( A )*op( B ) + beta*C, - - where op( X ) is one of - - op( X ) = X or op( X ) = X' or op( X ) = conjg( X' ), - - alpha and beta are scalars, and A, B and C are matrices, with op( A ) - an m by k matrix, op( B ) a k by n matrix and C an m by n matrix. - - Arguments - ========== - - TRANSA - CHARACTER*1. - On entry, TRANSA specifies the form of op( A ) to be used in - the matrix multiplication as follows: - - TRANSA = 'N' or 'n', op( A ) = A. - - TRANSA = 'T' or 't', op( A ) = A'. - - TRANSA = 'C' or 'c', op( A ) = conjg( A' ). - - Unchanged on exit. - - TRANSB - CHARACTER*1. - On entry, TRANSB specifies the form of op( B ) to be used in - the matrix multiplication as follows: - - TRANSB = 'N' or 'n', op( B ) = B. - - TRANSB = 'T' or 't', op( B ) = B'. - - TRANSB = 'C' or 'c', op( B ) = conjg( B' ). - - Unchanged on exit. - - M - INTEGER. - On entry, M specifies the number of rows of the matrix - op( A ) and of the matrix C. M must be at least zero. - Unchanged on exit. - - N - INTEGER. - On entry, N specifies the number of columns of the matrix - op( B ) and the number of columns of the matrix C. N must be - at least zero. - Unchanged on exit. - - K - INTEGER. - On entry, K specifies the number of columns of the matrix - op( A ) and the number of rows of the matrix op( B ). K must - be at least zero. - Unchanged on exit. - - ALPHA - COMPLEX*16 . - On entry, ALPHA specifies the scalar alpha. - Unchanged on exit. - - A - COMPLEX*16 array of DIMENSION ( LDA, ka ), where ka is - k when TRANSA = 'N' or 'n', and is m otherwise. - Before entry with TRANSA = 'N' or 'n', the leading m by k - part of the array A must contain the matrix A, otherwise - the leading k by m part of the array A must contain the - matrix A. - Unchanged on exit. - - LDA - INTEGER. - On entry, LDA specifies the first dimension of A as declared - in the calling (sub) program. When TRANSA = 'N' or 'n' then - LDA must be at least max( 1, m ), otherwise LDA must be at - least max( 1, k ). - Unchanged on exit. - - B - COMPLEX*16 array of DIMENSION ( LDB, kb ), where kb is - n when TRANSB = 'N' or 'n', and is k otherwise. - Before entry with TRANSB = 'N' or 'n', the leading k by n - part of the array B must contain the matrix B, otherwise - the leading n by k part of the array B must contain the - matrix B. - Unchanged on exit. - - LDB - INTEGER. - On entry, LDB specifies the first dimension of B as declared - in the calling (sub) program. When TRANSB = 'N' or 'n' then - LDB must be at least max( 1, k ), otherwise LDB must be at - least max( 1, n ). - Unchanged on exit. - - BETA - COMPLEX*16 . - On entry, BETA specifies the scalar beta. When BETA is - supplied as zero then C need not be set on input. - Unchanged on exit. - - C - COMPLEX*16 array of DIMENSION ( LDC, n ). - Before entry, the leading m by n part of the array C must - contain the matrix C, except when beta is zero, in which - case C need not be set on entry. - On exit, the array C is overwritten by the m by n matrix - ( alpha*op( A )*op( B ) + beta*C ). - - LDC - INTEGER. - On entry, LDC specifies the first dimension of C as declared - in the calling (sub) program. LDC must be at least - max( 1, m ). - Unchanged on exit. - - Further Details - =============== - - Level 3 Blas routine. - - -- Written on 8-February-1989. - Jack Dongarra, Argonne National Laboratory. - Iain Duff, AERE Harwell. - Jeremy Du Croz, Numerical Algorithms Group Ltd. - Sven Hammarling, Numerical Algorithms Group Ltd. - - ===================================================================== - - - Set NOTA and NOTB as true if A and B respectively are not - conjugated or transposed, set CONJA and CONJB as true if A and - B respectively are to be transposed but not conjugated and set - NROWA and NROWB as the number of rows and columns of A - and the number of rows of B respectively. - */ - - auto lsame_ = [](char* a, char* b) { - return std::string{a} == std::string{b}; - }; - - /* Parameter adjustments */ - a_dim1 = *lda; - a_offset = 1 + a_dim1; - // a -= a_offset; - b_dim1 = *ldb; - b_offset = 1 + b_dim1; - // b -= b_offset; - c_dim1 = *ldc; - c_offset = 1 + c_dim1; - // c__ -= c_offset; - - /* Function Body */ - nota = lsame_(transa, "N"); - notb = lsame_(transb, "N"); - conja = lsame_(transa, "C"); - conjb = lsame_(transb, "C"); - if (nota) { - nrowa = *m; - } else { - nrowa = *k; - } - if (notb) { - nrowb = *k; - } else { - nrowb = *n; - } - - /* Test the input parameters. */ - - info = 0; - if (!nota && !conja && !lsame_(transa, "T")) { - info = 1; - } else if (!notb && !conjb && !lsame_(transb, "T")) { - info = 2; - } else if (*m < 0) { - info = 3; - } else if (*n < 0) { - info = 4; - } else if (*k < 0) { - info = 5; - } else if (*lda < std::max(1, nrowa)) { - info = 8; - } else if (*ldb < std::max(1, nrowb)) { - info = 10; - } else if (*ldc < std::max(1, *m)) { - info = 13; - } - if (info != 0) { - // xerbla_("ZGEMM ", &info); - return 0; - } - - /* Quick return if possible. */ - - if (*m == 0 || *n == 0 || - (alpha->real() == 0. && alpha->imag() == 0. || *k == 0) && - (beta->real() == 1. && beta->imag() == 0.)) { - return 0; - } - - /* And when alpha.eq.zero. */ - - if (alpha->real() == 0. && alpha->imag() == 0.) { - if (beta->real() == 0. && beta->imag() == 0.) { - i__1 = *n; - for (j = 1; j <= i__1; ++j) { - i__2 = *m; - for (i__ = 1; i__ <= i__2; ++i__) { - i__3 = i__ + j * c_dim1; - c__[i__3].real(0.), c__[i__3].imag(0.); - /* L10: */ - } - /* L20: */ - } - } else { - i__1 = *n; - for (j = 1; j <= i__1; ++j) { - i__2 = *m; - for (i__ = 1; i__ <= i__2; ++i__) { - i__3 = i__ + j * c_dim1; - i__4 = i__ + j * c_dim1; - z__1.real(beta->real() * c__[i__4].real() - - beta->imag() * c__[i__4].imag()), - z__1.imag(beta->real() * c__[i__4].imag() + - beta->imag() * c__[i__4].real()); - c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); - /* L30: */ - } - /* L40: */ - } - } - return 0; - } - - /* Start the operations. */ - - if (notb) { - if (nota) { - - /* Form C := alpha*A*B + beta*C. */ - - i__1 = *n; - for (j = 1; j <= i__1; ++j) { - if (beta->real() == 0. && beta->imag() == 0.) { - i__2 = *m; - for (i__ = 1; i__ <= i__2; ++i__) { - i__3 = i__ + j * c_dim1; - c__[i__3 - c_offset].real(0.), c__[i__3 - c_offset].imag(0.); - /* L50: */ - } - } else if (beta->real() != 1. || beta->imag() != 0.) { - i__2 = *m; - for (i__ = 1; i__ <= i__2; ++i__) { - i__3 = i__ + j * c_dim1; - i__4 = i__ + j * c_dim1; - z__1.real(beta->real() * c__[i__4 - c_offset].real() - - beta->imag() * c__[i__4 - c_offset].imag()), - z__1.imag(beta->real() * c__[i__4 - c_offset].imag() + - beta->imag() * c__[i__4 - c_offset].real()); - c__[i__3 - c_offset].real(z__1.real()), c__[i__3 - c_offset].imag(z__1.imag()); - /* L60: */ - } - } - i__2 = *k; - for (l = 1; l <= i__2; ++l) { - i__3 = l + j * b_dim1; - if (b[i__3 - b_offset].real() != 0. || b[i__3 - b_offset].imag() != 0.) { - i__3 = l + j * b_dim1; - z__1.real(alpha->real() * b[i__3 - b_offset].real() - - alpha->imag() * b[i__3 - b_offset].imag()), - z__1.imag(alpha->real() * b[i__3 - b_offset].imag() + - alpha->imag() * b[i__3 - b_offset].real()); - temp.real(z__1.real()), temp.imag(z__1.imag()); - i__3 = *m; - for (i__ = 1; i__ <= i__3; ++i__) { - i__4 = i__ + j * c_dim1; - i__5 = i__ + j * c_dim1; - i__6 = i__ + l * a_dim1; - z__2.real(temp.real() * a[i__6 - a_offset].real() - - temp.imag() * a[i__6 - a_offset].imag()), - z__2.imag(temp.real() * a[i__6 - a_offset].imag() + - temp.imag() * a[i__6 - a_offset].real()); - z__1.real(c__[i__5 - c_offset].real() + z__2.real()), - z__1.imag(c__[i__5 - c_offset].imag() + z__2.imag()); - c__[i__4 - c_offset].real(z__1.real()), c__[i__4 - c_offset].imag(z__1.imag()); - /* L70: */ - } - } - /* L80: */ - } - /* L90: */ - } - } else if (conja) { - - /* Form C := alpha*conjg( A' )*B + beta*C. */ - - i__1 = *n; - for (j = 1; j <= i__1; ++j) { - i__2 = *m; - for (i__ = 1; i__ <= i__2; ++i__) { - temp.real(0.), temp.imag(0.); - i__3 = *k; - for (l = 1; l <= i__3; ++l) { - d_cnjg(&z__3, &a[l + i__ * a_dim1]); - i__4 = l + j * b_dim1; - z__2.real(z__3.real() * b[i__4].real() - - z__3.imag() * b[i__4].imag()), - z__2.imag(z__3.real() * b[i__4].imag() + - z__3.imag() * b[i__4].real()); - z__1.real(temp.real() + z__2.real()), - z__1.imag(temp.imag() + z__2.imag()); - temp.real(z__1.real()), temp.imag(z__1.imag()); - /* L100: */ - } - if (beta->real() == 0. && beta->imag() == 0.) { - i__3 = i__ + j * c_dim1; - z__1.real(alpha->real() * temp.real() - - alpha->imag() * temp.imag()), - z__1.imag(alpha->real() * temp.imag() + - alpha->imag() * temp.real()); - c__[i__3 - c_offset].real(z__1.real()), c__[i__3 - c_offset].imag(z__1.imag()); - } else { - i__3 = i__ + j * c_dim1; - z__2.real(alpha->real() * temp.real() - - alpha->imag() * temp.imag()), - z__2.imag(alpha->real() * temp.imag() + - alpha->imag() * temp.real()); - i__4 = i__ + j * c_dim1; - z__3.real(beta->real() * c__[i__4 - c_offset].real() - - beta->imag() * c__[i__4 - c_offset].imag()), - z__3.imag(beta->real() * c__[i__4 - c_offset].imag() + - beta->imag() * c__[i__4 - c_offset].real()); - z__1.real(z__2.real() + z__3.real()), - z__1.imag(z__2.imag() + z__3.imag()); - c__[i__3 - c_offset].real(z__1.real()), c__[i__3 - c_offset].imag(z__1.imag()); - } - /* L110: */ - } - /* L120: */ - } - } else { - - /* Form C := alpha*A'*B + beta*C */ - - i__1 = *n; - for (j = 1; j <= i__1; ++j) { - i__2 = *m; - for (i__ = 1; i__ <= i__2; ++i__) { - temp.real(0.), temp.imag(0.); - i__3 = *k; - for (l = 1; l <= i__3; ++l) { - i__4 = l + i__ * a_dim1; - i__5 = l + j * b_dim1; - z__2.real(a[i__4].real() * b[i__5].real() - - a[i__4].imag() * b[i__5].imag()), - z__2.imag(a[i__4].real() * b[i__5].imag() + - a[i__4].imag() * b[i__5].real()); - z__1.real(temp.real() + z__2.real()), - z__1.imag(temp.imag() + z__2.imag()); - temp.real(z__1.real()), temp.imag(z__1.imag()); - /* L130: */ - } - if (beta->real() == 0. && beta->imag() == 0.) { - i__3 = i__ + j * c_dim1; - z__1.real(alpha->real() * temp.real() - - alpha->imag() * temp.imag()), - z__1.imag(alpha->real() * temp.imag() + - alpha->imag() * temp.real()); - c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); - } else { - i__3 = i__ + j * c_dim1; - z__2.real(alpha->real() * temp.real() - - alpha->imag() * temp.imag()), - z__2.imag(alpha->real() * temp.imag() + - alpha->imag() * temp.real()); - i__4 = i__ + j * c_dim1; - z__3.real(beta->real() * c__[i__4].real() - - beta->imag() * c__[i__4].imag()), - z__3.imag(beta->real() * c__[i__4].imag() + - beta->imag() * c__[i__4].real()); - z__1.real(z__2.real() + z__3.real()), - z__1.imag(z__2.imag() + z__3.imag()); - c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); - } - /* L140: */ - } - /* L150: */ - } - } - } else if (nota) { - if (conjb) { - - /* Form C := alpha*A*conjg( B' ) + beta*C. */ - - i__1 = *n; - for (j = 1; j <= i__1; ++j) { - if (beta->real() == 0. && beta->imag() == 0.) { - i__2 = *m; - for (i__ = 1; i__ <= i__2; ++i__) { - i__3 = i__ + j * c_dim1; - c__[i__3].real(0.), c__[i__3].imag(0.); - /* L160: */ - } - } else if (beta->real() != 1. || beta->imag() != 0.) { - i__2 = *m; - for (i__ = 1; i__ <= i__2; ++i__) { - i__3 = i__ + j * c_dim1; - i__4 = i__ + j * c_dim1; - z__1.real(beta->real() * c__[i__4].real() - - beta->imag() * c__[i__4].imag()), - z__1.imag(beta->real() * c__[i__4].imag() + - beta->imag() * c__[i__4].real()); - c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); - /* L170: */ - } - } - i__2 = *k; - for (l = 1; l <= i__2; ++l) { - i__3 = j + l * b_dim1; - if (b[i__3].real() != 0. || b[i__3].imag() != 0.) { - d_cnjg(&z__2, &b[j + l * b_dim1]); - z__1.real(alpha->real() * z__2.real() - - alpha->imag() * z__2.imag()), - z__1.imag(alpha->real() * z__2.imag() + - alpha->imag() * z__2.real()); - temp.real(z__1.real()), temp.imag(z__1.imag()); - i__3 = *m; - for (i__ = 1; i__ <= i__3; ++i__) { - i__4 = i__ + j * c_dim1; - i__5 = i__ + j * c_dim1; - i__6 = i__ + l * a_dim1; - z__2.real(temp.real() * a[i__6].real() - - temp.imag() * a[i__6].imag()), - z__2.imag(temp.real() * a[i__6].imag() + - temp.imag() * a[i__6].real()); - z__1.real(c__[i__5].real() + z__2.real()), - z__1.imag(c__[i__5].imag() + z__2.imag()); - c__[i__4].real(z__1.real()), c__[i__4].imag(z__1.imag()); - /* L180: */ - } - } - /* L190: */ - } - /* L200: */ - } - } else { - - /* Form C := alpha*A*B' + beta*C */ - - i__1 = *n; - for (j = 1; j <= i__1; ++j) { - if (beta->real() == 0. && beta->imag() == 0.) { - i__2 = *m; - for (i__ = 1; i__ <= i__2; ++i__) { - i__3 = i__ + j * c_dim1; - c__[i__3].real(0.), c__[i__3].imag(0.); - /* L210: */ - } - } else if (beta->real() != 1. || beta->imag() != 0.) { - i__2 = *m; - for (i__ = 1; i__ <= i__2; ++i__) { - i__3 = i__ + j * c_dim1; - i__4 = i__ + j * c_dim1; - z__1.real(beta->real() * c__[i__4].real() - - beta->imag() * c__[i__4].imag()), - z__1.imag(beta->real() * c__[i__4].imag() + - beta->imag() * c__[i__4].real()); - c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); - /* L220: */ - } - } - i__2 = *k; - for (l = 1; l <= i__2; ++l) { - i__3 = j + l * b_dim1; - if (b[i__3].real() != 0. || b[i__3].imag() != 0.) { - i__3 = j + l * b_dim1; - z__1.real(alpha->real() * b[i__3].real() - - alpha->imag() * b[i__3].imag()), - z__1.imag(alpha->real() * b[i__3].imag() + - alpha->imag() * b[i__3].real()); - temp.real(z__1.real()), temp.imag(z__1.imag()); - i__3 = *m; - for (i__ = 1; i__ <= i__3; ++i__) { - i__4 = i__ + j * c_dim1; - i__5 = i__ + j * c_dim1; - i__6 = i__ + l * a_dim1; - z__2.real(temp.real() * a[i__6].real() - - temp.imag() * a[i__6].imag()), - z__2.imag(temp.real() * a[i__6].imag() + - temp.imag() * a[i__6].real()); - z__1.real(c__[i__5].real() + z__2.real()), - z__1.imag(c__[i__5].imag() + z__2.imag()); - c__[i__4].real(z__1.real()), c__[i__4].imag(z__1.imag()); - /* L230: */ - } - } - /* L240: */ - } - /* L250: */ - } - } - } else if (conja) { - if (conjb) { - - /* Form C := alpha*conjg( A' )*conjg( B' ) + beta*C. */ - - i__1 = *n; - for (j = 1; j <= i__1; ++j) { - i__2 = *m; - for (i__ = 1; i__ <= i__2; ++i__) { - temp.real(0.), temp.imag(0.); - i__3 = *k; - for (l = 1; l <= i__3; ++l) { - d_cnjg(&z__3, &a[l + i__ * a_dim1]); - d_cnjg(&z__4, &b[j + l * b_dim1]); - z__2.real(z__3.real() * z__4.real() - z__3.imag() * z__4.imag()), - z__2.imag(z__3.real() * z__4.imag() + - z__3.imag() * z__4.real()); - z__1.real(temp.real() + z__2.real()), - z__1.imag(temp.imag() + z__2.imag()); - temp.real(z__1.real()), temp.imag(z__1.imag()); - /* L260: */ - } - if (beta->real() == 0. && beta->imag() == 0.) { - i__3 = i__ + j * c_dim1; - z__1.real(alpha->real() * temp.real() - - alpha->imag() * temp.imag()), - z__1.imag(alpha->real() * temp.imag() + - alpha->imag() * temp.real()); - c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); - } else { - i__3 = i__ + j * c_dim1; - z__2.real(alpha->real() * temp.real() - - alpha->imag() * temp.imag()), - z__2.imag(alpha->real() * temp.imag() + - alpha->imag() * temp.real()); - i__4 = i__ + j * c_dim1; - z__3.real(beta->real() * c__[i__4].real() - - beta->imag() * c__[i__4].imag()), - z__3.imag(beta->real() * c__[i__4].imag() + - beta->imag() * c__[i__4].real()); - z__1.real(z__2.real() + z__3.real()), - z__1.imag(z__2.imag() + z__3.imag()); - c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); - } - /* L270: */ - } - /* L280: */ - } - } else { - - /* Form C := alpha*conjg( A' )*B' + beta*C */ - - i__1 = *n; - for (j = 1; j <= i__1; ++j) { - i__2 = *m; - for (i__ = 1; i__ <= i__2; ++i__) { - temp.real(0.), temp.imag(0.); - i__3 = *k; - for (l = 1; l <= i__3; ++l) { - d_cnjg(&z__3, &a[l + i__ * a_dim1]); - i__4 = j + l * b_dim1; - z__2.real(z__3.real() * b[i__4].real() - - z__3.imag() * b[i__4].imag()), - z__2.imag(z__3.real() * b[i__4].imag() + - z__3.imag() * b[i__4].real()); - z__1.real(temp.real() + z__2.real()), - z__1.imag(temp.imag() + z__2.imag()); - temp.real(z__1.real()), temp.imag(z__1.imag()); - /* L290: */ - } - if (beta->real() == 0. && beta->imag() == 0.) { - i__3 = i__ + j * c_dim1; - z__1.real(alpha->real() * temp.real() - - alpha->imag() * temp.imag()), - z__1.imag(alpha->real() * temp.imag() + - alpha->imag() * temp.real()); - c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); - } else { - i__3 = i__ + j * c_dim1; - z__2.real(alpha->real() * temp.real() - - alpha->imag() * temp.imag()), - z__2.imag(alpha->real() * temp.imag() + - alpha->imag() * temp.real()); - i__4 = i__ + j * c_dim1; - z__3.real(beta->real() * c__[i__4].real() - - beta->imag() * c__[i__4].imag()), - z__3.imag(beta->real() * c__[i__4].imag() + - beta->imag() * c__[i__4].real()); - z__1.real(z__2.real() + z__3.real()), - z__1.imag(z__2.imag() + z__3.imag()); - c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); - } - /* L300: */ - } - /* L310: */ - } - } - } else { - if (conjb) { - - /* Form C := alpha*A'*conjg( B' ) + beta*C */ - - i__1 = *n; - for (j = 1; j <= i__1; ++j) { - i__2 = *m; - for (i__ = 1; i__ <= i__2; ++i__) { - temp.real(0.), temp.imag(0.); - i__3 = *k; - for (l = 1; l <= i__3; ++l) { - i__4 = l + i__ * a_dim1; - d_cnjg(&z__3, &b[j + l * b_dim1]); - z__2.real(a[i__4].real() * z__3.real() - - a[i__4].imag() * z__3.imag()), - z__2.imag(a[i__4].real() * z__3.imag() + - a[i__4].imag() * z__3.real()); - z__1.real(temp.real() + z__2.real()), - z__1.imag(temp.imag() + z__2.imag()); - temp.real(z__1.real()), temp.imag(z__1.imag()); - /* L320: */ - } - if (beta->real() == 0. && beta->imag() == 0.) { - i__3 = i__ + j * c_dim1; - z__1.real(alpha->real() * temp.real() - - alpha->imag() * temp.imag()), - z__1.imag(alpha->real() * temp.imag() + - alpha->imag() * temp.real()); - c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); - } else { - i__3 = i__ + j * c_dim1; - z__2.real(alpha->real() * temp.real() - - alpha->imag() * temp.imag()), - z__2.imag(alpha->real() * temp.imag() + - alpha->imag() * temp.real()); - i__4 = i__ + j * c_dim1; - z__3.real(beta->real() * c__[i__4].real() - - beta->imag() * c__[i__4].imag()), - z__3.imag(beta->real() * c__[i__4].imag() + - beta->imag() * c__[i__4].real()); - z__1.real(z__2.real() + z__3.real()), - z__1.imag(z__2.imag() + z__3.imag()); - c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); - } - /* L330: */ - } - /* L340: */ - } - } else { - - /* Form C := alpha*A'*B' + beta*C */ - - i__1 = *n; - for (j = 1; j <= i__1; ++j) { - i__2 = *m; - for (i__ = 1; i__ <= i__2; ++i__) { - temp.real(0.), temp.imag(0.); - i__3 = *k; - for (l = 1; l <= i__3; ++l) { - i__4 = l + i__ * a_dim1; - i__5 = j + l * b_dim1; - z__2.real(a[i__4].real() * b[i__5].real() - - a[i__4].imag() * b[i__5].imag()), - z__2.imag(a[i__4].real() * b[i__5].imag() + - a[i__4].imag() * b[i__5].real()); - z__1.real(temp.real() + z__2.real()), - z__1.imag(temp.imag() + z__2.imag()); - temp.real(z__1.real()), temp.imag(z__1.imag()); - /* L350: */ - } - if (beta->real() == 0. && beta->imag() == 0.) { - i__3 = i__ + j * c_dim1; - z__1.real(alpha->real() * temp.real() - - alpha->imag() * temp.imag()), - z__1.imag(alpha->real() * temp.imag() + - alpha->imag() * temp.real()); - c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); - } else { - i__3 = i__ + j * c_dim1; - z__2.real(alpha->real() * temp.real() - - alpha->imag() * temp.imag()), - z__2.imag(alpha->real() * temp.imag() + - alpha->imag() * temp.real()); - i__4 = i__ + j * c_dim1; - z__3.real(beta->real() * c__[i__4].real() - - beta->imag() * c__[i__4].imag()), - z__3.imag(beta->real() * c__[i__4].imag() + - beta->imag() * c__[i__4].real()); - z__1.real(z__2.real() + z__3.real()), - z__1.imag(z__2.imag() + z__3.imag()); - c__[i__3].real(z__1.real()), c__[i__3].imag(z__1.imag()); - } - /* L360: */ - } - /* L370: */ - } - } - } - - return 0; - - /* End of ZGEMM . */ - -} /* zgemm_ */ - -template void print(std::array, N> matrix) { - int i{}; - for (auto&& a : matrix) { - std::cerr << std::setprecision(50) << a.real() << '+' << a.imag() << "i, "; - if (++i % 4 == 0) { - std::cerr << '\n'; - } - } - std::cerr << '\n'; -} - -auto mul(qfp a, qfp b) { - return qfp((a.real() * b.real() - a.imag() * b.imag()), - (a.real() * b.imag() + a.imag() * b.real())); -} - -// Function to perform SYRK (Symmetric Rank-K update) on 4x4 matrices stored in -// std::array -matrix4x4 syrk(bool upper, fp alpha, const matrix4x4& A, fp beta) { - matrix4x4 C; - // Iterate over the matrix rows and columns - for (size_t i = 0; i < 4; ++i) { - for (size_t j = (upper ? i : 0); j < 4; ++j) { - // Compute the dot product A[i, :] * A[j, :] (real and imaginary - // separately) - qfp sum{0.0, 0.0}; // Initialize sum as a complex number (0.0 + 0.0i) - - for (size_t k = 0; k < 4; ++k) { - sum += A[i * 4 + k] * std::conj(A[j * 4 + k]); // A[i, :] * A[j, :] - } - - // Apply the SYRK update: C(i, j) = alpha * sum + beta * C(i, j) - C[i * 4 + j] = alpha * sum + beta * C[i * 4 + j]; - - // If updating the lower triangle, mirror the values from the upper - // triangle - if (!upper && i != j) { - C[j * 4 + i] = C[i * 4 + j]; // Maintain symmetry - } - } - } - return C; -} - -template -[[nodiscard]] inline auto multiply(const std::array& lhs, - const std::array& rhs) { - // return matrixMultiplyWithKahan(lhs, rhs); - std::array result{}; - const int n = std::sqrt(N); - for (int i = 0; i < n; i++) { - for (int j = 0; j < n; j++) { - for (int k = 0; k < n; k++) { - std::cout << std::setprecision(17) << lhs[i * n + k] << " * " - << rhs[k * n + j] << " = " - << mul(lhs[i * n + k], rhs[k * n + j]) << '\n'; - result[i * n + j] += mul(lhs[i * n + k], rhs[k * n + j]); - std::cout << std::setprecision(50) << "->" << result[i * n + j] << '\n'; - } - std::cout << "\n===\n"; - } - } - return result; -} - -static matrix4x4 transpose(const matrix4x4& matrix) { - matrix4x4 result; - for (size_t i = 0; i < 4; ++i) { - for (size_t j = 0; j < 4; ++j) { - result[j * 4 + i] = matrix[i * 4 + j]; - } - } - return result; -} - -int main() { - matrix4x4 a = {qfp(0.3535533905932738, +0.35355339059327373), - qfp(-0.35355339059327373, +0.3535533905932738), - qfp(-0.35355339059327373, +0.3535533905932738), - qfp(0.3535533905932738, +0.35355339059327373), - qfp(0.35355339059327373, -0.3535533905932738), - qfp(0.3535533905932738, +0.35355339059327373), - qfp(-0.3535533905932738, -0.35355339059327373), - qfp(-0.35355339059327373, +0.3535533905932738), - qfp(0.35355339059327373, -0.3535533905932738), - qfp(-0.3535533905932738, -0.35355339059327373), - qfp(0.3535533905932738, +0.35355339059327373), - qfp(-0.35355339059327373, +0.3535533905932738), - qfp(0.3535533905932738, +0.35355339059327373), - qfp(0.35355339059327373, -0.3535533905932738), - qfp(0.35355339059327373, -0.3535533905932738), - qfp(0.3535533905932738, +0.35355339059327373)}; - - // print(multiply(transpose(a), transpose(transpose(a)))); - // print(syrk(false, 1.0, transpose(a), 0.0)); - print(zgemm(transpose(a), a)); -} From 1eeedba8fb44aad6c23d7bb98a819673b84dd4d4 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 20 Nov 2025 14:06:57 +0100 Subject: [PATCH 087/237] fix (?) tests --- .../MQTOpt/Transforms/gate-decomposition.mlir | 354 +++++++++--------- 1 file changed, 174 insertions(+), 180 deletions(-) diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index f66b0d333e..afb45bab9a 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -106,7 +106,7 @@ module { // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit // CHECK-NOT: mqtopt.i(%[[ANY:.*]]) - // CHECK: %[[Q0_0]], %[[Q1_0]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] + // CHECK: %[[Q1_1:.*]], %[[Q0_1:.*]] = mqtopt.x() %[[Q1_0]] ctrl %[[Q0_0]] // CHECK: mqtopt.deallocQubit %[[Q0_1]] // CHECK: mqtopt.deallocQubit %[[Q1_1]] @@ -134,22 +134,19 @@ module { module { // CHECK-LABEL: func.func @testSeriesOneQubitOpInbetween func.func @testSeriesOneQubitOpInbetween() { - // CHECK: %[[C2:.*]] = arith.constant -1.5707 - // CHECK: %[[C1:.*]] = arith.constant 3.14159 - // CHECK: %[[C0:.*]] = arith.constant 1.57079 + // CHECK-DAG: %[[C0:.*]] = arith.constant 3.14159 + // CHECK-DAG: %[[C1:.*]] = arith.constant 1.5707 // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: mqtopt.gphase(%[[C0:.*]]) - // CHECK: %[[Q0_1:.*]] = mqtopt.rz(%[[C0]]) %[[Q0_0]] - // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C1:.*]]) %[[Q0_1]] - // CHECK: %[[Q0_3:.*]] = mqtopt.rz(%[[C2:.*]]) %[[Q0_2]] + // CHECK: mqtopt.gphase(%[[C1]]) + // CHECK: %[[Q0_1:.*]] = mqtopt.rx(%[[C0]]) %[[Q0_0]] // ensure no other operations are inserted // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) - // CHECK: mqtopt.deallocQubit %[[Q0_3]] + // CHECK: mqtopt.deallocQubit %[[Q0_1]] // CHECK: mqtopt.deallocQubit %[[Q1_0]] %q0_0 = mqtopt.allocQubit @@ -172,22 +169,19 @@ module { module { // CHECK-LABEL: func.func @testSeriesStartingOneQubitOp func.func @testSeriesStartingOneQubitOp() { - // CHECK: %[[C2:.*]] = arith.constant -1.5707 - // CHECK: %[[C1:.*]] = arith.constant 3.14159 - // CHECK: %[[C0:.*]] = arith.constant 1.57079 + // CHECK-DAG: %[[C0:.*]] = arith.constant 3.14159 + // CHECK-DAG: %[[C1:.*]] = arith.constant 1.5707 // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: mqtopt.gphase(%[[C0:.*]]) - // CHECK: %[[Q0_1:.*]] = mqtopt.rz(%[[C0]]) %[[Q0_0]] - // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C1:.*]]) %[[Q0_1]] - // CHECK: %[[Q0_3:.*]] = mqtopt.rz(%[[C2:.*]]) %[[Q0_2]] + // CHECK: mqtopt.gphase(%[[C1]]) + // CHECK: %[[Q0_1:.*]] = mqtopt.rx(%[[C0]]) %[[Q0_0]] // ensure no other operations are inserted // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) - // CHECK: mqtopt.deallocQubit %[[Q0_3]] + // CHECK: mqtopt.deallocQubit %[[Q0_1]] // CHECK: mqtopt.deallocQubit %[[Q1_0]] %q0_0 = mqtopt.allocQubit @@ -210,22 +204,19 @@ module { module { // CHECK-LABEL: func.func @testSeriesEndingOneQubitOp func.func @testSeriesEndingOneQubitOp() { - // CHECK: %[[C2:.*]] = arith.constant -1.5707 - // CHECK: %[[C1:.*]] = arith.constant 3.14159 - // CHECK: %[[C0:.*]] = arith.constant 1.57079 + // CHECK-DAG: %[[C0:.*]] = arith.constant 3.14159 + // CHECK-DAG: %[[C1:.*]] = arith.constant 1.5707 // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: mqtopt.gphase(%[[C0:.*]]) - // CHECK: %[[Q0_1:.*]] = mqtopt.rz(%[[C0]]) %[[Q0_0]] - // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C1:.*]]) %[[Q0_1]] - // CHECK: %[[Q0_3:.*]] = mqtopt.rz(%[[C2:.*]]) %[[Q0_2]] + // CHECK: mqtopt.gphase(%[[C1]]) + // CHECK: %[[Q0_1:.*]] = mqtopt.rx(%[[C0]]) %[[Q0_0]] // ensure no other operations are inserted // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) - // CHECK: mqtopt.deallocQubit %[[Q0_3]] + // CHECK: mqtopt.deallocQubit %[[Q0_1]] // CHECK: mqtopt.deallocQubit %[[Q1_0]] %q0_0 = mqtopt.allocQubit @@ -283,52 +274,51 @@ module { } } +// ----- +// This test checks if a complex series containing two basis gates is decomposed correctly. + module { // CHECK-LABEL: func.func @testTwoBasisGateDecomposition func.func @testTwoBasisGateDecomposition() { - // CHECK: %[[C0:.*]] = arith.constant -2.356194490 - // CHECK: %[[C1:.*]] = arith.constant -1.070796326 - // CHECK: %[[C2:.*]] = arith.constant -0.370796326 - // CHECK: %[[C3:.*]] = arith.constant -2.526112944 - // CHECK: %[[C4:.*]] = arith.constant 1.0471975511 - // CHECK: %[[C5:.*]] = arith.constant 0.6154797086 - // CHECK: %[[C6:.*]] = arith.constant -3.141592653 - // CHECK: %[[C7:.*]] = arith.constant 2.7707963267 - // CHECK: %[[C8:.*]] = arith.constant -1.570796326 - // CHECK: %[[C9:.*]] = arith.constant 0.7853981633 - // CHECK: %[[C10:.*]] = arith.constant 2.5000 - // CHECK: %[[C11:.*]] = arith.constant 1.570796326 - // CHECK: %[[C12:.*]] = arith.constant -1.57079632 - // CHECK: %[[C13:.*]] = arith.constant 8.881784197 + // CHECK-DAG: %[[C0:.*]] = arith.constant -1.5707963267 + // CHECK-DAG: %[[C1:.*]] = arith.constant -3.141592653 + // CHECK-DAG: %[[C2:.*]] = arith.constant 1.2502369157 + // CHECK-DAG: %[[C3:.*]] = arith.constant 1.8299117708 + // CHECK-DAG: %[[C4:.*]] = arith.constant 2.8733113535 + // CHECK-DAG: %[[C5:.*]] = arith.constant 3.0097427712 + // CHECK-DAG: %[[C6:.*]] = arith.constant 0.2614370492 + // CHECK-DAG: %[[C7:.*]] = arith.constant -0.131849882 + // CHECK-DAG: %[[C8:.*]] = arith.constant 2.500000e+00 + // CHECK-DAG: %[[C9:.*]] = arith.constant -1.570796326 + // CHECK-DAG: %[[C10:.*]] = arith.constant 1.570796326 + // CHECK-DAG: %[[C11:.*]] = arith.constant 0.785398163 // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: mqtopt.gphase(%[[C13]]) - // CHECK: %[[Q0_1:.*]] = mqtopt.rz(%[[C12]]) %[[Q0_0]] - // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C11]]) %[[Q0_1]] - // CHECK: %[[Q0_3:.*]] = mqtopt.rz(%[[C11]]) %[[Q0_2]] - // CHECK: %[[Q1_1:.*]] = mqtopt.rz(%[[C10]]) %[[Q1_0]] - // CHECK: %[[Q1_2:.*]] = mqtopt.ry(%[[C9]]) %[[Q1_1]] - // CHECK: %[[Q1_3:.*]] = mqtopt.rz(%[[C8]]) %[[Q1_2]] - // CHECK: %[[Q1_4:.*]], %[[Q0_4:.*]] = mqtopt.x() %[[Q1_3]] ctrl %[[Q0_3]] - // CHECK: %[[Q0_5:.*]] = mqtopt.rz(%[[C11]]) %[[Q0_4]] - // CHECK: %[[Q0_6:.*]] = mqtopt.ry(%[[C7]]) %[[Q0_5]] - // CHECK: %[[Q0_7:.*]] = mqtopt.rz(%[[C6]]) %[[Q0_6]] - // CHECK: %[[Q1_5:.*]] = mqtopt.rz(%[[C5]]) %[[Q1_4]] - // CHECK: %[[Q1_6:.*]] = mqtopt.ry(%[[C4]]) %[[Q1_5]] - // CHECK: %[[Q1_7:.*]] = mqtopt.rz(%[[C3]]) %[[Q1_6]] - // CHECK: %[[Q1_8:.*]], %[[Q0_8:.*]] = mqtopt.x() %[[Q1_7]] ctrl %[[Q0_7]] - // CHECK: %[[Q0_9:.*]] = mqtopt.ry(%[[C2]]) %[[Q0_8]] - // CHECK: %[[Q0_10:.*]] = mqtopt.rz(%[[C1]]) %[[Q0_9]] - // CHECK: %[[Q1_9:.*]] = mqtopt.rz(%[[C12]]) %[[Q1_8]] - // CHECK: %[[Q1_10:.*]] = mqtopt.ry(%[[C0]]) %[[Q1_9]] + // CHECK-NOT: mqtopt.gphase(%[[ANY:.*]]) + // CHECK: %[[Q0_1:.*]] = mqtopt.ry(%[[C11]]) %[[Q0_0]] + // CHECK: %[[Q0_2:.*]] = mqtopt.rz(%[[C10]]) %[[Q0_1]] + // CHECK: %[[Q1_1:.*]] = mqtopt.rx(%[[C9]]) %[[Q1_0]] + // CHECK: %[[Q1_2:.*]] = mqtopt.ry(%[[C8]]) %[[Q1_1]] + // CHECK: %[[Q0_3:.*]], %[[Q1_3:.*]] = mqtopt.x() %[[Q0_2]] ctrl %[[Q1_2]] + // CHECK: %[[Q0_4:.*]] = mqtopt.rz(%[[C7]]) %[[Q0_3]] + // CHECK: %[[Q0_5:.*]] = mqtopt.ry(%[[C6]]) %[[Q0_4]] + // CHECK: %[[Q0_6:.*]] = mqtopt.rz(%[[C5]]) %[[Q0_5]] + // CHECK: %[[Q1_4:.*]] = mqtopt.rz(%[[C10]]) %[[Q1_3]] + // CHECK: %[[Q1_5:.*]] = mqtopt.ry(%[[C10]]) %[[Q1_4]] + // CHECK: %[[Q0_7:.*]], %[[Q1_6:.*]] = mqtopt.x() %[[Q0_6]] ctrl %[[Q1_5]] + // CHECK: %[[Q0_8:.*]] = mqtopt.rz(%[[C4]]) %[[Q0_7]] + // CHECK: %[[Q0_9:.*]] = mqtopt.ry(%[[C3]]) %[[Q0_8]] + // CHECK: %[[Q0_10:.*]] = mqtopt.rz(%[[C2]]) %[[Q0_9]] + // CHECK: %[[Q1_7:.*]] = mqtopt.rz(%[[C1]]) %[[Q1_6]] + // CHECK: %[[Q1_8:.*]] = mqtopt.rx(%[[C0]]) %[[Q1_7]] // ensure no other operations are inserted // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) // CHECK: mqtopt.deallocQubit %[[Q0_10]] - // CHECK: mqtopt.deallocQubit %[[Q1_10]] + // CHECK: mqtopt.deallocQubit %[[Q1_8]] %cst0 = arith.constant 2.5 : f64 %cst1 = arith.constant 1.2 : f64 @@ -346,127 +336,73 @@ module { %q0_5, %q1_4 = mqtopt.x() %q0_4 ctrl %q1_3: !mqtopt.Qubit ctrl !mqtopt.Qubit %q0_6 = mqtopt.rz(%cst2) %q0_5: !mqtopt.Qubit // make series longer to enforce decomposition - %q0_7 = mqtopt.i() %q0_6: !mqtopt.Qubit - %q0_8 = mqtopt.i() %q0_7: !mqtopt.Qubit - %q0_9 = mqtopt.i() %q0_8: !mqtopt.Qubit - %q1_5 = mqtopt.i() %q1_4: !mqtopt.Qubit - %q1_6 = mqtopt.i() %q1_5: !mqtopt.Qubit - %q1_7 = mqtopt.i() %q1_6: !mqtopt.Qubit - %q1_8 = mqtopt.i() %q1_7: !mqtopt.Qubit - %q1_9 = mqtopt.i() %q1_8: !mqtopt.Qubit + %q0_7, %q1_5 = mqtopt.i() %q0_6 ctrl %q1_4: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_8, %q1_6 = mqtopt.i() %q0_7 ctrl %q1_5: !mqtopt.Qubit ctrl !mqtopt.Qubit - mqtopt.deallocQubit %q0_9 - mqtopt.deallocQubit %q1_9 + mqtopt.deallocQubit %q0_8 + mqtopt.deallocQubit %q1_6 return } } // ----- -// This test checks if two single-qubit series (connected by an identity) remain separate without the insertion of a basis gate. - -module { - // CHECK-LABEL: func.func @testSingleQubitSeries - func.func @testSingleQubitSeries() { - // CHECK: %[[C0:.*]] = arith.constant -1.570796326 - // CHECK: %[[C1:.*]] = arith.constant 2.400000e+00 - // CHECK: %[[C2:.*]] = arith.constant 1.5707963267 - // CHECK: %[[C3:.*]] = arith.constant 2.500000e+00 - - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // CHECK: %[[Q0_1:.*]] = mqtopt.ry(%[[C3]]) %[[Q0_0]] - // CHECK: %[[Q1_1:.*]] = mqtopt.rz(%[[C2]]) %[[Q1_0]] - // CHECK: %[[Q1_2:.*]] = mqtopt.ry(%[[C1]]) %[[Q1_1]] - // CHECK: %[[Q1_3:.*]] = mqtopt.rz(%[[C0]]) %[[Q1_2]] - - // ensure no other operations are inserted - // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) - - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q1_3]] - - %cst0 = arith.constant 2.5 : f64 - %cst1 = arith.constant 1.2 : f64 - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.i() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_2 = mqtopt.ry(%cst0) %q0_1: !mqtopt.Qubit - %q1_2 = mqtopt.rx(%cst1) %q1_1: !mqtopt.Qubit - %q1_3 = mqtopt.rx(%cst1) %q1_2: !mqtopt.Qubit - - mqtopt.deallocQubit %q0_2 - mqtopt.deallocQubit %q1_3 - - return - } -} - -// ----- -// This test checks if two single-qubit series (connected by an identity) remain separate without the insertion of a basis gate. +// This test checks if a complex series containing at least three basis gates is decomposed correctly. module { // CHECK-LABEL: func.func @testThreeBasisGateDecomposition func.func @testThreeBasisGateDecomposition() { - // CHECK: %[[C0:.*]] = arith.constant 2.5762316133983267 : f64 - // CHECK: %[[C1:.*]] = arith.constant 0.7853981633974495 : f64 - // CHECK: %[[C2:.*]] = arith.constant -1.5707963267948981 : f64 - // CHECK: %[[C3:.*]] = arith.constant -2.2830967446466035 : f64 - // CHECK: %[[C4:.*]] = arith.constant 2.7053466041231213 : f64 - // CHECK: %[[C5:.*]] = arith.constant -2.4341917193679428 : f64 - // CHECK: %[[C6:.*]] = arith.constant -0.61547970867038693 : f64 - // CHECK: %[[C7:.*]] = arith.constant 2.0943951023931953 : f64 - // CHECK: %[[C8:.*]] = arith.constant 0.61547970867038737 : f64 - // CHECK: %[[C9:.*]] = arith.constant 0.25938051218841107 : f64 - // CHECK: %[[C10:.*]] = arith.constant -2.5261129449194062 : f64 - // CHECK: %[[C11:.*]] = arith.constant 1.0471975511965979 : f64 - // CHECK: %[[C12:.*]] = arith.constant -2.5261129449194053 : f64 - // CHECK: %[[C13:.*]] = arith.constant -1.5707963267948966 : f64 - // CHECK: %[[C14:.*]] = arith.constant 0.6948042173197494 : f64 - // CHECK: %[[C15:.*]] = arith.constant -1.5707963267948968 : f64 - // CHECK: %[[C16:.*]] = arith.constant 0.45501497985473183 : f64 - // CHECK: %[[C17:.*]] = arith.constant 1.7692140485614463 : f64 - // CHECK: %[[C18:.*]] = arith.constant -1.508911464303158 : f64 - // CHECK: %[[C19:.*]] = arith.constant -0.82190477627456371 : f64 - // CHECK: %[[C20:.*]] = arith.constant 1.5707963267948963 : f64 - // CHECK: %[[C21:.*]] = arith.constant 1.5707963267948966 : f64 - // CHECK: %[[C22:.*]] = arith.constant -0.78539816339744739 : f64 + // CHECK-DAG: %[[C0:.*]] = arith.constant 2.5762316133 + // CHECK-DAG: %[[C1:.*]] = arith.constant 2.3561944901 + // CHECK-DAG: %[[C2:.*]] = arith.constant -1.570796326 + // CHECK-DAG: %[[C3:.*]] = arith.constant 0.8584959089 + // CHECK-DAG: %[[C4:.*]] = arith.constant 0.4362460494 + // CHECK-DAG: %[[C5:.*]] = arith.constant 2.4341917193 + // CHECK-DAG: %[[C6:.*]] = arith.constant -0.615479708 + // CHECK-DAG: %[[C7:.*]] = arith.constant 1.0471975511 + // CHECK-DAG: %[[C8:.*]] = arith.constant 2.5261129449 + // CHECK-DAG: %[[C9:.*]] = arith.constant -0.259380512 + // CHECK-DAG: %[[C10:.*]] = arith.constant 1.570796326 + // CHECK-DAG: %[[C11:.*]] = arith.constant -2.52611294 + // CHECK-DAG: %[[C12:.*]] = arith.constant 2.094395102 + // CHECK-DAG: %[[C13:.*]] = arith.constant -0.61547970 + // CHECK-DAG: %[[C14:.*]] = arith.constant 0.694804217 + // CHECK-DAG: %[[C15:.*]] = arith.constant 0.220207934 + // CHECK-DAG: %[[C16:.*]] = arith.constant 1.125358392 + // CHECK-DAG: %[[C17:.*]] = arith.constant -0.03425899 + // CHECK-DAG: %[[C18:.*]] = arith.constant -1.57079632 + // CHECK-DAG: %[[C19:.*]] = arith.constant -2.39270110 + // CHECK-DAG: %[[C20:.*]] = arith.constant -0.78539816 // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: mqtopt.gphase(%[[C22]]) - // CHECK: %[[Q0_1:.*]] = mqtopt.rz(%[[C21]]) %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C20]]) %[[Q0_1]] : !mqtopt.Qubit - // CHECK: %[[Q0_3:.*]] = mqtopt.rz(%[[C19]]) %[[Q0_2]] : !mqtopt.Qubit - // CHECK: %[[Q1_1:.*]] = mqtopt.rz(%[[C18]]) %[[Q1_0]] : !mqtopt.Qubit - // CHECK: %[[Q1_2:.*]] = mqtopt.ry(%[[C17]]) %[[Q1_1]] : !mqtopt.Qubit - // CHECK: %[[Q1_3:.*]] = mqtopt.rz(%[[C16]]) %[[Q1_2]] : !mqtopt.Qubit - // CHECK: %[[Q1_4:.*]], %[[Q0_4:.*]] = mqtopt.x() %[[Q1_3]] ctrl %[[Q0_3]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_5:.*]] = mqtopt.rz(%[[C15]]) %[[Q0_4]] : !mqtopt.Qubit - // CHECK: %[[Q0_6:.*]] = mqtopt.ry(%[[C14]]) %[[Q0_5]] : !mqtopt.Qubit - // CHECK: %[[Q0_7:.*]] = mqtopt.rz(%[[C13]]) %[[Q0_6]] : !mqtopt.Qubit - // CHECK: %[[Q1_5:.*]] = mqtopt.rz(%[[C12]]) %[[Q1_4]] : !mqtopt.Qubit - // CHECK: %[[Q1_6:.*]] = mqtopt.ry(%[[C11]]) %[[Q1_5]] : !mqtopt.Qubit - // CHECK: %[[Q1_7:.*]] = mqtopt.rz(%[[C10]]) %[[Q1_6]] : !mqtopt.Qubit - // CHECK: %[[Q1_8:.*]], %[[Q0_8:.*]] = mqtopt.x() %[[Q1_7]] ctrl %[[Q0_7]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_9:.*]] = mqtopt.rz(%[[C21]]) %[[Q0_8]] : !mqtopt.Qubit - // CHECK: %[[Q0_10:.*]] = mqtopt.ry(%[[C9]]) %[[Q0_9]] : !mqtopt.Qubit + // CHECK: mqtopt.gphase(%[[C20]]) + // CHECK: %[[Q0_1:.*]] = mqtopt.ry(%[[C19]]) %[[Q0_0]] : !mqtopt.Qubit + // CHECK: %[[Q0_2:.*]] = mqtopt.rx(%[[C18]]) %[[Q0_1]] : !mqtopt.Qubit + // CHECK: %[[Q1_1:.*]] = mqtopt.rz(%[[C17]]) %[[Q1_0]] : !mqtopt.Qubit + // CHECK: %[[Q1_2:.*]] = mqtopt.ry(%[[C16]]) %[[Q1_1]] : !mqtopt.Qubit + // CHECK: %[[Q1_3:.*]] = mqtopt.rz(%[[C15]]) %[[Q1_2]] : !mqtopt.Qubit + // CHECK: %[[Q1_4:.*]], %[[Q0_3:.*]] = mqtopt.x() %[[Q1_3]] ctrl %[[Q0_2]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[Q0_4:.*]] = mqtopt.rx(%[[C14]]) %[[Q0_3]] : !mqtopt.Qubit + // CHECK: %[[Q1_5:.*]] = mqtopt.rz(%[[C13]]) %[[Q1_4]] : !mqtopt.Qubit + // CHECK: %[[Q1_6:.*]] = mqtopt.ry(%[[C12]]) %[[Q1_5]] : !mqtopt.Qubit + // CHECK: %[[Q1_7:.*]] = mqtopt.rz(%[[C11]]) %[[Q1_6]] : !mqtopt.Qubit + // CHECK: %[[Q1_8:.*]], %[[Q0_5:.*]] = mqtopt.x() %[[Q1_7]] ctrl %[[Q0_4]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[Q0_6:.*]] = mqtopt.rz(%[[C10]]) %[[Q0_5]] : !mqtopt.Qubit + // CHECK: %[[Q0_7:.*]] = mqtopt.ry(%[[C9]]) %[[Q0_6]] : !mqtopt.Qubit // CHECK: %[[Q1_9:.*]] = mqtopt.rz(%[[C8]]) %[[Q1_8]] : !mqtopt.Qubit // CHECK: %[[Q1_10:.*]] = mqtopt.ry(%[[C7]]) %[[Q1_9]] : !mqtopt.Qubit // CHECK: %[[Q1_11:.*]] = mqtopt.rz(%[[C6]]) %[[Q1_10]] : !mqtopt.Qubit - // CHECK: %[[Q1_12:.*]], %[[Q0_11:.*]] = mqtopt.x() %[[Q1_11]] ctrl %[[Q0_10]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_12:.*]] = mqtopt.rz(%[[C5]]) %[[Q0_11]] : !mqtopt.Qubit - // CHECK: %[[Q0_13:.*]] = mqtopt.ry(%[[C4]]) %[[Q0_12]] : !mqtopt.Qubit - // CHECK: %[[Q0_14:.*]] = mqtopt.rz(%[[C3]]) %[[Q0_13]] : !mqtopt.Qubit + // CHECK: %[[Q1_12:.*]], %[[Q0_8:.*]] = mqtopt.x() %[[Q1_11]] ctrl %[[Q0_7]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[Q0_9:.*]] = mqtopt.rz(%[[C5]]) %[[Q0_8]] : !mqtopt.Qubit + // CHECK: %[[Q0_10:.*]] = mqtopt.ry(%[[C4]]) %[[Q0_9]] : !mqtopt.Qubit + // CHECK: %[[Q0_11:.*]] = mqtopt.rz(%[[C3]]) %[[Q0_10]] : !mqtopt.Qubit // CHECK: %[[Q1_13:.*]] = mqtopt.rz(%[[C2]]) %[[Q1_12]] : !mqtopt.Qubit // CHECK: %[[Q1_14:.*]] = mqtopt.ry(%[[C1]]) %[[Q1_13]] : !mqtopt.Qubit // CHECK: %[[Q1_15:.*]] = mqtopt.rz(%[[C0]]) %[[Q1_14]] : !mqtopt.Qubit - // CHECK: mqtopt.deallocQubit %[[Q0_14]] + // CHECK: mqtopt.deallocQubit %[[Q0_11]] // CHECK: mqtopt.deallocQubit %[[Q1_15]] %cst0 = arith.constant 2.5 : f64 @@ -504,40 +440,98 @@ module { module { // CHECK-LABEL: func.func @testRepeatedDecomposition func.func @testRepeatedDecomposition() { + return + } +} + +// ----- +// This test checks if two single-qubit series (connected by an identity) remain separate without the insertion of a basis gate. + +module { + // CHECK-LABEL: func.func @testSingleQubitSeries + func.func @testSingleQubitSeries() { + // CHECK-DAG: %[[C0:.*]] = arith.constant 2.400000 + // CHECK-DAG: %[[C1:.*]] = arith.constant 2.500000 + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit - // TODO + // CHECK: %[[Q0_1:.*]] = mqtopt.ry(%[[C1]]) %[[Q0_0]] + // CHECK: %[[Q1_1:.*]] = mqtopt.rx(%[[C0]]) %[[Q1_0]] - // CHECK: mqtopt.deallocQubit %[[Q0_5]] - // CHECK: mqtopt.deallocQubit %[[Q1_4]] - // CHECK: mqtopt.deallocQubit %[[Q2_1]] + // ensure no other operations are inserted + // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) + + // CHECK: mqtopt.deallocQubit %[[Q0_1]] + // CHECK: mqtopt.deallocQubit %[[Q1_1]] %cst0 = arith.constant 2.5 : f64 %cst1 = arith.constant 1.2 : f64 - %cst2 = arith.constant 0.5 : f64 %q0_0 = mqtopt.allocQubit %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - %q0_x, %q1_x = mqtopt.i() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_1 = mqtopt.h() %q0_x: !mqtopt.Qubit - %q1_1, %q0_2 = mqtopt.x() %q1_x ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3, %q1_2 = mqtopt.rzz(%cst0) %q0_2, %q1_1: !mqtopt.Qubit, !mqtopt.Qubit - %q1_3 = mqtopt.ry(%cst1) %q1_2: !mqtopt.Qubit - %q0_4 = mqtopt.rx(%cst1) %q0_3: !mqtopt.Qubit - %q0_5, %q1_4 = mqtopt.x() %q0_4 ctrl %q1_3: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_6 = mqtopt.rz(%cst2) %q0_5: !mqtopt.Qubit - %q0_7, %q1_5 = mqtopt.rxx(%cst0) %q0_6, %q1_4: !mqtopt.Qubit, !mqtopt.Qubit - %q0_8, %q1_6 = mqtopt.ryy(%cst2) %q0_7, %q1_5: !mqtopt.Qubit, !mqtopt.Qubit - // make series longer to enforce decomposition - %q0_9, %q1_7 = mqtopt.i() %q0_8 ctrl %q1_6: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_1, %q1_1 = mqtopt.i() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_2 = mqtopt.ry(%cst0) %q0_1: !mqtopt.Qubit + %q1_2 = mqtopt.rx(%cst1) %q1_1: !mqtopt.Qubit + %q1_3 = mqtopt.rx(%cst1) %q1_2: !mqtopt.Qubit - mqtopt.deallocQubit %q0_9 - mqtopt.deallocQubit %q1_7 - mqtopt.deallocQubit %q2_0 + mqtopt.deallocQubit %q0_2 + mqtopt.deallocQubit %q1_3 + + return + } +} + +// ----- +// This test checks if a two-qubit series starting with two separate single-qubit chains is fully decomposed. + +module { + // CHECK-LABEL: func.func @testSeriesSingleQubitBacktracking + func.func @testSeriesSingleQubitBacktracking() { + // CHECK-DAG: %[[C0:.*]] = arith.constant -2.4269908 + // CHECK-DAG: %[[C1:.*]] = arith.constant 3.14159265 + // CHECK-DAG: %[[C2:.*]] = arith.constant 0.85619449 + // CHECK-DAG: %[[C3:.*]] = arith.constant -2.3561944 + // CHECK-DAG: %[[C4:.*]] = arith.constant -1.5707963 + // CHECK-DAG: %[[C5:.*]] = arith.constant -1.5707963 + // CHECK-DAG: %[[C6:.*]] = arith.constant 2.35619449 + // CHECK-DAG: %[[C7:.*]] = arith.constant -3.1415926 + + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit + // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit + + // CHECK: mqtopt.gphase(%[[C7]]) + // CHECK: %[[Q0_1:.*]] = mqtopt.rz(%[[C6]]) %[[Q0_0]] : !mqtopt.Qubit + // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C5]]) %[[Q0_1]] : !mqtopt.Qubit + // CHECK: %[[Q1_1:.*]] = mqtopt.ry(%[[C4]]) %[[Q1_0]] : !mqtopt.Qubit + // CHECK: %[[Q1_2:.*]] = mqtopt.rz(%[[C3]]) %[[Q1_1]] : !mqtopt.Qubit + // CHECK: %[[Q0_3:.*]], %[[Q1_3:.*]] = mqtopt.x() %[[Q0_2]] ctrl %[[Q1_2]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[Q0_4:.*]] = mqtopt.rx(%[[C2]]) %[[Q0_3]] : !mqtopt.Qubit + // CHECK: %[[Q0_5:.*]] = mqtopt.ry(%[[C1]]) %[[Q0_4]] : !mqtopt.Qubit + // CHECK: %[[Q1_4:.*]] = mqtopt.rz(%[[C0]]) %[[Q1_3]] : !mqtopt.Qubit + + // CHECK: mqtopt.deallocQubit %[[Q0_5]] + // CHECK: mqtopt.deallocQubit %[[Q1_4]] + + %q0_0 = mqtopt.allocQubit + %q1_0 = mqtopt.allocQubit + + %cst0 = arith.constant 1.5 : f64 + %cst1 = arith.constant 0.2 : f64 + %cst2 = arith.constant 0.9 : f64 + + %q0_1 = mqtopt.h() %q0_0: !mqtopt.Qubit + %q0_2 = mqtopt.rx(%cst0) %q0_1: !mqtopt.Qubit + %q0_3 = mqtopt.x() %q0_2: !mqtopt.Qubit + %q1_1 = mqtopt.h() %q1_0: !mqtopt.Qubit + %q1_2 = mqtopt.rz(%cst0) %q1_1: !mqtopt.Qubit + %q0_4, %q1_3 = mqtopt.x() %q0_3 ctrl %q1_2: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q1_4, %q0_5 = mqtopt.i() %q1_3 ctrl %q0_4: !mqtopt.Qubit ctrl !mqtopt.Qubit + %q1_5, %q0_6 = mqtopt.i() %q1_4 ctrl %q0_5: !mqtopt.Qubit ctrl !mqtopt.Qubit + + mqtopt.deallocQubit %q0_6 + mqtopt.deallocQubit %q1_5 return } From 3afb374371ce75344601c0fed0059e36b415e824 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 20 Nov 2025 14:24:24 +0100 Subject: [PATCH 088/237] fix final test --- .../Transforms/GateDecompositionPattern.cpp | 7 +- .../MQTOpt/Transforms/gate-decomposition.mlir | 90 +++++++++---------- 2 files changed, 49 insertions(+), 48 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 8e01af1ddf..f1f0bcfadf 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -86,7 +86,7 @@ struct GateDecompositionPattern final } if (hasGlobalPhase()) { // need to add a global phase gate if a global phase needs to be applied - c += getComplexity(qc::GPhase, 1); + c += getComplexity(qc::GPhase, 0); } return c; } @@ -186,12 +186,15 @@ struct GateDecompositionPattern final protected: static constexpr fp SANITY_CHECK_PRECISION = 1e-12; - [[nodiscard]] static std::size_t getComplexity(qc::OpType /*type*/, + [[nodiscard]] static std::size_t getComplexity(qc::OpType type, std::size_t numOfQubits) { if (numOfQubits > 1) { constexpr std::size_t multiQubitFactor = 10; return (numOfQubits - 1) * multiQubitFactor; } + if (type == qc::GPhase) { + return 2; + } return 1; } diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index afb45bab9a..c0b1dd17c0 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -352,58 +352,56 @@ module { module { // CHECK-LABEL: func.func @testThreeBasisGateDecomposition func.func @testThreeBasisGateDecomposition() { - // CHECK-DAG: %[[C0:.*]] = arith.constant 2.5762316133 - // CHECK-DAG: %[[C1:.*]] = arith.constant 2.3561944901 - // CHECK-DAG: %[[C2:.*]] = arith.constant -1.570796326 - // CHECK-DAG: %[[C3:.*]] = arith.constant 0.8584959089 - // CHECK-DAG: %[[C4:.*]] = arith.constant 0.4362460494 - // CHECK-DAG: %[[C5:.*]] = arith.constant 2.4341917193 - // CHECK-DAG: %[[C6:.*]] = arith.constant -0.615479708 - // CHECK-DAG: %[[C7:.*]] = arith.constant 1.0471975511 - // CHECK-DAG: %[[C8:.*]] = arith.constant 2.5261129449 - // CHECK-DAG: %[[C9:.*]] = arith.constant -0.259380512 - // CHECK-DAG: %[[C10:.*]] = arith.constant 1.570796326 - // CHECK-DAG: %[[C11:.*]] = arith.constant -2.52611294 - // CHECK-DAG: %[[C12:.*]] = arith.constant 2.094395102 - // CHECK-DAG: %[[C13:.*]] = arith.constant -0.61547970 - // CHECK-DAG: %[[C14:.*]] = arith.constant 0.694804217 - // CHECK-DAG: %[[C15:.*]] = arith.constant 0.220207934 - // CHECK-DAG: %[[C16:.*]] = arith.constant 1.125358392 - // CHECK-DAG: %[[C17:.*]] = arith.constant -0.03425899 - // CHECK-DAG: %[[C18:.*]] = arith.constant -1.57079632 - // CHECK-DAG: %[[C19:.*]] = arith.constant -2.39270110 - // CHECK-DAG: %[[C20:.*]] = arith.constant -0.78539816 + // CHECK-DAG: %[[C0:.*]] = arith.constant 1.00543528660 + // CHECK-DAG: %[[C1:.*]] = arith.constant -2.3561944901 + // CHECK-DAG: %[[C2:.*]] = arith.constant 0.85849590894 + // CHECK-DAG: %[[C3:.*]] = arith.constant 0.43624604946 + // CHECK-DAG: %[[C4:.*]] = arith.constant 2.43419171936 + // CHECK-DAG: %[[C5:.*]] = arith.constant -0.6154797086 + // CHECK-DAG: %[[C6:.*]] = arith.constant 1.04719755119 + // CHECK-DAG: %[[C7:.*]] = arith.constant 2.52611294491 + // CHECK-DAG: %[[C8:.*]] = arith.constant -0.2593805121 + // CHECK-DAG: %[[C9:.*]] = arith.constant 1.57079632679 + // CHECK-DAG: %[[C10:.*]] = arith.constant -2.52611294491 + // CHECK-DAG: %[[C11:.*]] = arith.constant 2.094395102393 + // CHECK-DAG: %[[C12:.*]] = arith.constant -0.61547970867 + // CHECK-DAG: %[[C13:.*]] = arith.constant 0.694804217319 + // CHECK-DAG: %[[C14:.*]] = arith.constant 0.220207934068 + // CHECK-DAG: %[[C15:.*]] = arith.constant 1.125358392049 + // CHECK-DAG: %[[C16:.*]] = arith.constant -0.03425899788 + // CHECK-DAG: %[[C17:.*]] = arith.constant -1.57079632679 + // CHECK-DAG: %[[C18:.*]] = arith.constant -2.39270110306 + // CHECK-DAG: %[[C19:.*]] = arith.constant -0.78539816339 // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: mqtopt.gphase(%[[C20]]) - // CHECK: %[[Q0_1:.*]] = mqtopt.ry(%[[C19]]) %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]] = mqtopt.rx(%[[C18]]) %[[Q0_1]] : !mqtopt.Qubit - // CHECK: %[[Q1_1:.*]] = mqtopt.rz(%[[C17]]) %[[Q1_0]] : !mqtopt.Qubit - // CHECK: %[[Q1_2:.*]] = mqtopt.ry(%[[C16]]) %[[Q1_1]] : !mqtopt.Qubit - // CHECK: %[[Q1_3:.*]] = mqtopt.rz(%[[C15]]) %[[Q1_2]] : !mqtopt.Qubit - // CHECK: %[[Q1_4:.*]], %[[Q0_3:.*]] = mqtopt.x() %[[Q1_3]] ctrl %[[Q0_2]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_4:.*]] = mqtopt.rx(%[[C14]]) %[[Q0_3]] : !mqtopt.Qubit - // CHECK: %[[Q1_5:.*]] = mqtopt.rz(%[[C13]]) %[[Q1_4]] : !mqtopt.Qubit - // CHECK: %[[Q1_6:.*]] = mqtopt.ry(%[[C12]]) %[[Q1_5]] : !mqtopt.Qubit - // CHECK: %[[Q1_7:.*]] = mqtopt.rz(%[[C11]]) %[[Q1_6]] : !mqtopt.Qubit - // CHECK: %[[Q1_8:.*]], %[[Q0_5:.*]] = mqtopt.x() %[[Q1_7]] ctrl %[[Q0_4]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_6:.*]] = mqtopt.rz(%[[C10]]) %[[Q0_5]] : !mqtopt.Qubit - // CHECK: %[[Q0_7:.*]] = mqtopt.ry(%[[C9]]) %[[Q0_6]] : !mqtopt.Qubit - // CHECK: %[[Q1_9:.*]] = mqtopt.rz(%[[C8]]) %[[Q1_8]] : !mqtopt.Qubit - // CHECK: %[[Q1_10:.*]] = mqtopt.ry(%[[C7]]) %[[Q1_9]] : !mqtopt.Qubit - // CHECK: %[[Q1_11:.*]] = mqtopt.rz(%[[C6]]) %[[Q1_10]] : !mqtopt.Qubit - // CHECK: %[[Q1_12:.*]], %[[Q0_8:.*]] = mqtopt.x() %[[Q1_11]] ctrl %[[Q0_7]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_9:.*]] = mqtopt.rz(%[[C5]]) %[[Q0_8]] : !mqtopt.Qubit - // CHECK: %[[Q0_10:.*]] = mqtopt.ry(%[[C4]]) %[[Q0_9]] : !mqtopt.Qubit - // CHECK: %[[Q0_11:.*]] = mqtopt.rz(%[[C3]]) %[[Q0_10]] : !mqtopt.Qubit - // CHECK: %[[Q1_13:.*]] = mqtopt.rz(%[[C2]]) %[[Q1_12]] : !mqtopt.Qubit - // CHECK: %[[Q1_14:.*]] = mqtopt.ry(%[[C1]]) %[[Q1_13]] : !mqtopt.Qubit - // CHECK: %[[Q1_15:.*]] = mqtopt.rz(%[[C0]]) %[[Q1_14]] : !mqtopt.Qubit + // CHECK: mqtopt.gphase(%[[C19]]) + // CHECK: %[[Q0_1:.*]] = mqtopt.ry(%[[C18]]) %[[Q0_0]] + // CHECK: %[[Q0_2:.*]] = mqtopt.rx(%[[C17]]) %[[Q0_1]] + // CHECK: %[[Q1_1:.*]] = mqtopt.rz(%[[C16]]) %[[Q1_0]] + // CHECK: %[[Q1_2:.*]] = mqtopt.ry(%[[C15]]) %[[Q1_1]] + // CHECK: %[[Q1_3:.*]] = mqtopt.rz(%[[C14]]) %[[Q1_2]] + // CHECK: %[[Q1_4:.*]], %[[Q0_3:.*]] = mqtopt.x() %[[Q1_3]] ctrl %[[Q0_2]] + // CHECK: %[[Q0_4:.*]] = mqtopt.rx(%[[C13]]) %[[Q0_3]] + // CHECK: %[[Q1_5:.*]] = mqtopt.rz(%[[C12]]) %[[Q1_4]] + // CHECK: %[[Q1_6:.*]] = mqtopt.ry(%[[C11]]) %[[Q1_5]] + // CHECK: %[[Q1_7:.*]] = mqtopt.rz(%[[C10]]) %[[Q1_6]] + // CHECK: %[[Q1_8:.*]], %[[Q0_5:.*]] = mqtopt.x() %[[Q1_7]] ctrl %[[Q0_4]] + // CHECK: %[[Q0_6:.*]] = mqtopt.rz(%[[C9]]) %[[Q0_5]] + // CHECK: %[[Q0_7:.*]] = mqtopt.ry(%[[C8]]) %[[Q0_6]] + // CHECK: %[[Q1_9:.*]] = mqtopt.rz(%[[C7]]) %[[Q1_8]] + // CHECK: %[[Q1_10:.*]] = mqtopt.ry(%[[C6]]) %[[Q1_9]] + // CHECK: %[[Q1_11:.*]] = mqtopt.rz(%[[C5]]) %[[Q1_10]] + // CHECK: %[[Q1_12:.*]], %[[Q0_8:.*]] = mqtopt.x() %[[Q1_11]] ctrl %[[Q0_7]] + // CHECK: %[[Q0_9:.*]] = mqtopt.rz(%[[C4]]) %[[Q0_8]] + // CHECK: %[[Q0_10:.*]] = mqtopt.ry(%[[C3]]) %[[Q0_9]] + // CHECK: %[[Q0_11:.*]] = mqtopt.rz(%[[C2]]) %[[Q0_10]] + // CHECK: %[[Q1_13:.*]] = mqtopt.rx(%[[C1]]) %[[Q1_12]] + // CHECK: %[[Q1_14:.*]] = mqtopt.rz(%[[C0]]) %[[Q1_13]] // CHECK: mqtopt.deallocQubit %[[Q0_11]] - // CHECK: mqtopt.deallocQubit %[[Q1_15]] + // CHECK: mqtopt.deallocQubit %[[Q1_14]] %cst0 = arith.constant 2.5 : f64 %cst1 = arith.constant 1.2 : f64 From 17b02c597a7edba296eadaeb33d48e9c84a44b8f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 20 Nov 2025 14:51:34 +0100 Subject: [PATCH 089/237] another minor cleanup --- .../Transforms/GateDecompositionPattern.cpp | 74 +++++-------------- 1 file changed, 17 insertions(+), 57 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index f1f0bcfadf..2382d6953e 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -643,7 +643,6 @@ struct GateDecompositionPattern final // or "7.1 Kronecker decomposition" in on_gates.pdf // or quantumflow.kronecker_decomposition - helpers::print(specialUnitary, "SPECIAL_UNITARY"); // first quadrant matrix2x2 r{{specialUnitary(0, 0), specialUnitary(0, 1)}, {specialUnitary(1, 0), specialUnitary(1, 1)}}; @@ -659,14 +658,11 @@ struct GateDecompositionPattern final "decompose_two_qubit_product_gate: unable to decompose: det_r < 0.1"}; } r /= std::sqrt(detR); - helpers::print(r, "R"); // transpose with complex conjugate of each element matrix2x2 rTConj = r.transpose().conjugate(); auto temp = helpers::kroneckerProduct(IDENTITY_GATE, rTConj); - helpers::print(temp, "TEMP (decompose_two_qubit_product_gate, 1)"); temp = specialUnitary * temp; - helpers::print(temp, "TEMP (decompose_two_qubit_product_gate, 2)"); // [[a, b, c, d], // [e, f, g, h], => [[a, c], @@ -692,10 +688,6 @@ struct GateDecompositionPattern final {C_ZERO, C_ZERO, IM, C_M_ONE}, {C_ONE, M_IM, C_ZERO, C_ZERO}, }; - const matrix4x4 b = FRAC1_SQRT2 * matrix4x4{{C_ONE, C_ZERO, C_ZERO, IM}, - {C_ZERO, IM, C_ONE, C_ZERO}, - {C_ZERO, IM, -C_ONE, C_ZERO}, - {C_ONE, C_ZERO, C_ZERO, -IM}}; const matrix4x4 bNonNormalizedDagger{ {qfp(0.5, 0.), C_ZERO, C_ZERO, qfp(0.5, 0.)}, @@ -703,20 +695,16 @@ struct GateDecompositionPattern final {C_ZERO, qfp(0., -0.5), qfp(0., -0.5), C_ZERO}, {C_ZERO, qfp(0.5, 0.), qfp(-0.5, 0.), C_ZERO}, }; - const matrix4x4 bDagger = b.conjugate().transpose(); - helpers::print(unitary, "UNITARY in MAGIC BASIS TRANSFORM"); if (direction == MagicBasisTransform::OutOf) { - // return bDagger * unitary * b; // TODO: same result? return bNonNormalizedDagger * unitary * bNonNormalized; } if (direction == MagicBasisTransform::Into) { - // return b * unitary * bDagger; return bNonNormalized * unitary * bNonNormalizedDagger; } throw std::logic_error{"Unknown MagicBasisTransform direction!"}; } - static fp traceToFid(const qfp& x) { + static fp traceToFidelity(const qfp& x) { auto xAbs = std::abs(x); return (4.0 + xAbs * xAbs) / 20.0; } @@ -745,8 +733,6 @@ struct GateDecompositionPattern final static matrix2x2 rzMatrix(fp theta) { return matrix2x2{{qfp{std::cos(theta / 2.), -std::sin(theta / 2.)}, 0}, {0, qfp{std::cos(theta / 2.), std::sin(theta / 2.)}}}; - // auto ilam2 = qfp(0., 0.5 * theta); - // return {std::exp(-ilam2), C_ZERO, C_ZERO, std::exp(ilam2)}; } static matrix4x4 rxxMatrix(const fp theta) { @@ -766,7 +752,7 @@ struct GateDecompositionPattern final return matrix4x4{{{cosTheta, 0, 0, {0., sinTheta}}, {0, cosTheta, {0., -sinTheta}, 0}, {0, {0., -sinTheta}, cosTheta, 0}, - {std::complex{0., sinTheta}, 0, 0, cosTheta}}}; + {{0., sinTheta}, 0, 0, cosTheta}}}; } static matrix4x4 rzzMatrix(const fp theta) { @@ -901,15 +887,15 @@ struct GateDecompositionPattern final } } if (gate.type == qc::RXX) { - // TODO: check qubit order + // TODO: check qubit order? return rxxMatrix(gate.parameter[0]); } if (gate.type == qc::RYY) { - // TODO: check qubit order + // TODO: check qubit order? return ryyMatrix(gate.parameter[0]); } if (gate.type == qc::RZZ) { - // TODO: check qubit order + // TODO: check qubit order? return rzzMatrix(gate.parameter[0]); } if (gate.type == qc::I) { @@ -921,6 +907,13 @@ struct GateDecompositionPattern final throw std::logic_error{"Invalid number of qubit IDs in compute_unitary"}; } + /** + * Weyl decomposition of a 2-qubit unitary matrix (4x4). + * The result consists of four 2x2 1-qubit matrices (k1l, k2l, k1r, k2r) and + * three parameters for a canonical gate (a, b, c). The matrices can then be + * decomposed using a single-qubit decomposition into e.g. rotation gates and + * the canonical gate is RXX(-2 * a), RYY(-2 * b), RZZ (-2 * c). + */ struct TwoQubitWeylDecomposition { // a, b, c are the parameters of the canonical gate (CAN) fp a; // rotation of RXX gate in CAN @@ -960,13 +953,10 @@ struct GateDecompositionPattern final auto detU = u.determinant(); auto detPow = std::pow(detU, static_cast(-0.25)); u *= detPow; - helpers::print(u, "U"); auto globalPhase = std::arg(detU) / 4.; auto uP = magicBasisTransform(u, MagicBasisTransform::OutOf); - helpers::print(uP, "U_P"); matrix4x4 m2 = uP.transpose() * uP; auto defaultEulerBasis = EulerBasis::ZYZ; - helpers::print(m2, "M2"); // M2 is a symmetric complex matrix. We need to decompose it as M2 = P D // P^T where P ∈ SO(4), D is diagonal with unit-magnitude elements. @@ -1008,12 +998,9 @@ struct GateDecompositionPattern final matrix4x4 pInner = pInnerReal; diagonal4x4 dInner = (pInner.transpose() * m2 * pInner).diagonal(); - helpers::print(dInner, "D_INNER"); - helpers::print(pInner, "P_INNER"); matrix4x4 diagD = dInner.asDiagonal(); matrix4x4 compare = pInner * diagD * pInner.transpose(); - helpers::print(compare, "COMPARE"); found = compare.isApprox(m2, 1e-13); if (found) { // p are the eigenvectors which are decomposed into the @@ -1040,13 +1027,11 @@ struct GateDecompositionPattern final // Step 7 Eigen::Vector cs; rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; - helpers::print(dReal, "D_REAL"); dReal(3) = -dReal(0) - dReal(1) - dReal(2); for (int i = 0; i < static_cast(cs.size()); ++i) { assert(i < dReal.size()); cs[i] = remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); } - helpers::print(cs, "CS (1)"); decltype(cs) cstemp; llvm::transform(cs, cstemp.begin(), [](auto&& x) { @@ -1058,20 +1043,12 @@ struct GateDecompositionPattern final // order in eigen decomposition algorithm? llvm::stable_sort(order, [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); - // llvm::stable_sort(order, [&](fp a, fp b) { - // auto tmp1 = remEuclid(cs[a], qc::PI_2); - // tmp1 = std::min(tmp1, qc::PI_2 - tmp1); - // auto tmp2 = remEuclid(cs[b], qc::PI_2); - // tmp2 = std::min(tmp2, qc::PI_2 - tmp2); - // return tmp1 < tmp2; - // }); std::tie(order[0], order[1], order[2]) = std::tuple{order[1], order[2], order[0]}; std::tie(cs[0], cs[1], cs[2]) = std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; std::tie(dReal(0), dReal(1), dReal(2)) = std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2])}; - helpers::print(dReal, "D_REAL (sorted)"); // swap columns of p according to order matrix4x4 pOrig = p; @@ -1079,7 +1056,6 @@ struct GateDecompositionPattern final p.col(i) = pOrig.col(order[i]); } if (p.determinant().real() < 0.0) { - std::cerr << "SECOND CORRECTION?\n"; auto lastColumnIndex = p.cols() - 1; p.col(lastColumnIndex) *= -1.0; } @@ -1088,20 +1064,13 @@ struct GateDecompositionPattern final temp *= IM; temp = temp.exp(); - // temp = temp.conjugate(); - // temp += matrix4x4::Constant(0.0); - helpers::print(temp, "TEMP"); - helpers::print(p, "P"); assert(std::abs(p.determinant() - 1.0) < SANITY_CHECK_PRECISION); - // https://threeplusone.com/pubs/on_gates.pdf - // uP = V, m2 = V^T*V, temp = D, p = Q1 + matrix4x4 k1 = uP * p * temp; - helpers::print(k1, "K1 (1)"); assert((k1.transpose() * k1).isIdentity()); // k1 must be orthogonal assert(k1.determinant().real() > 0.0); k1 = magicBasisTransform(k1, MagicBasisTransform::Into); matrix4x4 k2 = p.transpose().conjugate(); - helpers::print(k2, "K2 (1)"); assert((k2.transpose() * k2).isIdentity()); // k2 must be orthogonal assert(k2.determinant().real() > 0.0); k2 = magicBasisTransform(k2, MagicBasisTransform::Into); @@ -1178,16 +1147,6 @@ struct GateDecompositionPattern final globalPhase -= qc::PI_2; } - helpers::print(K1l, "K1l (1)"); - helpers::print(K2l, "K2l (1)"); - helpers::print(K1r, "K1r (1)"); - helpers::print(K2r, "K2r (1)"); - - helpers::print(cs, "CS (2)"); - helpers::print(K1l, "K1l (2)"); - helpers::print(K2l, "K2l (2)"); - helpers::print(K1r, "K1r (2)"); - helpers::print(K2r, "K2r (2)"); auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); auto getCanonicalMatrix = [](fp a, fp b, fp c) -> matrix4x4 { auto xx = getTwoQubitMatrix({ @@ -1229,7 +1188,7 @@ struct GateDecompositionPattern final qfp(std::cos(da) * std::cos(db) * std::cos(dc), std::sin(da) * std::sin(db) * std::sin(dc)); if (fidelity) { - return traceToFid(tr) >= *fidelity; + return traceToFidelity(tr) >= *fidelity; } return false; }; @@ -1566,7 +1525,7 @@ struct GateDecompositionPattern final std::sin(da) * std::sin(db) * std::sin(dc)); }; auto tr = getTr(); - specialized.calculatedFidelity = traceToFid(tr); + specialized.calculatedFidelity = traceToFidelity(tr); if (specialized.requestedFidelity) { if (specialized.calculatedFidelity + 1.0e-13 < *specialized.requestedFidelity) { @@ -1809,7 +1768,8 @@ struct GateDecompositionPattern final for (int i = 0; i < static_cast(traces.size()); ++i) { // lower fidelity means it becomes easier to choose a lower number of // basis gates - auto value = traceToFid(traces[i]) * std::pow(actualBasisFidelity, i); + auto value = + traceToFidelity(traces[i]) * std::pow(actualBasisFidelity, i); if (value > minValue) { minIndex = i; minValue = value; From ca9c9f1f87ec551e4ea20eb6c3b7fc1874b523ba Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 20 Nov 2025 14:52:49 +0100 Subject: [PATCH 090/237] clean sanity check prints up --- .../Transforms/GateDecompositionPattern.cpp | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 2382d6953e..35f6be2f0e 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -557,9 +557,6 @@ struct GateDecompositionPattern final } } std::cerr << '\n'; - helpers::print(series.getUnitaryMatrix(), "ORIGINAL UNITARY"); - helpers::print((unitaryMatrix * std::exp(IM * sequence.globalPhase)).eval(), - "RESULT UNITARY MATRIX"); assert((unitaryMatrix * std::exp(IM * sequence.globalPhase)) .isApprox(series.getUnitaryMatrix(), SANITY_CHECK_PRECISION)); @@ -1075,12 +1072,6 @@ struct GateDecompositionPattern final assert(k2.determinant().real() > 0.0); k2 = magicBasisTransform(k2, MagicBasisTransform::Into); - helpers::print( - (k1 * - magicBasisTransform(temp.conjugate(), MagicBasisTransform::Into) * - k2) - .eval(), - "SANITY CHECK (1)"); assert((k1 * magicBasisTransform(temp.conjugate(), MagicBasisTransform::Into) * k2) @@ -1166,16 +1157,6 @@ struct GateDecompositionPattern final }); return zz * yy * xx; }; - helpers::print(getCanonicalMatrix(a * -2.0, b * -2.0, c * -2.0), - "SANITY CHECK (2.1)"); - helpers::print(helpers::kroneckerProduct(K1l, K1r), "SANITY CHECK (2.2)"); - helpers::print(helpers::kroneckerProduct(K2l, K2r), "SANITY CHECK (2.3)"); - helpers::print((helpers::kroneckerProduct(K1l, K1r) * - getCanonicalMatrix(a * -2.0, b * -2.0, c * -2.0) * - helpers::kroneckerProduct(K2l, K2r) * - std::exp(IM * globalPhase)) - .eval(), - "SANITY CHECK (2.x)"); assert((helpers::kroneckerProduct(K1l, K1r) * getCanonicalMatrix(a * -2.0, b * -2.0, c * -2.0) * helpers::kroneckerProduct(K2l, K2r) * std::exp(IM * globalPhase)) @@ -1537,14 +1518,6 @@ struct GateDecompositionPattern final } specialized.globalPhase += std::arg(tr); - helpers::print( - (helpers::kroneckerProduct(specialized.k1l, specialized.k1r) * - getCanonicalMatrix(specialized.a * -2.0, specialized.b * -2.0, - specialized.c * -2.0) * - helpers::kroneckerProduct(specialized.k2l, specialized.k2r) * - std::exp(IM * specialized.globalPhase)) - .eval(), - "SANITY CHECK (3)"); assert((helpers::kroneckerProduct(specialized.k1l, specialized.k1r) * getCanonicalMatrix(specialized.a * -2.0, specialized.b * -2.0, specialized.c * -2.0) * @@ -2017,9 +1990,6 @@ struct GateDecompositionPattern final OneQubitGateSequence bestCircuit; for (auto targetBasis : targetBasisList) { auto circuit = generateCircuit(targetBasis, unitaryMat, simplify, atol); - helpers::print(circuit.getUnitaryMatrix(), "SANITY CHECK (4.1)"); - helpers::print(helpers::kroneckerProduct(IDENTITY_GATE, unitaryMat), - "SANITY CHECK (4.2)"); assert(circuit.getUnitaryMatrix().isApprox( helpers::kroneckerProduct(IDENTITY_GATE, unitaryMat), SANITY_CHECK_PRECISION)); From 0174a3de28c765732f8a12babeb464ba1d7e2215 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 20 Nov 2025 16:46:27 +0100 Subject: [PATCH 091/237] properly add Eigen lib to cmake --- CMakeLists.txt | 9 +++++++++ mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 86f93ccc8e..5114b54f2d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,15 @@ include(PreventInSourceBuilds) include(PackageAddTest) include(Cache) include(AddMQTCoreLibrary) +include(FetchContent) + +FetchContent_Declare( + Eigen + GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git + GIT_TAG 3.4.1 + GIT_SHALLOW TRUE +) +FetchContent_MakeAvailable(Eigen) option(BUILD_MQT_CORE_BINDINGS "Build the MQT Core Python bindings" OFF) if(BUILD_MQT_CORE_BINDINGS) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt b/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt index e7f1f55dac..7bff1278b6 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt +++ b/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt @@ -7,7 +7,7 @@ # Licensed under the MIT License get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) -set(LIBRARIES ${dialect_libs} MQT::CoreIR) +set(LIBRARIES ${dialect_libs} MQT::CoreIR Eigen3::Eigen) add_compile_options(-fexceptions) file(GLOB_RECURSE TRANSFORMS_SOURCES *.cpp) From 3c707135049c28e29654c3c80f8df017cadf5b8f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 20 Nov 2025 16:46:38 +0100 Subject: [PATCH 092/237] remove unnecessary correction --- .../MQTOpt/Transforms/GateDecompositionPattern.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 35f6be2f0e..dd7e46ca8e 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -620,17 +620,6 @@ struct GateDecompositionPattern final auto [U, S] = helpers::selfAdjointEvd(std::forward(a)); - // TODO: not in original code - if (std::real(U.determinant()) < 0.0) { - std::cerr << "CORRECTION!\n"; - // if determinant of eigenvector matrix is -1.0, multiply first - // eigenvector by -1.0 - U.col(0) *= -1.0; - // U.col(U.cols() - 1) *= -1.0; - // U *= -1.0; - // U += std::remove_cvref_t::Constant(0.0); // ensure no -0.0 exists - } - return std::make_pair(U, S); } From 9f645a01de03a5334430b69c5dbe4831417d8569 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 15:47:06 +0000 Subject: [PATCH 093/237] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5114b54f2d..e8096ec2e8 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,9 +28,8 @@ include(FetchContent) FetchContent_Declare( Eigen GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git - GIT_TAG 3.4.1 - GIT_SHALLOW TRUE -) + GIT_TAG 3.4.1 + GIT_SHALLOW TRUE) FetchContent_MakeAvailable(Eigen) option(BUILD_MQT_CORE_BINDINGS "Build the MQT Core Python bindings" OFF) From b8f6b0bdfba2198835acddffb40301f49caf1c23 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 20 Nov 2025 17:24:43 +0100 Subject: [PATCH 094/237] fix eigen compilation issues --- .../MQTOpt/Transforms/GateDecompositionPattern.cpp | 6 +++--- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 11 ++++------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index dd7e46ca8e..1deb1bd4e5 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -16,8 +16,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -27,6 +26,7 @@ #include #include #include +#include #include namespace mqt::ir::opt { @@ -1024,7 +1024,7 @@ struct GateDecompositionPattern final auto tmp = remEuclid(x, qc::PI_2); return std::min(tmp, qc::PI_2 - tmp); }); - std::array order{ + std::array order{ 0, 1, 2}; // TODO: needs to be adjusted depending on eigenvector // order in eigen decomposition algorithm? llvm::stable_sort(order, diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index d8ab373462..1167af4658 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -14,9 +14,9 @@ #include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" #include -#include -#include -#include // TODO: unstable +#include +#include +#include // TODO: unstable #include // TODO: remove #include // TODO: remove #include @@ -207,10 +207,7 @@ getParameters(UnitaryInterface op) { template inline Eigen::Matrix4 kroneckerProduct(const Eigen::Matrix2& lhs, const Eigen::Matrix2& rhs) { - Eigen::Matrix4 result; - Eigen::KroneckerProduct kroneckerProduct{lhs, rhs}; - kroneckerProduct.evalTo(result); - return result; + return Eigen::kroneckerProduct(lhs, rhs); } template From 5cd8ca4c7a876c6cddee157eed8aeb8fa58c0708 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 20 Nov 2025 18:14:03 +0100 Subject: [PATCH 095/237] more cleanup --- .../Transforms/GateDecompositionPattern.cpp | 51 +++++++++++++++---- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 47 ++--------------- 2 files changed, 45 insertions(+), 53 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 1deb1bd4e5..3f82ccc234 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -9,6 +9,7 @@ */ #include "Helpers.h" +#include "ir/Definitions.hpp" #include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" #include "mlir/Dialect/MQTOpt/Transforms/Passes.h" @@ -32,7 +33,9 @@ namespace mqt::ir::opt { /** - * @brief This pattern attempts to cancel consecutive self-inverse operations. + * @brief This pattern attempts to collect as many operations as possible into a + * 4x4 unitary matrix and then decompose it into rotation and given basis + * gates. */ struct GateDecompositionPattern final : mlir::OpInterfaceRewritePattern { @@ -70,7 +73,13 @@ struct GateDecompositionPattern final */ std::vector gates; + /** + * Global phase adjustment required for the sequence. + */ fp globalPhase{}; + /** + * @return true if the global phase adjustment is not zero. + */ [[nodiscard]] bool hasGlobalPhase() const { return std::abs(globalPhase) > DEFAULT_ATOL; } @@ -106,7 +115,14 @@ struct GateDecompositionPattern final return unitaryMatrix; } }; + /** + * Helper type to show that a gate sequence is supposed to only contain + * single-qubit gates. + */ using OneQubitGateSequence = QubitGateSequence; + /** + * Helper type to show that the gate sequence may contain two-qubit gates. + */ using TwoQubitGateSequence = QubitGateSequence; /** @@ -1518,7 +1534,15 @@ struct GateDecompositionPattern final } }; + /** + * Factor by which two matrices are considered to be the same when simplifying + * during a decomposition. + */ static constexpr auto DEFAULT_FIDELITY = 1.0 - 1e-15; + /** + * Largest number that will be assumed as zero for the euler decompositions + * and the global phase. + */ static constexpr auto DEFAULT_ATOL = 1e-12; /** @@ -1922,7 +1946,7 @@ struct GateDecompositionPattern final * necessary to construct an equivalent to the canonical gate. */ [[nodiscard]] std::array - traces(TwoQubitWeylDecomposition target) const { + traces(const TwoQubitWeylDecomposition& target) const { return { static_cast(4.) * qfp(std::cos(target.a) * std::cos(target.b) * std::cos(target.c), @@ -1938,10 +1962,13 @@ struct GateDecompositionPattern final }; } - static OneQubitGateSequence generateCircuit(EulerBasis targetBasis, - const matrix2x2& unitaryMatrix, - bool simplify, - std::optional atol) { + /** + * Perform single-qubit decomposition of a 2x2 unitary matrix based on a + * given euler basis. + */ + [[nodiscard]] static OneQubitGateSequence + generateCircuit(EulerBasis targetBasis, const matrix2x2& unitaryMatrix, + bool simplify, std::optional atol) { auto [theta, phi, lambda, phase] = anglesFromUnitary(unitaryMatrix, targetBasis); @@ -1964,12 +1991,16 @@ struct GateDecompositionPattern final } } - static OneQubitGateSequence unitaryToGateSequenceInner( - matrix2x2 unitaryMat, + /** + * Decompose a single-qubit unitary matrix into a single-qubit gate + * sequence. Multiple euler bases may be specified and the one with the + * least complexity will be chosen. + */ + [[nodiscard]] static OneQubitGateSequence unitaryToGateSequenceInner( + const matrix2x2& unitaryMat, const llvm::SmallVector& targetBasisList, QubitId /*qubit*/, const std::vector>& - /*error_map*/, // TODO: remove error_map+qubit for platform - // independence + /*error_map*/, // per qubit a mapping of operation name to error value bool simplify, std::optional atol) { auto calculateError = [](const OneQubitGateSequence& sequence) -> fp { return static_cast(sequence.complexity()); diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index 1167af4658..1ab09dea1d 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -10,27 +10,25 @@ #pragma once +#include "ir/Definitions.hpp" #include "ir/operations/OpType.hpp" #include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include #include #include -#include // TODO: unstable -#include // TODO: remove -#include // TODO: remove +#include #include #include +#include // TODO: unstable namespace mqt::ir::opt { -using fp = double; +using fp = qc::fp; using qfp = std::complex; using matrix2x2 = Eigen::Matrix2; using matrix4x4 = Eigen::Matrix4; using rmatrix4x4 = Eigen::Matrix4; using diagonal4x4 = Eigen::Vector; using rdiagonal4x4 = Eigen::Vector; -; constexpr qfp C_ZERO{0., 0.}; constexpr qfp C_ONE{1., 0.}; @@ -42,43 +40,6 @@ constexpr qfp M_IM{0., -1.}; namespace mqt::ir::opt::helpers { -inline void print(std::size_t x) { std::cerr << x; } -inline void print(fp x) { std::cerr << x; } - -inline void print(qfp x) { - std::cerr << std::setprecision(17) << x.real() << 'i' << x.imag(); -} - -// TODO: remove -template -void print(Eigen::Matrix matrix, const std::string& s = "", - bool force = false) { - if (!force) { - return; - } - if (!s.empty()) { - llvm::errs() << "=== " << s << " ===\n"; - } - std::cerr << matrix; - llvm::errs() << '\n'; -} - -template -void print(T matrix, const std::string& s = "", bool force = false) { - if (!force) { - return; - } - if (!s.empty()) { - llvm::errs() << "=== " << s << " ===\n"; - } - - for (auto&& a : matrix) { - print(a); - std::cerr << ' '; - } - llvm::errs() << '\n'; -} - std::optional mlirValueToFp(mlir::Value value); template From 412a8295d7deb0d5d2081c573f5d269ea237f0df Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 21 Nov 2025 16:48:18 +0100 Subject: [PATCH 096/237] clean up weyl decomposition creation --- .../Transforms/GateDecompositionPattern.cpp | 781 +++++++++--------- 1 file changed, 382 insertions(+), 399 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 3f82ccc234..4cf968f3fd 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -88,6 +88,8 @@ struct GateDecompositionPattern final * Calculate complexity of sequence according to getComplexity(). */ [[nodiscard]] std::size_t complexity() const { + // TODO: add more sophisticated metric to determine complexity of + // series/sequence // TODO: caching mechanism? std::size_t c{}; for (auto&& gate : gates) { @@ -164,8 +166,8 @@ struct GateDecompositionPattern final } matrix4x4 unitaryMatrix = series.getUnitaryMatrix(); - auto targetDecomposition = TwoQubitWeylDecomposition::create( - unitaryMatrix, DEFAULT_FIDELITY, std::nullopt); + auto targetDecomposition = + TwoQubitWeylDecomposition::create(unitaryMatrix, DEFAULT_FIDELITY); std::optional bestSequence; for (const auto& decomposer : basisDecomposers) { @@ -186,8 +188,6 @@ struct GateDecompositionPattern final // only accept new sequence if it shortens existing series by more than two // gates; this prevents an oscillation with phase gates if (bestSequence->complexity() + 2 >= series.complexity) { - // TODO: add more sophisticated metric to determine complexity of - // series/sequence llvm::errs() << "SEQUENCE LONGER THAN INPUT (" << bestSequence->gates.size() << "; " << bestSequence->complexity() << " vs " << series.complexity @@ -583,13 +583,14 @@ struct GateDecompositionPattern final } enum class Specialization : std::uint8_t { - General, - IdEquiv, - SWAPEquiv, - PartialSWAPEquiv, - PartialSWAPFlipEquiv, - ControlledEquiv, - MirrorControlledEquiv, + General, // canonical gate has no special symmetry. + IdEquiv, // canonical gate is identity. + SWAPEquiv, // canonical gate is SWAP. + PartialSWAPEquiv, // canonical gate is partial SWAP. + PartialSWAPFlipEquiv, // canonical gate is flipped partial SWAP. + ControlledEquiv, // canonical gate is a controlled gate. + MirrorControlledEquiv, // canonical gate is swap + controlled gate. + // These next 3 gates use the definition of fSim from eq (1) in: // https://arxiv.org/pdf/2001.08343.pdf FSimaabEquiv, @@ -933,7 +934,9 @@ struct GateDecompositionPattern final matrix2x2 k2r; // "right" qubit before canonical gate Specialization specialization; // detected symmetries in the matrix EulerBasis defaultEulerBasis; // recommended euler basis for k1l/k2l/k1r/k2r - std::optional requestedFidelity; // desired fidelity + std::optional requestedFidelity; // desired fidelity; + // if set to std::nullopt, no automatic + // specialization will be applied fp calculatedFidelity; // actual fidelity of decomposition matrix4x4 unitaryMatrix; // original matrix for this decomposition @@ -948,9 +951,8 @@ struct GateDecompositionPattern final * gates. * @param specialization Force the use this specialization. */ - static TwoQubitWeylDecomposition - create(matrix4x4 unitaryMatrix, std::optional fidelity, - std::optional specialization) { + static TwoQubitWeylDecomposition create(const matrix4x4& unitaryMatrix, + std::optional fidelity) { auto u = unitaryMatrix; auto detU = u.determinant(); auto detPow = std::pow(detU, static_cast(-0.25)); @@ -960,74 +962,14 @@ struct GateDecompositionPattern final matrix4x4 m2 = uP.transpose() * uP; auto defaultEulerBasis = EulerBasis::ZYZ; - // M2 is a symmetric complex matrix. We need to decompose it as M2 = P D - // P^T where P ∈ SO(4), D is diagonal with unit-magnitude elements. - // - // We can't use raw `eig` directly because it isn't guaranteed to give - // us real or orthogonal eigenvectors. Instead, since `M2` is - // complex-symmetric, - // M2 = A + iB - // for real-symmetric `A` and `B`, and as - // M2^+ @ M2 = A^2 + B^2 + i [A, B] = 1 - // we must have `A` and `B` commute, and consequently they are - // simultaneously diagonalizable. Mixing them together _should_ account - // for any degeneracy problems, but it's not guaranteed, so we repeat it - // a little bit. The fixed seed is to make failures deterministic; the - // value is not important. - auto state = std::mt19937{2023}; - std::normal_distribution dist; - auto found = false; - diagonal4x4 d = diagonal4x4::Zero(); - matrix4x4 p = matrix4x4::Zero(); + // diagonalization yields eigenvectors (p) and eigenvalues (d); + // p is used to calculate K1/K2 (and thus the single-qubit gates + // surrounding the canonical gate); d is is used to determine the weyl + // coordinates and thus the parameters of the canonical gate + // TODO: it may be possible to lower the precision + auto [p, d] = diagonalizeComplexSymmetric(m2, 1e-13); - for (int i = 0; i < 100; ++i) { - fp randA{}; - fp randB{}; - // For debugging the algorithm use the same RNG values from the - // previous Python implementation for the first random trial. - // In most cases this loop only executes a single iteration and - // using the same rng values rules out possible RNG differences - // as the root cause of a test failure - if (i == 0) { - randA = 1.2602066112249388; - randB = 0.22317849046722027; - } else { - randA = dist(state); - randB = dist(state); - } - rmatrix4x4 m2Real = randA * m2.real() + randB * m2.imag(); - rmatrix4x4 pInnerReal = selfAdjointEigenLower(m2Real).first; - matrix4x4 pInner = pInnerReal; - diagonal4x4 dInner = (pInner.transpose() * m2 * pInner).diagonal(); - - matrix4x4 diagD = dInner.asDiagonal(); - - matrix4x4 compare = pInner * diagD * pInner.transpose(); - found = compare.isApprox(m2, 1e-13); - if (found) { - // p are the eigenvectors which are decomposed into the - // single-qubit gates surrounding the canonical gate - p = pInner; - // d is the sqrt of the eigenvalues that are used to determine the - // weyl coordinates and thus the parameters of the canonical gate - d = dInner; - break; - } - } - if (!found) { - throw std::runtime_error{ - "TwoQubitWeylDecomposition: failed to diagonalize M2."}; - } - // check that p is in SO(4) - assert((p.transpose() * p).isIdentity(SANITY_CHECK_PRECISION)); - // make sure determinant of sqrt(eigenvalues) is 1.0 - assert(std::abs(matrix4x4{d.asDiagonal()}.determinant() - 1.0) < - SANITY_CHECK_PRECISION); - - // see - // https://github.com/mpham26uchicago/laughing-umbrella/blob/main/background/Full%20Two%20Qubit%20KAK%20Implementation.ipynb, - // Step 7 - Eigen::Vector cs; + Eigen::Vector cs; // weyl coordinates rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; dReal(3) = -dReal(0) - dReal(1) - dReal(2); for (int i = 0; i < static_cast(cs.size()); ++i) { @@ -1035,14 +977,14 @@ struct GateDecompositionPattern final cs[i] = remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); } + // re-order coordinates and according to min(a, pi/2 - a) with + // a = x mod pi/2 for each weyl coordinate x decltype(cs) cstemp; llvm::transform(cs, cstemp.begin(), [](auto&& x) { auto tmp = remEuclid(x, qc::PI_2); return std::min(tmp, qc::PI_2 - tmp); }); - std::array order{ - 0, 1, 2}; // TODO: needs to be adjusted depending on eigenvector - // order in eigen decomposition algorithm? + std::array order{0, 1, 2}; llvm::stable_sort(order, [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); std::tie(order[0], order[1], order[2]) = @@ -1052,22 +994,22 @@ struct GateDecompositionPattern final std::tie(dReal(0), dReal(1), dReal(2)) = std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2])}; - // swap columns of p according to order + // update columns of p according to order matrix4x4 pOrig = p; for (int i = 0; i < static_cast(order.size()); ++i) { p.col(i) = pOrig.col(order[i]); } + // apply correction for determinant if necessary if (p.determinant().real() < 0.0) { auto lastColumnIndex = p.cols() - 1; p.col(lastColumnIndex) *= -1.0; } + assert(std::abs(p.determinant() - 1.0) < SANITY_CHECK_PRECISION); matrix4x4 temp = dReal.asDiagonal(); temp *= IM; temp = temp.exp(); - assert(std::abs(p.determinant() - 1.0) < SANITY_CHECK_PRECISION); - matrix4x4 k1 = uP * p * temp; assert((k1.transpose() * k1).isIdentity()); // k1 must be orthogonal assert(k1.determinant().real() > 0.0); @@ -1077,12 +1019,15 @@ struct GateDecompositionPattern final assert(k2.determinant().real() > 0.0); k2 = magicBasisTransform(k2, MagicBasisTransform::Into); + // ensure k1 and k2 are correct assert((k1 * magicBasisTransform(temp.conjugate(), MagicBasisTransform::Into) * k2) .isApprox(u, SANITY_CHECK_PRECISION)); + // calculate k1 = K1l⊗ K1r auto [K1l, K1r, phase_l] = decomposeTwoQubitProductGate(k1); + // decompose k2 = K2l⊗ K2r auto [K2l, K2r, phase_r] = decomposeTwoQubitProductGate(k2); assert(helpers::kroneckerProduct(K1l, K1r).isApprox( k1, SANITY_CHECK_PRECISION)); @@ -1144,79 +1089,13 @@ struct GateDecompositionPattern final } auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); - auto getCanonicalMatrix = [](fp a, fp b, fp c) -> matrix4x4 { - auto xx = getTwoQubitMatrix({ - .type = qc::RXX, - .parameter = {a}, - .qubitId = {0, 1}, - }); - auto yy = getTwoQubitMatrix({ - .type = qc::RYY, - .parameter = {b}, - .qubitId = {0, 1}, - }); - auto zz = getTwoQubitMatrix({ - .type = qc::RZZ, - .parameter = {c}, - .qubitId = {0, 1}, - }); - return zz * yy * xx; - }; + // make sure decomposition is equal to input assert((helpers::kroneckerProduct(K1l, K1r) * getCanonicalMatrix(a * -2.0, b * -2.0, c * -2.0) * helpers::kroneckerProduct(K2l, K2r) * std::exp(IM * globalPhase)) .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); - auto isClose = [&](fp ap, fp bp, fp cp) -> bool { - auto da = a - ap; - auto db = b - bp; - auto dc = c - cp; - auto tr = static_cast(4.) * - qfp(std::cos(da) * std::cos(db) * std::cos(dc), - std::sin(da) * std::sin(db) * std::sin(dc)); - if (fidelity) { - return traceToFidelity(tr) >= *fidelity; - } - return false; - }; - - auto closestAbc = closestPartialSwap(a, b, c); - auto closestAbMinusC = closestPartialSwap(a, b, -c); - auto flippedFromOriginal = false; - auto getDefaultSpecialzation = [&]() { - if (isClose(0., 0., 0.)) { - return Specialization::IdEquiv; - } - if (isClose(qc::PI_4, qc::PI_4, qc::PI_4) || - isClose(qc::PI_4, qc::PI_4, -qc::PI_4)) { - return Specialization::SWAPEquiv; - } - if (isClose(closestAbc, closestAbc, closestAbc)) { - return Specialization::PartialSWAPEquiv; - } - if (isClose(closestAbMinusC, closestAbMinusC, -closestAbMinusC)) { - return Specialization::PartialSWAPFlipEquiv; - } - if (isClose(a, 0., 0.)) { - return Specialization::ControlledEquiv; - } - if (isClose(qc::PI_4, qc::PI_4, c)) { - return Specialization::MirrorControlledEquiv; - } - if (isClose((a + b) / 2., (a + b) / 2., c)) { - return Specialization::FSimaabEquiv; - } - if (isClose(a, (b + c) / 2., (b + c) / 2.)) { - return Specialization::FSimabbEquiv; - } - if (isClose(a, (b - c) / 2., (c - b) / 2.)) { - return Specialization::FSimabmbEquiv; - } - return Specialization::General; - }; - auto actualSpecialization = - specialization.value_or(getDefaultSpecialzation()); - TwoQubitWeylDecomposition general{ + TwoQubitWeylDecomposition decomposition{ .a = a, .b = b, .c = c, @@ -1231,96 +1110,272 @@ struct GateDecompositionPattern final .calculatedFidelity = -1.0, .unitaryMatrix = unitaryMatrix, }; - auto getSpecializedDecomposition = [&]() { + + // determine actual specialization of canonical gate so that the 1q + // matrices can potentially be simplified + auto flippedFromOriginal = decomposition.applySpecialization(); + + auto getTrace = [&]() { + if (flippedFromOriginal) { + auto [da, db, dc] = std::array{ + qc::PI_2 - a - decomposition.a, + b - decomposition.b, + -c - decomposition.c, + }; + return static_cast(4.) * + qfp(std::cos(da) * std::cos(db) * std::cos(dc), + std::sin(da) * std::sin(db) * std::sin(dc)); + } + auto [da, db, dc] = std::array{a - decomposition.a, b - decomposition.b, + c - decomposition.c}; + return static_cast(4.) * + qfp(std::cos(da) * std::cos(db) * std::cos(dc), + std::sin(da) * std::sin(db) * std::sin(dc)); + }; + auto trace = getTrace(); + decomposition.calculatedFidelity = traceToFidelity(trace); + // final check if specialization is close enough to the original matrix to + // satisfy the requested fidelity; since no forced specialization is + // allowed, this should never fail + if (decomposition.requestedFidelity) { + if (decomposition.calculatedFidelity + 1.0e-13 < + *decomposition.requestedFidelity) { + throw std::runtime_error{ + "TwoQubitWeylDecomposition: Calculated fidelity of " + "specialization is worse than requested fidelity!"}; + } + } + decomposition.globalPhase += std::arg(trace); + + // final check if decomposition is still valid after specialization + assert((helpers::kroneckerProduct(decomposition.k1l, decomposition.k1r) * + getCanonicalMatrix(decomposition.a * -2.0, decomposition.b * -2.0, + decomposition.c * -2.0) * + helpers::kroneckerProduct(decomposition.k2l, decomposition.k2r) * + std::exp(IM * decomposition.globalPhase)) + .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); + + return decomposition; + } + + /** + * Calculate matrix of canonical gate based on its parameters a, b, c. + */ + static matrix4x4 getCanonicalMatrix(fp a, fp b, fp c) { + auto xx = getTwoQubitMatrix({ + .type = qc::RXX, + .parameter = {a}, + .qubitId = {0, 1}, + }); + auto yy = getTwoQubitMatrix({ + .type = qc::RYY, + .parameter = {b}, + .qubitId = {0, 1}, + }); + auto zz = getTwoQubitMatrix({ + .type = qc::RZZ, + .parameter = {c}, + .qubitId = {0, 1}, + }); + return zz * yy * xx; + } + + private: + /** + * Diagonalize given complex symmetric matrix M into (P, d) using a + * randomized algorithm. + * This approach is used in both qiskit and quantumflow. + * + * P is the matrix of real or orthogonal eigenvectors of M with P ∈ SO(4) + * d is a vector containing sqrt(eigenvalues) of M with unit-magnitude + * elements (for each element, complex magnitude is 1.0). + * D is d as a diagonal matrix. + * + * M = P * D * P^T + * + * @return pair of (P, D.diagonal()) + */ + [[nodiscard]] static std::pair + diagonalizeComplexSymmetric(const matrix4x4& m, fp precision) { + // We can't use raw `eig` directly because it isn't guaranteed to give + // us real or orthogonal eigenvectors. Instead, since `M` is + // complex-symmetric, + // M = A + iB + // for real-symmetric `A` and `B`, and as + // M^+ @ M2 = A^2 + B^2 + i [A, B] = 1 + // we must have `A` and `B` commute, and consequently they are + // simultaneously diagonalizable. Mixing them together _should_ account + // for any degeneracy problems, but it's not guaranteed, so we repeat it + // a little bit. The fixed seed is to make failures deterministic; the + // value is not important. + auto state = std::mt19937{2023}; + std::normal_distribution dist; + + for (int i = 0; i < 100; ++i) { + fp randA{}; + fp randB{}; + // For debugging the algorithm use the same RNG values from the + // previous Python implementation for the first random trial. + // In most cases this loop only executes a single iteration and + // using the same rng values rules out possible RNG differences + // as the root cause of a test failure + if (i == 0) { + randA = 1.2602066112249388; + randB = 0.22317849046722027; + } else { + randA = dist(state); + randB = dist(state); + } + rmatrix4x4 m2Real = randA * m.real() + randB * m.imag(); + rmatrix4x4 pInnerReal = selfAdjointEigenLower(m2Real).first; + matrix4x4 p = pInnerReal; + diagonal4x4 d = (p.transpose() * m * p).diagonal(); + + matrix4x4 diagD = d.asDiagonal(); + + matrix4x4 compare = p * diagD * p.transpose(); + if (compare.isApprox(m, precision)) { + // p are the eigenvectors which are decomposed into the + // single-qubit gates surrounding the canonical gate + // d is the sqrt of the eigenvalues that are used to determine the + // weyl coordinates and thus the parameters of the canonical gate + // check that p is in SO(4) + assert((p.transpose() * p).isIdentity(SANITY_CHECK_PRECISION)); + // make sure determinant of sqrt(eigenvalues) is 1.0 + assert(std::abs(matrix4x4{d.asDiagonal()}.determinant() - 1.0) < + SANITY_CHECK_PRECISION); + return std::make_pair(p, d); + } + } + throw std::runtime_error{ + "TwoQubitWeylDecomposition: failed to diagonalize M2."}; + } + + [[nodiscard]] Specialization bestSpecialization() const { + auto isClose = [this](fp ap, fp bp, fp cp) -> bool { + auto da = a - ap; + auto db = b - bp; + auto dc = c - cp; + auto tr = static_cast(4.) * + qfp(std::cos(da) * std::cos(db) * std::cos(dc), + std::sin(da) * std::sin(db) * std::sin(dc)); + if (requestedFidelity) { + return traceToFidelity(tr) >= *requestedFidelity; + } + return false; + }; + + auto closestAbc = closestPartialSwap(a, b, c); + auto closestAbMinusC = closestPartialSwap(a, b, -c); + + if (isClose(0., 0., 0.)) { + return Specialization::IdEquiv; + } + if (isClose(qc::PI_4, qc::PI_4, qc::PI_4) || + isClose(qc::PI_4, qc::PI_4, -qc::PI_4)) { + return Specialization::SWAPEquiv; + } + if (isClose(closestAbc, closestAbc, closestAbc)) { + return Specialization::PartialSWAPEquiv; + } + if (isClose(closestAbMinusC, closestAbMinusC, -closestAbMinusC)) { + return Specialization::PartialSWAPFlipEquiv; + } + if (isClose(a, 0., 0.)) { + return Specialization::ControlledEquiv; + } + if (isClose(qc::PI_4, qc::PI_4, c)) { + return Specialization::MirrorControlledEquiv; + } + if (isClose((a + b) / 2., (a + b) / 2., c)) { + return Specialization::FSimaabEquiv; + } + if (isClose(a, (b + c) / 2., (b + c) / 2.)) { + return Specialization::FSimabbEquiv; + } + if (isClose(a, (b - c) / 2., (c - b) / 2.)) { + return Specialization::FSimabmbEquiv; + } + return Specialization::General; + } + + /** + * @return true if the specialization flipped the original decomposition + */ + bool applySpecialization() { + if (specialization != Specialization::General) { + throw std::logic_error{"Application of specialization only works on " + "general decomposition!"}; + } + bool flippedFromOriginal = false; + auto newSpecialization = bestSpecialization(); + if (newSpecialization == Specialization::General) { + // U has no special symmetry. + // + // This gate binds all 6 possible parameters, so there is no need to + // make the single-qubit pre-/post-gates canonical. + return flippedFromOriginal; + } + specialization = newSpecialization; + + if (newSpecialization == Specialization::IdEquiv) { // :math:`U \sim U_d(0,0,0) \sim Id` // // This gate binds 0 parameters, we make it canonical by // setting // :math:`K2_l = Id` , :math:`K2_r = Id`. - if (actualSpecialization == Specialization::IdEquiv) { - return TwoQubitWeylDecomposition{ - .a = 0., - .b = 0., - .c = 0., - .globalPhase = general.globalPhase, - .k1l = general.k1l * general.k2l, - .k2l = IDENTITY_GATE, - .k1r = general.k1r * general.k2r, - .k2r = IDENTITY_GATE, - .specialization = actualSpecialization, - .defaultEulerBasis = general.defaultEulerBasis, - .requestedFidelity = general.requestedFidelity, - .calculatedFidelity = general.calculatedFidelity, - .unitaryMatrix = general.unitaryMatrix, - }; - } + a = 0.; + b = 0.; + c = 0.; + // unmodified global phase + k1l = k1l * k2l; + k2l = IDENTITY_GATE; + k1r = k1r * k2r; + k2r = IDENTITY_GATE; + } else if (newSpecialization == Specialization::SWAPEquiv) { // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, // -\pi/4) \sim \text{SWAP}` // // This gate binds 0 parameters, we make it canonical by // setting // :math:`K2_l = Id` , :math:`K2_r = Id`. - if (actualSpecialization == Specialization::SWAPEquiv) { - if (c > 0.) { - return TwoQubitWeylDecomposition{ - .a = qc::PI_4, - .b = qc::PI_4, - .c = qc::PI_4, - .globalPhase = general.globalPhase, - .k1l = general.k1l * general.k2r, - .k2l = IDENTITY_GATE, - .k1r = general.k1r * general.k2l, - .k2r = IDENTITY_GATE, - .specialization = actualSpecialization, - .defaultEulerBasis = general.defaultEulerBasis, - .requestedFidelity = general.requestedFidelity, - .calculatedFidelity = general.calculatedFidelity, - .unitaryMatrix = general.unitaryMatrix, - }; - } + a = qc::PI_4; + b = qc::PI_4; + c = qc::PI_4; + if (c > 0.) { + // unmodified global phase + k1l = k1l * k2r; + k1r = k1r * k2l; + k2l = IDENTITY_GATE; + k2r = IDENTITY_GATE; + } else { flippedFromOriginal = true; - return TwoQubitWeylDecomposition{ - .a = qc::PI_4, - .b = qc::PI_4, - .c = qc::PI_4, - .globalPhase = globalPhase + qc::PI_2, - .k1l = general.k1l * IPZ * general.k2r, - .k2l = IDENTITY_GATE, - .k1r = general.k1r * IPZ * general.k2l, - .k2r = IDENTITY_GATE, - .specialization = actualSpecialization, - .defaultEulerBasis = general.defaultEulerBasis, - .requestedFidelity = general.requestedFidelity, - .calculatedFidelity = general.calculatedFidelity, - .unitaryMatrix = general.unitaryMatrix, - }; + + globalPhase += qc::PI_2; + k1l = k1l * IPZ * k2r; + k1r = k1r * IPZ * k2l; + k2l = IDENTITY_GATE; + k2r = IDENTITY_GATE; } + } else if (newSpecialization == Specialization::PartialSWAPEquiv) { // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4) \sim // \text{SWAP}^\alpha` // // This gate binds 3 parameters, we make it canonical by setting: // // :math:`K2_l = Id`. - if (actualSpecialization == Specialization::PartialSWAPEquiv) { - auto closest = closestPartialSwap(a, b, c); - auto k2lDag = general.k2l.transpose().conjugate(); - - return TwoQubitWeylDecomposition{ - .a = closest, - .b = closest, - .c = closest, - .globalPhase = general.globalPhase, - .k1l = general.k1l * general.k2l, - .k2l = IDENTITY_GATE, - .k1r = general.k1r * general.k2l, - .k2r = k2lDag * general.k2r, - .specialization = actualSpecialization, - .defaultEulerBasis = general.defaultEulerBasis, - .requestedFidelity = general.requestedFidelity, - .calculatedFidelity = general.calculatedFidelity, - .unitaryMatrix = general.unitaryMatrix, - }; - } + auto closest = closestPartialSwap(a, b, c); + auto k2lDagger = k2l.transpose().conjugate(); + + a = closest; + b = closest; + c = closest; + // unmodified global phase + k1l = k1l * k2l; + k1r = k1r * k2l; + k2r = k2lDagger * k2r; + k2l = IDENTITY_GATE; + } else if (newSpecialization == Specialization::PartialSWAPFlipEquiv) { // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4) \sim // \text{SWAP}^\alpha` // @@ -1331,54 +1386,40 @@ struct GateDecompositionPattern final // This gate binds 3 parameters, we make it canonical by setting: // // :math:`K2_l = Id` - if (actualSpecialization == Specialization::PartialSWAPFlipEquiv) { - auto closest = closestPartialSwap(a, b, -c); - auto k2lDag = general.k2l.transpose().conjugate(); - - return TwoQubitWeylDecomposition{ - .a = closest, - .b = closest, - .c = -closest, - .globalPhase = general.globalPhase, - .k1l = general.k1l * general.k2l, - .k2l = IDENTITY_GATE, - .k1r = general.k1r * IPZ * general.k2l * IPZ, - .k2r = IPZ * k2lDag * IPZ * general.k2r, - .specialization = actualSpecialization, - .defaultEulerBasis = general.defaultEulerBasis, - .requestedFidelity = general.requestedFidelity, - .calculatedFidelity = general.calculatedFidelity, - .unitaryMatrix = general.unitaryMatrix, - }; - } + auto closest = closestPartialSwap(a, b, -c); + auto k2lDagger = k2l.transpose().conjugate(); + + a = closest; + b = closest; + c = -closest; + // unmodified global phase + k1l = k1l * k2l; + k1r = k1r * IPZ * k2l * IPZ; + k2r = IPZ * k2lDagger * IPZ * k2r; + k2l = IDENTITY_GATE; + } else if (newSpecialization == Specialization::ControlledEquiv) { // :math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}` // // This gate binds 4 parameters, we make it canonical by setting: // // :math:`K2_l = Ry(\theta_l) Rx(\lambda_l)` , // :math:`K2_r = Ry(\theta_r) Rx(\lambda_r)` . - if (actualSpecialization == Specialization::ControlledEquiv) { - auto eulerBasis = EulerBasis::XYX; - auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - anglesFromUnitary(general.k2l, eulerBasis); - auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = - anglesFromUnitary(general.k2r, eulerBasis); - return TwoQubitWeylDecomposition{ - .a = a, - .b = 0., - .c = 0., - .globalPhase = globalPhase + k2lphase + k2rphase, - .k1l = general.k1l * rxMatrix(k2lphi), - .k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda), - .k1r = general.k1r * rxMatrix(k2rphi), - .k2r = ryMatrix(k2rtheta) * rxMatrix(k2rlambda), - .specialization = actualSpecialization, - .defaultEulerBasis = eulerBasis, - .requestedFidelity = general.requestedFidelity, - .calculatedFidelity = general.calculatedFidelity, - .unitaryMatrix = general.unitaryMatrix, - }; - } + auto eulerBasis = EulerBasis::XYX; + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + anglesFromUnitary(k2l, eulerBasis); + auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = + anglesFromUnitary(k2r, eulerBasis); + + // unmodified parameter a + b = 0.; + c = 0.; + globalPhase = globalPhase + k2lphase + k2rphase; + k1l = k1l * rxMatrix(k2lphi); + k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda); + k1r = k1r * rxMatrix(k2rphi); + k2r = ryMatrix(k2rtheta) * rxMatrix(k2rlambda); + defaultEulerBasis = eulerBasis; + } else if (newSpecialization == Specialization::MirrorControlledEquiv) { // :math:`U \sim U_d(\pi/4, \pi/4, \alpha) \sim \text{SWAP} \cdot // \text{Ctrl-U}` // @@ -1386,151 +1427,81 @@ struct GateDecompositionPattern final // // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` , :math:`K2_r = // Ry(\theta_r)\cdot Rz(\lambda_r)` - if (actualSpecialization == Specialization::MirrorControlledEquiv) { - auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - anglesFromUnitary(general.k2l, EulerBasis::ZYZ); - auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = - anglesFromUnitary(general.k2r, EulerBasis::ZYZ); - return TwoQubitWeylDecomposition{ - .a = qc::PI_4, - .b = qc::PI_4, - .c = c, - .globalPhase = globalPhase + k2lphase + k2rphase, - .k1l = general.k1l * rzMatrix(k2rphi), - .k2l = ryMatrix(k2ltheta) * rzMatrix(k2llambda), - .k1r = general.k1r * rzMatrix(k2lphi), - .k2r = ryMatrix(k2rtheta) * rzMatrix(k2rlambda), - .specialization = actualSpecialization, - .defaultEulerBasis = general.defaultEulerBasis, - .requestedFidelity = general.requestedFidelity, - .calculatedFidelity = general.calculatedFidelity, - .unitaryMatrix = general.unitaryMatrix, - }; - } + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + anglesFromUnitary(k2l, EulerBasis::ZYZ); + auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = + anglesFromUnitary(k2r, EulerBasis::ZYZ); + + a = qc::PI_4; + b = qc::PI_4; + // unmodified parameter c + globalPhase = globalPhase + k2lphase + k2rphase; + k1l = k1l * rzMatrix(k2rphi); + k2l = ryMatrix(k2ltheta) * rzMatrix(k2llambda); + k1r = k1r * rzMatrix(k2lphi); + k2r = ryMatrix(k2rtheta) * rzMatrix(k2rlambda); + } else if (newSpecialization == Specialization::FSimaabEquiv) { // :math:`U \sim U_d(\alpha, \alpha, \beta), \alpha \geq |\beta|` // // This gate binds 5 parameters, we make it canonical by setting: // // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)`. - if (actualSpecialization == Specialization::FSimaabEquiv) { - auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - anglesFromUnitary(general.k2l, EulerBasis::ZYZ); - return TwoQubitWeylDecomposition{ - .a = (a + b) / 2., - .b = (a + b) / 2., - .c = c, - .globalPhase = globalPhase + k2lphase, - .k1l = general.k1l * rzMatrix(k2lphi), - .k2l = ryMatrix(k2ltheta) * rzMatrix(k2llambda), - .k1r = general.k1r * rzMatrix(k2lphi), - .k2r = rzMatrix(-k2lphi) * general.k2r, - .specialization = actualSpecialization, - .defaultEulerBasis = general.defaultEulerBasis, - .requestedFidelity = general.requestedFidelity, - .calculatedFidelity = general.calculatedFidelity, - .unitaryMatrix = general.unitaryMatrix, - }; - } + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + anglesFromUnitary(k2l, EulerBasis::ZYZ); + auto ab = (a + b) / 2.; + + a = ab; + b = ab; + // unmodified parameter c + globalPhase = globalPhase + k2lphase; + k1l = k1l * rzMatrix(k2lphi); + k2l = ryMatrix(k2ltheta) * rzMatrix(k2llambda); + k1r = k1r * rzMatrix(k2lphi); + k2r = rzMatrix(-k2lphi) * k2r; + } else if (newSpecialization == Specialization::FSimabbEquiv) { // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` // // This gate binds 5 parameters, we make it canonical by setting: // // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` - if (actualSpecialization == Specialization::FSimabbEquiv) { - auto eulerBasis = EulerBasis::XYX; - auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - anglesFromUnitary(general.k2l, eulerBasis); - return TwoQubitWeylDecomposition{ - .a = a, - .b = (b + c) / 2., - .c = (b + c) / 2., - .globalPhase = globalPhase + k2lphase, - .k1l = general.k1l * rxMatrix(k2lphi), - .k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda), - .k1r = general.k1r * rxMatrix(k2lphi), - .k2r = rxMatrix(-k2lphi) * general.k2r, - .specialization = actualSpecialization, - .defaultEulerBasis = eulerBasis, - .requestedFidelity = general.requestedFidelity, - .calculatedFidelity = general.calculatedFidelity, - .unitaryMatrix = general.unitaryMatrix, - }; - } + auto eulerBasis = EulerBasis::XYX; + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + anglesFromUnitary(k2l, eulerBasis); + auto bc = (b + c) / 2.; + + // unmodified parameter a + b = bc; + c = bc; + globalPhase = globalPhase + k2lphase; + k1l = k1l * rxMatrix(k2lphi); + k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda); + k1r = k1r * rxMatrix(k2lphi); + k2r = rxMatrix(-k2lphi) * k2r; + defaultEulerBasis = eulerBasis; + } else if (newSpecialization == Specialization::FSimabmbEquiv) { // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` // // This gate binds 5 parameters, we make it canonical by setting: // // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` - if (actualSpecialization == Specialization::FSimabmbEquiv) { - auto eulerBasis = EulerBasis::XYX; - auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - anglesFromUnitary(general.k2l, eulerBasis); - return TwoQubitWeylDecomposition{ - .a = a, - .b = (b - c) / 2., - .c = -((b - c) / 2.), - .globalPhase = globalPhase + k2lphase, - .k1l = general.k1l * rxMatrix(k2lphi), - .k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda), - .k1r = general.k1r * IPZ * rxMatrix(k2lphi) * IPZ, - .k2r = IPZ * rxMatrix(-k2lphi) * IPZ * general.k2r, - .specialization = actualSpecialization, - .defaultEulerBasis = eulerBasis, - .requestedFidelity = general.requestedFidelity, - .calculatedFidelity = general.calculatedFidelity, - .unitaryMatrix = general.unitaryMatrix, - }; - } - // U has no special symmetry. - // - // This gate binds all 6 possible parameters, so there is no need to - // make the single-qubit pre-/post-gates canonical. - if (actualSpecialization == Specialization::General) { - return general; - } + auto eulerBasis = EulerBasis::XYX; + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + anglesFromUnitary(k2l, eulerBasis); + auto bc = (b - c) / 2.; + + // unmodified parameter a + b = bc; + c = -bc; + globalPhase = globalPhase + k2lphase; + k1l = k1l * rxMatrix(k2lphi); + k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda); + k1r = k1r * IPZ * rxMatrix(k2lphi) * IPZ; + k2r = IPZ * rxMatrix(-k2lphi) * IPZ * k2r; + defaultEulerBasis = eulerBasis; + } else { throw std::logic_error{"Unknown specialization"}; - }; - - TwoQubitWeylDecomposition specialized = getSpecializedDecomposition(); - - auto getTr = [&]() { - if (flippedFromOriginal) { - auto [da, db, dc] = std::array{ - qc::PI_2 - a - specialized.a, - b - specialized.b, - -c - specialized.c, - }; - return static_cast(4.) * - qfp(std::cos(da) * std::cos(db) * std::cos(dc), - std::sin(da) * std::sin(db) * std::sin(dc)); - } - auto [da, db, dc] = - std::array{a - specialized.a, b - specialized.b, c - specialized.c}; - return static_cast(4.) * - qfp(std::cos(da) * std::cos(db) * std::cos(dc), - std::sin(da) * std::sin(db) * std::sin(dc)); - }; - auto tr = getTr(); - specialized.calculatedFidelity = traceToFidelity(tr); - if (specialized.requestedFidelity) { - if (specialized.calculatedFidelity + 1.0e-13 < - *specialized.requestedFidelity) { - throw std::runtime_error{ - "Specialization: {:?} calculated fidelity: {} is worse than " - "requested fidelity: {}", - }; - } } - specialized.globalPhase += std::arg(tr); - - assert((helpers::kroneckerProduct(specialized.k1l, specialized.k1r) * - getCanonicalMatrix(specialized.a * -2.0, specialized.b * -2.0, - specialized.c * -2.0) * - helpers::kroneckerProduct(specialized.k2l, specialized.k2r) * - std::exp(IM * specialized.globalPhase)) - .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); - - return specialized; + return flippedFromOriginal; } }; @@ -1575,6 +1546,15 @@ struct GateDecompositionPattern final matrix2x2 q2r; public: + /** + * Create decomposer that allows two-qubit decompositions based on the + * specified basis gate. + * This basis gate will appear between 0 and 3 times in each decompositions. + * The order of qubits is relevant and will change the results accordingly. + * The decomposer cannot handle different basis gates in the same + * decomposition (different order of the qubits also counts as a different + * basis gate). + */ static TwoQubitBasisDecomposer create(const OneQubitGateSequence::Gate& basisGate = {.type = qc::X, .parameter = {}, @@ -1616,7 +1596,7 @@ struct GateDecompositionPattern final }; auto basisDecomposer = TwoQubitWeylDecomposition::create( - getTwoQubitMatrix(basisGate), basisFidelity, std::nullopt); + getTwoQubitMatrix(basisGate), basisFidelity); auto superControlled = relativeEq(basisDecomposer.a, qc::PI_4, 1e-13, 1e-09) && relativeEq(basisDecomposer.c, 0.0, 1e-13, 1e-09); @@ -1763,7 +1743,7 @@ struct GateDecompositionPattern final } return minIndex; }; - // number of basis gates that need to be inserted + // number of basis gates that need to be used in the decomposition auto bestNbasis = numBasisGateUses.value_or(getDefaultNbasis()); auto chooseDecomposition = [&]() { if (bestNbasis == 0) { @@ -1816,12 +1796,15 @@ struct GateDecompositionPattern final }; for (std::size_t i = 0; i < bestNbasis; ++i) { + // add single-qubit decompositions before basis gate addEulerDecomposition(2 * i, 0); addEulerDecomposition((2 * i) + 1, 1); + // add basis gate gates.gates.push_back(basisGate); } + // add single-qubit decompositions after basis gate addEulerDecomposition(2UL * bestNbasis, 0); addEulerDecomposition((2UL * bestNbasis) + 1, 1); From 6f04dbbade5261dd521aebd46e4fca838478f9a3 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 21 Nov 2025 17:15:09 +0100 Subject: [PATCH 097/237] remove openqasm code generation --- .../Transforms/GateDecompositionPattern.cpp | 65 ++----------------- 1 file changed, 6 insertions(+), 59 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 4cf968f3fd..359b09142b 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -26,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -149,11 +149,6 @@ struct GateDecompositionPattern final matchAndRewrite(UnitaryInterface op, mlir::PatternRewriter& rewriter) const override { auto series = TwoQubitSeries::getTwoQubitSeries(op); - llvm::errs() << "SERIES SIZE: " << series.gates.size() << '\n'; - for (auto&& gate : series.gates) { - std::cerr << gate.op->getName().stripDialect().str() << ", "; - } - std::cerr << '\n'; if (series.gates.size() < 3) { // too short @@ -182,16 +177,11 @@ struct GateDecompositionPattern final } } if (!bestSequence) { - llvm::errs() << "NO SEQUENCE GENERATED!\n"; return mlir::failure(); } // only accept new sequence if it shortens existing series by more than two // gates; this prevents an oscillation with phase gates if (bestSequence->complexity() + 2 >= series.complexity) { - llvm::errs() << "SEQUENCE LONGER THAN INPUT (" - << bestSequence->gates.size() << "; " - << bestSequence->complexity() << " vs " << series.complexity - << ")\n"; return mlir::failure(); } @@ -483,57 +473,13 @@ struct GateDecompositionPattern final {}); } - std::cerr << "SERIES: "; - for (auto&& gate : series.gates) { - auto name = gate.op->getName().stripDialect().str(); - if (name == "x" && gate.qubitIds.size() == 2) { - // controls come first - std::cerr << std::format("cx() q[{}], q[{}];", gate.qubitIds[1], - gate.qubitIds[0]); - } else if (name == "i") { - } else if (gate.op.getParams().empty()) { - std::cerr << std::format( - "{}() q[{}] {};", name, gate.qubitIds[0], - (gate.qubitIds.size() > 1 - ? (", q[" + std::to_string(gate.qubitIds[1]) + "]") - : std::string{})); - } else { - auto parameter = helpers::getParameters(gate.op)[0]; - std::cerr << std::format( - "{}({}*pi) q[{}] {};", name, parameter / qc::PI, gate.qubitIds[0], - (gate.qubitIds.size() > 1 - ? (", q[" + std::to_string(gate.qubitIds[1]) + "]") - : std::string{})); - } - } - std::cerr << '\n'; - std::cerr << "GATE SEQUENCE!: gphase(" << sequence.globalPhase / qc::PI - << "*pi); \n"; matrix4x4 unitaryMatrix = helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); for (auto&& gate : sequence.gates) { auto gateMatrix = getTwoQubitMatrix(gate); unitaryMatrix = gateMatrix * unitaryMatrix; - if (gate.type == qc::X && gate.qubitId.size() == 2) { - // controls come first - std::cerr << std::format("cx() q[{}], q[{}];", gate.qubitId[1], - gate.qubitId[0]); - } else if (gate.parameter.empty()) { - std::cerr << std::format( - "{}() q[{}] {};", qc::toString(gate.type), gate.qubitId[0], - (gate.qubitId.size() > 1 - ? (", q[" + std::to_string(gate.qubitId[1]) + "]") - : std::string{})); - } else { - std::cerr << std::format( - "{}({}*pi) q[{}] {};", qc::toString(gate.type), - gate.parameter[0] / qc::PI, gate.qubitId[0], - (gate.qubitId.size() > 1 - ? (", q[" + std::to_string(gate.qubitId[1]) + "]") - : std::string{})); - } - std::cerr << '\n'; + // TODO: need to add each basis gate we want to use if (gate.type == qc::X) { mlir::SmallVector inCtrlQubits; if (gate.qubitId.size() > 1) { @@ -572,7 +518,6 @@ struct GateDecompositionPattern final throw std::runtime_error{"Unknown gate type!"}; } } - std::cerr << '\n'; assert((unitaryMatrix * std::exp(IM * sequence.globalPhase)) .isApprox(series.getUnitaryMatrix(), SANITY_CHECK_PRECISION)); @@ -1769,8 +1714,10 @@ struct GateDecompositionPattern final decomp, target1qEulerBases, 0, {}, true, std::nullopt); eulerDecompositions.push_back(eulerDecomp); } - TwoQubitGateSequence gates{.globalPhase = - targetDecomposition.globalPhase}; + TwoQubitGateSequence gates{ + .gates = {}, + .globalPhase = targetDecomposition.globalPhase, + }; // Worst case length is 5x 1q gates for each 1q decomposition + 1x 2q // gate We might overallocate a bit if the euler basis is different but // the worst case is just 16 extra elements with just a String and 2 From eac38dec769bb124b5466d3209bbfa47817b6972 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 21 Nov 2025 17:30:39 +0100 Subject: [PATCH 098/237] fix includes and use SmallVector instead of std::vector --- .../Transforms/GateDecompositionPattern.cpp | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 359b09142b..c2d377f121 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -10,6 +10,7 @@ #include "Helpers.h" #include "ir/Definitions.hpp" +#include "ir/operations/OpType.hpp" #include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" #include "mlir/Dialect/MQTOpt/Transforms/Passes.h" @@ -17,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -27,7 +29,7 @@ #include #include #include -#include +#include // NOLINT(misc-include-cleaner) #include namespace mqt::ir::opt { @@ -71,7 +73,7 @@ struct GateDecompositionPattern final /** * Container sorting the gate sequence in order. */ - std::vector gates; + llvm::SmallVector gates; /** * Global phase adjustment required for the sequence. @@ -1777,7 +1779,7 @@ struct GateDecompositionPattern final * * which is optimal for all targets and bases */ - [[nodiscard]] static std::vector + [[nodiscard]] static llvm::SmallVector decomp0(const TwoQubitWeylDecomposition& target) { return { target.k1r * target.k2r, @@ -1799,7 +1801,7 @@ struct GateDecompositionPattern final * * which is optimal for all targets and bases with ``z==0`` or ``c==0``. */ - [[nodiscard]] std::vector + [[nodiscard]] llvm::SmallVector decomp1(const TwoQubitWeylDecomposition& target) const { // FIXME: fix for z!=0 and c!=0 using closest reflection (not always in // the Weyl chamber) @@ -1834,7 +1836,7 @@ struct GateDecompositionPattern final * and target :math:`\sim U_d(x, y, 0)`. No guarantees for * non-supercontrolled basis. */ - [[nodiscard]] std::vector + [[nodiscard]] llvm::SmallVector decomp2Supercontrolled(const TwoQubitWeylDecomposition& target) const { return { q2r * target.k2r, @@ -1855,7 +1857,7 @@ struct GateDecompositionPattern final * :math:`\sim U_d(\pi/4, b, 0)`, all b, and any target. No guarantees for * non-supercontrolled basis. */ - [[nodiscard]] std::vector + [[nodiscard]] llvm::SmallVector decomp3Supercontrolled(const TwoQubitWeylDecomposition& target) const { return { u3r * target.k2r, @@ -1975,21 +1977,22 @@ struct GateDecompositionPattern final angleZeroEpsilon = -1.0; } - fp globalPhase = phase - ((phi + lambda) / 2.); - - std::vector gates; + OneQubitGateSequence sequence{ + .gates = {}, + .globalPhase = phase - ((phi + lambda) / 2.), + }; if (std::abs(theta) <= angleZeroEpsilon) { lambda += phi; lambda = mod2pi(lambda); if (std::abs(lambda) > angleZeroEpsilon) { - gates.push_back({kGate, {lambda}}); - globalPhase += lambda / 2.0; + sequence.gates.push_back({.type = kGate, .parameter = {lambda}}); + sequence.globalPhase += lambda / 2.0; } - return {gates, globalPhase}; + return sequence; } if (std::abs(theta - qc::PI) <= angleZeroEpsilon) { - globalPhase += phi; + sequence.globalPhase += phi; lambda -= phi; phi = 0.0; } @@ -2001,16 +2004,16 @@ struct GateDecompositionPattern final } lambda = mod2pi(lambda); if (std::abs(lambda) > angleZeroEpsilon) { - globalPhase += lambda / 2.0; - gates.push_back({kGate, {lambda}}); + sequence.globalPhase += lambda / 2.0; + sequence.gates.push_back({.type = kGate, .parameter = {lambda}}); } - gates.push_back({aGate, {theta}}); + sequence.gates.push_back({.type = aGate, .parameter = {theta}}); phi = mod2pi(phi); if (std::abs(phi) > angleZeroEpsilon) { - globalPhase += phi / 2.0; - gates.push_back({kGate, {phi}}); + sequence.globalPhase += phi / 2.0; + sequence.gates.push_back({.type = kGate, .parameter = {phi}}); } - return {gates, globalPhase}; + return sequence; } }; From 4ea31209081757da475a4e4b7bffacec96fa62b4 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 21 Nov 2025 17:55:18 +0100 Subject: [PATCH 099/237] fix or silence linter --- .../Transforms/GateDecompositionPattern.cpp | 150 ++++++++++-------- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 23 ++- 2 files changed, 98 insertions(+), 75 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index c2d377f121..35c0f19915 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -14,22 +14,29 @@ #include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" #include "mlir/Dialect/MQTOpt/Transforms/Passes.h" +#include // NOLINT(misc-include-cleaner) #include #include +#include #include +#include #include #include +#include #include +#include #include #include #include +#include #include #include #include #include +#include #include -#include -#include // NOLINT(misc-include-cleaner) +#include +#include // TODO: unstable, NOLINT(misc-include-cleaner) #include namespace mqt::ir::opt { @@ -162,8 +169,8 @@ struct GateDecompositionPattern final return mlir::failure(); } - matrix4x4 unitaryMatrix = series.getUnitaryMatrix(); - auto targetDecomposition = + const matrix4x4 unitaryMatrix = series.getUnitaryMatrix(); + const auto targetDecomposition = TwoQubitWeylDecomposition::create(unitaryMatrix, DEFAULT_FIDELITY); std::optional bestSequence; @@ -357,10 +364,11 @@ struct GateDecompositionPattern final ? *firstQubitIt : *secondQubitIt); // new qubit ID based on position in outQubits - QubitId newInQubitId = std::distance(outQubits.begin(), it); + const QubitId newInQubitId = std::distance(outQubits.begin(), it); // position in operation input; since there are only two qubits, it must // be the "not old" one - QubitId newOpInQubitId = 1 - std::distance(opInQubits.begin(), it2); + const QubitId newOpInQubitId = + 1 - std::distance(opInQubits.begin(), it2); // update inQubit and update dangling iterator, then proceed as usual inQubits[newInQubitId] = opInQubits[newOpInQubitId]; @@ -371,8 +379,10 @@ struct GateDecompositionPattern final // possible to collect other single-qubit operations backtrackSingleQubitSeries(newInQubitId); } - QubitId firstQubitId = std::distance(outQubits.begin(), firstQubitIt); - QubitId secondQubitId = std::distance(outQubits.begin(), secondQubitIt); + const QubitId firstQubitId = + std::distance(outQubits.begin(), firstQubitIt); + const QubitId secondQubitId = + std::distance(outQubits.begin(), secondQubitIt); *firstQubitIt = nextGate->getResult(0); *secondQubitIt = nextGate->getResult(1); @@ -609,7 +619,7 @@ struct GateDecompositionPattern final } r /= std::sqrt(detR); // transpose with complex conjugate of each element - matrix2x2 rTConj = r.transpose().conjugate(); + const matrix2x2 rTConj = r.transpose().conjugate(); auto temp = helpers::kroneckerProduct(IDENTITY_GATE, rTConj); temp = specialUnitary * temp; @@ -734,24 +744,24 @@ struct GateDecompositionPattern final } static std::array paramsZyzInner(const matrix2x2& matrix) { - auto detArg = std::arg(matrix.determinant()); - auto phase = 0.5 * detArg; - auto theta = + const auto detArg = std::arg(matrix.determinant()); + const auto phase = 0.5 * detArg; + const auto theta = 2. * std::atan2(std::abs(matrix(1, 0)), std::abs(matrix(0, 0))); - auto ang1 = std::arg(matrix(1, 1)); - auto ang2 = std::arg(matrix(1, 0)); - auto phi = ang1 + ang2 - detArg; - auto lam = ang1 - ang2; + const auto ang1 = std::arg(matrix(1, 1)); + const auto ang2 = std::arg(matrix(1, 0)); + const auto phi = ang1 + ang2 - detArg; + const auto lam = ang1 - ang2; return {theta, phi, lam, phase}; } static std::array paramsZxzInner(const matrix2x2& matrix) { - auto [theta, phi, lam, phase] = paramsZyzInner(matrix); + const auto [theta, phi, lam, phase] = paramsZyzInner(matrix); return {theta, phi + (qc::PI / 2.), lam - (qc::PI / 2.), phase}; } static std::array paramsXyxInner(const matrix2x2& matrix) { - matrix2x2 matZyz{ + const matrix2x2 matZyz{ {static_cast(0.5) * (matrix(0, 0) + matrix(0, 1) + matrix(1, 0) + matrix(1, 1)), static_cast(0.5) * @@ -906,7 +916,7 @@ struct GateDecompositionPattern final u *= detPow; auto globalPhase = std::arg(detU) / 4.; auto uP = magicBasisTransform(u, MagicBasisTransform::OutOf); - matrix4x4 m2 = uP.transpose() * uP; + const matrix4x4 m2 = uP.transpose() * uP; auto defaultEulerBasis = EulerBasis::ZYZ; // diagonalization yields eigenvectors (p) and eigenvalues (d); @@ -943,7 +953,7 @@ struct GateDecompositionPattern final // update columns of p according to order matrix4x4 pOrig = p; - for (int i = 0; i < static_cast(order.size()); ++i) { + for (int i = 0; std::cmp_less(i, order.size()); ++i) { p.col(i) = pOrig.col(order[i]); } // apply correction for determinant if necessary @@ -1173,14 +1183,14 @@ struct GateDecompositionPattern final randA = dist(state); randB = dist(state); } - rmatrix4x4 m2Real = randA * m.real() + randB * m.imag(); - rmatrix4x4 pInnerReal = selfAdjointEigenLower(m2Real).first; - matrix4x4 p = pInnerReal; - diagonal4x4 d = (p.transpose() * m * p).diagonal(); + const rmatrix4x4 m2Real = randA * m.real() + randB * m.imag(); + const rmatrix4x4 pReal = selfAdjointEigenLower(m2Real).first; + const matrix4x4 p = pReal; + const diagonal4x4 d = (p.transpose() * m * p).diagonal(); - matrix4x4 diagD = d.asDiagonal(); + const matrix4x4 diagD = d.asDiagonal(); - matrix4x4 compare = p * diagD * p.transpose(); + const matrix4x4 compare = p * diagD * p.transpose(); if (compare.isApprox(m, precision)) { // p are the eigenvectors which are decomposed into the // single-qubit gates surrounding the canonical gate @@ -1542,9 +1552,9 @@ struct GateDecompositionPattern final {qfp(-0.5, 0.5), qfp(0.5, -0.5)}, }; - auto basisDecomposer = TwoQubitWeylDecomposition::create( + const auto basisDecomposer = TwoQubitWeylDecomposition::create( getTwoQubitMatrix(basisGate), basisFidelity); - auto superControlled = + const auto superControlled = relativeEq(basisDecomposer.a, qc::PI_4, 1e-13, 1e-09) && relativeEq(basisDecomposer.c, 0.0, 1e-13, 1e-09); @@ -1552,72 +1562,72 @@ struct GateDecompositionPattern final // expand as Ui = Ki1.Ubasis.Ki2 auto b = basisDecomposer.b; auto temp = qfp(0.5, -0.5); - matrix2x2 k11l{ + const matrix2x2 k11l{ {temp * (M_IM * std::exp(qfp(0., -b))), temp * std::exp(qfp(0., -b))}, {temp * (M_IM * std::exp(qfp(0., b))), temp * -std::exp(qfp(0., b))}}; - matrix2x2 k11r{{FRAC1_SQRT2 * (IM * std::exp(qfp(0., -b))), - FRAC1_SQRT2 * -std::exp(qfp(0., -b))}, - {FRAC1_SQRT2 * std::exp(qfp(0., b)), - FRAC1_SQRT2 * (M_IM * std::exp(qfp(0., b)))}}; - matrix2x2 k32lK21l{{FRAC1_SQRT2 * qfp(1., std::cos(2. * b)), - FRAC1_SQRT2 * (IM * std::sin(2. * b))}, - {FRAC1_SQRT2 * (IM * std::sin(2. * b)), - FRAC1_SQRT2 * qfp(1., -std::cos(2. * b))}}; + const matrix2x2 k11r{{FRAC1_SQRT2 * (IM * std::exp(qfp(0., -b))), + FRAC1_SQRT2 * -std::exp(qfp(0., -b))}, + {FRAC1_SQRT2 * std::exp(qfp(0., b)), + FRAC1_SQRT2 * (M_IM * std::exp(qfp(0., b)))}}; + const matrix2x2 k32lK21l{{FRAC1_SQRT2 * qfp(1., std::cos(2. * b)), + FRAC1_SQRT2 * (IM * std::sin(2. * b))}, + {FRAC1_SQRT2 * (IM * std::sin(2. * b)), + FRAC1_SQRT2 * qfp(1., -std::cos(2. * b))}}; temp = qfp(0.5, 0.5); - matrix2x2 k21r{ + const matrix2x2 k21r{ {temp * (M_IM * std::exp(qfp(0., -2. * b))), temp * std::exp(qfp(0., -2. * b))}, {temp * (IM * std::exp(qfp(0., 2. * b))), temp * std::exp(qfp(0., 2. * b))}, }; - const matrix2x2 k22LArr{ + const matrix2x2 k22l{ {qfp(FRAC1_SQRT2, 0.), qfp(-FRAC1_SQRT2, 0.)}, {qfp(FRAC1_SQRT2, 0.), qfp(FRAC1_SQRT2, 0.)}, }; - const matrix2x2 k22RArr{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; - matrix2x2 k31l{ + const matrix2x2 k22r{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; + const matrix2x2 k31l{ {FRAC1_SQRT2 * std::exp(qfp(0., -b)), FRAC1_SQRT2 * std::exp(qfp(0., -b))}, {FRAC1_SQRT2 * -std::exp(qfp(0., b)), FRAC1_SQRT2 * std::exp(qfp(0., b))}, }; - matrix2x2 k31r{ + const matrix2x2 k31r{ {IM * std::exp(qfp(0., b)), C_ZERO}, {C_ZERO, M_IM * std::exp(qfp(0., -b))}, }; temp = qfp(0.5, 0.5); - matrix2x2 k32r{ + const matrix2x2 k32r{ {temp * std::exp(qfp(0., b)), temp * -std::exp(qfp(0., -b))}, {temp * (M_IM * std::exp(qfp(0., b))), temp * (M_IM * std::exp(qfp(0., -b)))}, }; - auto k1ld = basisDecomposer.k1l.transpose().conjugate(); - auto k1rd = basisDecomposer.k1r.transpose().conjugate(); - auto k2ld = basisDecomposer.k2l.transpose().conjugate(); - auto k2rd = basisDecomposer.k2r.transpose().conjugate(); + auto k1lDagger = basisDecomposer.k1l.transpose().conjugate(); + auto k1rDagger = basisDecomposer.k1r.transpose().conjugate(); + auto k2lDagger = basisDecomposer.k2l.transpose().conjugate(); + auto k2rDagger = basisDecomposer.k2r.transpose().conjugate(); // Pre-build the fixed parts of the matrices used in 3-part // decomposition - auto u0l = k31l * k1ld; - auto u0r = k31r * k1rd; - auto u1l = k2ld * k32lK21l * k1ld; - auto u1ra = k2rd * k32r; - auto u1rb = k21r * k1rd; - auto u2la = k2ld * k22LArr; - auto u2lb = k11l * k1ld; - auto u2ra = k2rd * k22RArr; - auto u2rb = k11r * k1rd; - auto u3l = k2ld * k12LArr; - auto u3r = k2rd * k12RArr; + auto u0l = k31l * k1lDagger; + auto u0r = k31r * k1rDagger; + auto u1l = k2lDagger * k32lK21l * k1lDagger; + auto u1ra = k2rDagger * k32r; + auto u1rb = k21r * k1rDagger; + auto u2la = k2lDagger * k22l; + auto u2lb = k11l * k1lDagger; + auto u2ra = k2rDagger * k22r; + auto u2rb = k11r * k1rDagger; + auto u3l = k2lDagger * k12LArr; + auto u3r = k2rDagger * k12RArr; // Pre-build the fixed parts of the matrices used in the 2-part // decomposition - auto q0l = k12LArr.transpose().conjugate() * k1ld; - auto q0r = k12RArr.transpose().conjugate() * IPZ * k1rd; - auto q1la = k2ld * k11l.transpose().conjugate(); - auto q1lb = k11l * k1ld; - auto q1ra = k2rd * IPZ * k11r.transpose().conjugate(); - auto q1rb = k11r * k1rd; - auto q2l = k2ld * k12LArr; - auto q2r = k2rd * k12RArr; + auto q0l = k12LArr.transpose().conjugate() * k1lDagger; + auto q0r = k12RArr.transpose().conjugate() * IPZ * k1rDagger; + auto q1la = k2lDagger * k11l.transpose().conjugate(); + auto q1lb = k11l * k1lDagger; + auto q1ra = k2rDagger * IPZ * k11r.transpose().conjugate(); + auto q1rb = k11r * k1rDagger; + auto q2l = k2lDagger * k12LArr; + auto q2r = k2rDagger * k12RArr; return TwoQubitBasisDecomposer{ .basisGate = basisGate, @@ -1678,7 +1688,7 @@ struct GateDecompositionPattern final auto getDefaultNbasis = [&]() { auto minValue = std::numeric_limits::min(); auto minIndex = -1; - for (int i = 0; i < static_cast(traces.size()); ++i) { + for (int i = 0; std::cmp_less(i, traces.size()); ++i) { // lower fidelity means it becomes easier to choose a lower number of // basis gates auto value = @@ -1713,7 +1723,7 @@ struct GateDecompositionPattern final for (auto&& decomp : decomposition) { assert(helpers::isUnitaryMatrix(decomp)); auto eulerDecomp = unitaryToGateSequenceInner( - decomp, target1qEulerBases, 0, {}, true, std::nullopt); + decomp, target1qEulerBases, 0, true, std::nullopt); eulerDecompositions.push_back(eulerDecomp); } TwoQubitGateSequence gates{ @@ -1931,8 +1941,8 @@ struct GateDecompositionPattern final [[nodiscard]] static OneQubitGateSequence unitaryToGateSequenceInner( const matrix2x2& unitaryMat, const llvm::SmallVector& targetBasisList, QubitId /*qubit*/, - const std::vector>& - /*error_map*/, // per qubit a mapping of operation name to error value + // TODO: add error map here: per qubit a mapping of operation to error + // value for better calculateError() bool simplify, std::optional atol) { auto calculateError = [](const OneQubitGateSequence& sequence) -> fp { return static_cast(sequence.complexity()); diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index 1ab09dea1d..bf00c80f40 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -14,21 +14,32 @@ #include "ir/operations/OpType.hpp" #include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include -#include +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include +#include +#include +#include +#include #include +#include #include -#include // TODO: unstable +#include +#include +#include +#include +#include // TODO: unstable, NOLINT(misc-include-cleaner) namespace mqt::ir::opt { using fp = qc::fp; using qfp = std::complex; +// NOLINTBEGIN(misc-include-cleaner) using matrix2x2 = Eigen::Matrix2; using matrix4x4 = Eigen::Matrix4; using rmatrix4x4 = Eigen::Matrix4; using diagonal4x4 = Eigen::Vector; using rdiagonal4x4 = Eigen::Vector; +// NOLINTEND(misc-include-cleaner) constexpr qfp C_ZERO{0., 0.}; constexpr qfp C_ONE{1., 0.}; @@ -145,7 +156,7 @@ getParameters(UnitaryInterface op) { [[nodiscard]] inline bool isSingleQubitOperation(UnitaryInterface op) { auto&& inQubits = op.getInQubits(); auto&& outQubits = op.getOutQubits(); - bool isSingleQubitOp = + const bool isSingleQubitOp = inQubits.size() == 1 && outQubits.size() == 1 && !op.isControlled(); return isSingleQubitOp; } @@ -161,10 +172,11 @@ getParameters(UnitaryInterface op) { auto&& outNegCtrlQubits = op.getNegCtrlInQubits(); auto outQubitSize = outQubits.size() + outPosCtrlQubits.size() + outNegCtrlQubits.size(); - bool isTwoQubitOp = inQubitSize == 2 && outQubitSize == 2; + const bool isTwoQubitOp = inQubitSize == 2 && outQubitSize == 2; return isTwoQubitOp; } +// NOLINTBEGIN(misc-include-cleaner) template inline Eigen::Matrix4 kroneckerProduct(const Eigen::Matrix2& lhs, const Eigen::Matrix2& rhs) { @@ -185,5 +197,6 @@ template isUnitaryMatrix(const Eigen::Matrix& matrix) { return (matrix.transpose().conjugate() * matrix).isIdentity(); } +// NOLINTEND(misc-include-cleaner) } // namespace mqt::ir::opt::helpers From 0cf229246c28450d1126b52ece1d02c10e58f10a Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 21 Nov 2025 19:15:55 +0100 Subject: [PATCH 100/237] incorporate coderabbitai feedback --- mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td | 3 +++ .../Dialect/MQTOpt/Transforms/GateDecomposition.cpp | 4 +++- .../MQTOpt/Transforms/GateDecompositionPattern.cpp | 2 ++ mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 10 +++++----- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td index 6c714a3179..89c3c517a8 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td @@ -44,8 +44,11 @@ def GateElimination : Pass<"gate-elimination", "mlir::ModuleOp"> { } def GateDecomposition : Pass<"gate-decomposition", "mlir::ModuleOp"> { + let dependentDialects = [ "mlir::arith::ArithDialect", "mqt::ir::opt::MQTOptDialect" ]; let summary = "This pass performs various gate decompositions to translate quantum gates being used."; let description = [{ + Decomposes series of operations that operate on up to two qubits into a sequence of up to three + two-qubit basis gates and single-qubit operations. }]; } diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp index 7c60ab2597..7db1378553 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp @@ -21,7 +21,9 @@ namespace mqt::ir::opt { #include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" /** - * @brief This pass attempts to cancel consecutive self-inverse operations. + * @brief This pass attempts to collect as many operations as possible into a + * 4x4 unitary matrix and then decompose it into 1q rotations and 2q + * basis gates. */ struct GateDecomposition final : impl::GateDecompositionBase { diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 35c0f19915..8a0d486073 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -926,6 +927,7 @@ struct GateDecompositionPattern final // TODO: it may be possible to lower the precision auto [p, d] = diagonalizeComplexSymmetric(m2, 1e-13); + // NOLINTNEXTLINE(misc-include-cleaner) Eigen::Vector cs; // weyl coordinates rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; dReal(3) = -dReal(0) - dReal(1) - dReal(2); diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index bf00c80f40..bb0fa3357e 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -119,15 +119,15 @@ inline std::optional mlirValueToFp(mlir::Value value) { return result; } if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return a + b; })) { + value, [](fp a, fp b) { return a * b; })) { return result; } if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return a + b; })) { + value, [](fp a, fp b) { return a / b; })) { return result; } if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return a + b; })) { + value, [](fp a, fp b) { return a - b; })) { return result; } return std::nullopt; @@ -168,8 +168,8 @@ getParameters(UnitaryInterface op) { auto inQubitSize = inQubits.size() + inPosCtrlQubits.size() + inNegCtrlQubits.size(); auto&& outQubits = op.getOutQubits(); - auto&& outPosCtrlQubits = op.getPosCtrlInQubits(); - auto&& outNegCtrlQubits = op.getNegCtrlInQubits(); + auto&& outPosCtrlQubits = op.getPosCtrlOutQubits(); + auto&& outNegCtrlQubits = op.getNegCtrlOutQubits(); auto outQubitSize = outQubits.size() + outPosCtrlQubits.size() + outNegCtrlQubits.size(); const bool isTwoQubitOp = inQubitSize == 2 && outQubitSize == 2; From b4d419cc5d1c2e591680a5fd260351e2b7fbaaf1 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 25 Nov 2025 19:22:48 +0100 Subject: [PATCH 101/237] remove global phase accumulation --- .../Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 8a0d486073..f0bd067702 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -316,12 +316,6 @@ struct GateDecompositionPattern final } complexity += getComplexity(helpers::getQcType(initialOperation), in.size()); - - // TODO: necessary? - for (auto&& globalPhaseOp : - initialOperation->getBlock()->getOps()) { - globalPhase += helpers::getParameters(globalPhaseOp)[0]; - } } /** From 75672af4f448cd7056406cc799cfb0cf61ef34e0 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 25 Nov 2025 19:23:07 +0100 Subject: [PATCH 102/237] fix :math: latex formatting --- .../Transforms/GateDecompositionPattern.cpp | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index f0bd067702..7a7a429e10 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -1272,10 +1272,11 @@ struct GateDecompositionPattern final specialization = newSpecialization; if (newSpecialization == Specialization::IdEquiv) { - // :math:`U \sim U_d(0,0,0) \sim Id` + // :math:`U \sim U_d(0,0,0)` + // Thus, :math:`\sim Id` + // + // This gate binds 0 parameters, we make it canonical by setting: // - // This gate binds 0 parameters, we make it canonical by - // setting // :math:`K2_l = Id` , :math:`K2_r = Id`. a = 0.; b = 0.; @@ -1286,11 +1287,11 @@ struct GateDecompositionPattern final k1r = k1r * k2r; k2r = IDENTITY_GATE; } else if (newSpecialization == Specialization::SWAPEquiv) { - // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, - // -\pi/4) \sim \text{SWAP}` + // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, -\pi/4)` + // Thus, :math:`U \sim \text{SWAP}` + // + // This gate binds 0 parameters, we make it canonical by setting: // - // This gate binds 0 parameters, we make it canonical by - // setting // :math:`K2_l = Id` , :math:`K2_r = Id`. a = qc::PI_4; b = qc::PI_4; @@ -1311,8 +1312,8 @@ struct GateDecompositionPattern final k2r = IDENTITY_GATE; } } else if (newSpecialization == Specialization::PartialSWAPEquiv) { - // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4) \sim - // \text{SWAP}^\alpha` + // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4)` + // Thus, :math:`U \sim \text{SWAP}^\alpha` // // This gate binds 3 parameters, we make it canonical by setting: // @@ -1329,12 +1330,11 @@ struct GateDecompositionPattern final k2r = k2lDagger * k2r; k2l = IDENTITY_GATE; } else if (newSpecialization == Specialization::PartialSWAPFlipEquiv) { - // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4) \sim - // \text{SWAP}^\alpha` + // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4)` + // Thus, :math:`U \sim \text{SWAP}^\alpha` // - // (a non-equivalent root of SWAP from the - // TwoQubitWeylPartialSWAPEquiv similar to how :math:`x = (\pm - // \sqrt(x))^2`) + // (a non-equivalent root of SWAP from the TwoQubitWeylPartialSWAPEquiv + // similar to how :math:`x = (\pm \sqrt(x))^2`) // // This gate binds 3 parameters, we make it canonical by setting: // @@ -1351,12 +1351,13 @@ struct GateDecompositionPattern final k2r = IPZ * k2lDagger * IPZ * k2r; k2l = IDENTITY_GATE; } else if (newSpecialization == Specialization::ControlledEquiv) { - // :math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}` + // :math:`U \sim U_d(\alpha, 0, 0)` + // Thus, :math:`U \sim \text{Ctrl-U}` // // This gate binds 4 parameters, we make it canonical by setting: // - // :math:`K2_l = Ry(\theta_l) Rx(\lambda_l)` , - // :math:`K2_r = Ry(\theta_r) Rx(\lambda_r)` . + // :math:`K2_l = Ry(\theta_l) Rx(\lambda_l)` + // :math:`K2_r = Ry(\theta_r) Rx(\lambda_r)` auto eulerBasis = EulerBasis::XYX; auto [k2ltheta, k2lphi, k2llambda, k2lphase] = anglesFromUnitary(k2l, eulerBasis); @@ -1373,13 +1374,13 @@ struct GateDecompositionPattern final k2r = ryMatrix(k2rtheta) * rxMatrix(k2rlambda); defaultEulerBasis = eulerBasis; } else if (newSpecialization == Specialization::MirrorControlledEquiv) { - // :math:`U \sim U_d(\pi/4, \pi/4, \alpha) \sim \text{SWAP} \cdot - // \text{Ctrl-U}` + // :math:`U \sim U_d(\pi/4, \pi/4, \alpha)` + // Thus, :math:`U \sim \text{SWAP} \cdot \text{Ctrl-U}` // // This gate binds 4 parameters, we make it canonical by setting: // - // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` , :math:`K2_r = - // Ry(\theta_r)\cdot Rz(\lambda_r)` + // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` + // :math:`K2_r = Ry(\theta_r)\cdot Rz(\lambda_r)` auto [k2ltheta, k2lphi, k2llambda, k2lphase] = anglesFromUnitary(k2l, EulerBasis::ZYZ); auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = @@ -1775,7 +1776,7 @@ struct GateDecompositionPattern final /** * Calculate decompositions when no basis gate is required. * - * Decompose target :math:`\sim U_d(x, y, z)` with :math:`0` uses of the + * Decompose target :math:`\sim U_d(x, y, z)` with 0 uses of the * basis gate. Result :math:`U_r` has trace: * * .. math:: @@ -1796,7 +1797,7 @@ struct GateDecompositionPattern final /** * Calculate decompositions when one basis gate is required. * - * Decompose target :math:`\sim U_d(x, y, z)` with :math:`1` use of the + * Decompose target :math:`\sim U_d(x, y, z)` with 1 use of the * basis gate math:`\sim U_d(a, b, c)`. Result :math:`U_r` has trace: * * .. math:: @@ -1822,7 +1823,7 @@ struct GateDecompositionPattern final /** * Calculate decompositions when two basis gates are required. * - * Decompose target :math:`\sim U_d(x, y, z)` with :math:`2` uses of the + * Decompose target :math:`\sim U_d(x, y, z)` with 2 uses of the * basis gate. * * For supercontrolled basis :math:`\sim U_d(\pi/4, b, 0)`, all b, result @@ -1833,11 +1834,11 @@ struct GateDecompositionPattern final * \Big\vert\text{Tr}(U_r \cdot U_\text{target}^\dag) \Big\vert = * 4\cos(z) * - * which is the optimal approximation for basis of CNOT-class :math:`\sim - * U_d(\pi/4, 0, 0)` or DCNOT-class :math:`\sim U_d(\pi/4, \pi/4, 0)` and - * any target. It may be sub-optimal for :math:`b \neq 0` (i.e. there exists - * an exact decomposition for any target using - * :math:`B \sim U_d(\pi/4, \pi/8, 0)`, but it may not be this + * which is the optimal approximation for basis of CNOT-class + * :math:`\sim U_d(\pi/4, 0, 0)` or DCNOT-class + * :math:`\sim U_d(\pi/4, \pi/4, 0)` and any target. It may be sub-optimal + * for :math:`b \neq 0` (i.e. there exists an exact decomposition for any + * target using :math:`B \sim U_d(\pi/4, \pi/8, 0)`, but it may not be this * decomposition). This is an exact decomposition for supercontrolled basis * and target :math:`\sim U_d(x, y, 0)`. No guarantees for * non-supercontrolled basis. @@ -1857,7 +1858,7 @@ struct GateDecompositionPattern final /** * Calculate decompositions when three basis gates are required. * - * Decompose target with :math:`3` uses of the basis. + * Decompose target with 3 uses of the basis. * * This is an exact decomposition for supercontrolled basis * :math:`\sim U_d(\pi/4, b, 0)`, all b, and any target. No guarantees for From fe08c78a748d7d46a2188b0ea9e278cc3a1c3791 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 25 Nov 2025 19:30:50 +0100 Subject: [PATCH 103/237] minor comment fixes --- .../Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 7a7a429e10..ea0b91feeb 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -1167,8 +1167,8 @@ struct GateDecompositionPattern final for (int i = 0; i < 100; ++i) { fp randA{}; fp randB{}; - // For debugging the algorithm use the same RNG values from the - // previous Python implementation for the first random trial. + // For debugging the algorithm use the same RNG values as the + // Qiskit implementation for the first random trial. // In most cases this loop only executes a single iteration and // using the same rng values rules out possible RNG differences // as the root cause of a test failure @@ -1810,8 +1810,7 @@ struct GateDecompositionPattern final */ [[nodiscard]] llvm::SmallVector decomp1(const TwoQubitWeylDecomposition& target) const { - // FIXME: fix for z!=0 and c!=0 using closest reflection (not always in - // the Weyl chamber) + // may not work for z != 0 and c != 0 (not always in Weyl chamber) return { basisDecomposer.k2r.transpose().conjugate() * target.k2r, basisDecomposer.k2l.transpose().conjugate() * target.k2l, From 5128d83e71db76375f37ad10fa9a1e89e0457140 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 25 Nov 2025 19:53:49 +0100 Subject: [PATCH 104/237] replace CHECK-DAG by CHECK again --- .../MQTOpt/Transforms/gate-decomposition.mlir | 96 +++++++++---------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index c0b1dd17c0..c38c2ad403 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -134,8 +134,8 @@ module { module { // CHECK-LABEL: func.func @testSeriesOneQubitOpInbetween func.func @testSeriesOneQubitOpInbetween() { - // CHECK-DAG: %[[C0:.*]] = arith.constant 3.14159 - // CHECK-DAG: %[[C1:.*]] = arith.constant 1.5707 + // CHECK: %[[C0:.*]] = arith.constant 3.14159 + // CHECK: %[[C1:.*]] = arith.constant 1.5707 // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit @@ -169,8 +169,8 @@ module { module { // CHECK-LABEL: func.func @testSeriesStartingOneQubitOp func.func @testSeriesStartingOneQubitOp() { - // CHECK-DAG: %[[C0:.*]] = arith.constant 3.14159 - // CHECK-DAG: %[[C1:.*]] = arith.constant 1.5707 + // CHECK: %[[C0:.*]] = arith.constant 3.14159 + // CHECK: %[[C1:.*]] = arith.constant 1.5707 // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit @@ -204,8 +204,8 @@ module { module { // CHECK-LABEL: func.func @testSeriesEndingOneQubitOp func.func @testSeriesEndingOneQubitOp() { - // CHECK-DAG: %[[C0:.*]] = arith.constant 3.14159 - // CHECK-DAG: %[[C1:.*]] = arith.constant 1.5707 + // CHECK: %[[C0:.*]] = arith.constant 3.14159 + // CHECK: %[[C1:.*]] = arith.constant 1.5707 // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit @@ -280,18 +280,18 @@ module { module { // CHECK-LABEL: func.func @testTwoBasisGateDecomposition func.func @testTwoBasisGateDecomposition() { - // CHECK-DAG: %[[C0:.*]] = arith.constant -1.5707963267 - // CHECK-DAG: %[[C1:.*]] = arith.constant -3.141592653 - // CHECK-DAG: %[[C2:.*]] = arith.constant 1.2502369157 - // CHECK-DAG: %[[C3:.*]] = arith.constant 1.8299117708 - // CHECK-DAG: %[[C4:.*]] = arith.constant 2.8733113535 - // CHECK-DAG: %[[C5:.*]] = arith.constant 3.0097427712 - // CHECK-DAG: %[[C6:.*]] = arith.constant 0.2614370492 - // CHECK-DAG: %[[C7:.*]] = arith.constant -0.131849882 - // CHECK-DAG: %[[C8:.*]] = arith.constant 2.500000e+00 - // CHECK-DAG: %[[C9:.*]] = arith.constant -1.570796326 - // CHECK-DAG: %[[C10:.*]] = arith.constant 1.570796326 - // CHECK-DAG: %[[C11:.*]] = arith.constant 0.785398163 + // CHECK: %[[C0:.*]] = arith.constant -1.5707963267 + // CHECK: %[[C1:.*]] = arith.constant -3.141592653 + // CHECK: %[[C2:.*]] = arith.constant 1.2502369157 + // CHECK: %[[C3:.*]] = arith.constant 1.8299117708 + // CHECK: %[[C4:.*]] = arith.constant 2.8733113535 + // CHECK: %[[C5:.*]] = arith.constant 3.0097427712 + // CHECK: %[[C6:.*]] = arith.constant 0.2614370492 + // CHECK: %[[C7:.*]] = arith.constant -0.131849882 + // CHECK: %[[C8:.*]] = arith.constant 2.500000e+00 + // CHECK: %[[C9:.*]] = arith.constant -1.570796326 + // CHECK: %[[C10:.*]] = arith.constant 1.570796326 + // CHECK: %[[C11:.*]] = arith.constant 0.785398163 // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit @@ -352,26 +352,26 @@ module { module { // CHECK-LABEL: func.func @testThreeBasisGateDecomposition func.func @testThreeBasisGateDecomposition() { - // CHECK-DAG: %[[C0:.*]] = arith.constant 1.00543528660 - // CHECK-DAG: %[[C1:.*]] = arith.constant -2.3561944901 - // CHECK-DAG: %[[C2:.*]] = arith.constant 0.85849590894 - // CHECK-DAG: %[[C3:.*]] = arith.constant 0.43624604946 - // CHECK-DAG: %[[C4:.*]] = arith.constant 2.43419171936 - // CHECK-DAG: %[[C5:.*]] = arith.constant -0.6154797086 - // CHECK-DAG: %[[C6:.*]] = arith.constant 1.04719755119 - // CHECK-DAG: %[[C7:.*]] = arith.constant 2.52611294491 - // CHECK-DAG: %[[C8:.*]] = arith.constant -0.2593805121 - // CHECK-DAG: %[[C9:.*]] = arith.constant 1.57079632679 - // CHECK-DAG: %[[C10:.*]] = arith.constant -2.52611294491 - // CHECK-DAG: %[[C11:.*]] = arith.constant 2.094395102393 - // CHECK-DAG: %[[C12:.*]] = arith.constant -0.61547970867 - // CHECK-DAG: %[[C13:.*]] = arith.constant 0.694804217319 - // CHECK-DAG: %[[C14:.*]] = arith.constant 0.220207934068 - // CHECK-DAG: %[[C15:.*]] = arith.constant 1.125358392049 - // CHECK-DAG: %[[C16:.*]] = arith.constant -0.03425899788 - // CHECK-DAG: %[[C17:.*]] = arith.constant -1.57079632679 - // CHECK-DAG: %[[C18:.*]] = arith.constant -2.39270110306 - // CHECK-DAG: %[[C19:.*]] = arith.constant -0.78539816339 + // CHECK: %[[C0:.*]] = arith.constant 1.00543528660 + // CHECK: %[[C1:.*]] = arith.constant -2.3561944901 + // CHECK: %[[C2:.*]] = arith.constant 0.85849590894 + // CHECK: %[[C3:.*]] = arith.constant 0.43624604946 + // CHECK: %[[C4:.*]] = arith.constant 2.43419171936 + // CHECK: %[[C5:.*]] = arith.constant -0.6154797086 + // CHECK: %[[C6:.*]] = arith.constant 1.04719755119 + // CHECK: %[[C7:.*]] = arith.constant 2.52611294491 + // CHECK: %[[C8:.*]] = arith.constant -0.2593805121 + // CHECK: %[[C9:.*]] = arith.constant 1.57079632679 + // CHECK: %[[C10:.*]] = arith.constant -2.52611294491 + // CHECK: %[[C11:.*]] = arith.constant 2.094395102393 + // CHECK: %[[C12:.*]] = arith.constant -0.61547970867 + // CHECK: %[[C13:.*]] = arith.constant 0.694804217319 + // CHECK: %[[C14:.*]] = arith.constant 0.220207934068 + // CHECK: %[[C15:.*]] = arith.constant 1.125358392049 + // CHECK: %[[C16:.*]] = arith.constant -0.03425899788 + // CHECK: %[[C17:.*]] = arith.constant -1.57079632679 + // CHECK: %[[C18:.*]] = arith.constant -2.39270110306 + // CHECK: %[[C19:.*]] = arith.constant -0.78539816339 // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit @@ -448,8 +448,8 @@ module { module { // CHECK-LABEL: func.func @testSingleQubitSeries func.func @testSingleQubitSeries() { - // CHECK-DAG: %[[C0:.*]] = arith.constant 2.400000 - // CHECK-DAG: %[[C1:.*]] = arith.constant 2.500000 + // CHECK: %[[C0:.*]] = arith.constant 2.400000 + // CHECK: %[[C1:.*]] = arith.constant 2.500000 // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit @@ -487,14 +487,14 @@ module { module { // CHECK-LABEL: func.func @testSeriesSingleQubitBacktracking func.func @testSeriesSingleQubitBacktracking() { - // CHECK-DAG: %[[C0:.*]] = arith.constant -2.4269908 - // CHECK-DAG: %[[C1:.*]] = arith.constant 3.14159265 - // CHECK-DAG: %[[C2:.*]] = arith.constant 0.85619449 - // CHECK-DAG: %[[C3:.*]] = arith.constant -2.3561944 - // CHECK-DAG: %[[C4:.*]] = arith.constant -1.5707963 - // CHECK-DAG: %[[C5:.*]] = arith.constant -1.5707963 - // CHECK-DAG: %[[C6:.*]] = arith.constant 2.35619449 - // CHECK-DAG: %[[C7:.*]] = arith.constant -3.1415926 + // CHECK: %[[C0:.*]] = arith.constant -2.4269908 + // CHECK: %[[C1:.*]] = arith.constant 3.14159265 + // CHECK: %[[C2:.*]] = arith.constant 0.85619449 + // CHECK: %[[C3:.*]] = arith.constant -2.3561944 + // CHECK: %[[C4:.*]] = arith.constant -1.5707963 + // CHECK: %[[C5:.*]] = arith.constant -1.5707963 + // CHECK: %[[C6:.*]] = arith.constant 2.35619449 + // CHECK: %[[C7:.*]] = arith.constant -3.1415926 // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit From 9194ae91dfcd711d37bd3ed4524b8514fdea3703 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 26 Nov 2025 00:20:57 +0100 Subject: [PATCH 105/237] add comments to weyl decomposition --- .../Transforms/GateDecompositionPattern.cpp | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index ea0b91feeb..b2a182b84f 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -908,11 +908,18 @@ struct GateDecompositionPattern final auto u = unitaryMatrix; auto detU = u.determinant(); auto detPow = std::pow(detU, static_cast(-0.25)); - u *= detPow; + u *= detPow; // remove global phase from unitary matrix auto globalPhase = std::arg(detU) / 4.; + + // this should have normalized determinant of u, so that u ∈ SU(4) + assert(std::abs(u.determinant() - 1.0) < SANITY_CHECK_PRECISION); + + // transform unitary matrix to magic basis; this enables two properties: + // 1. if uP ∈ SO(4), V = A ⊗ B (SO(4) → SU(2) ⊗ SU(2)) + // 2. magic basis diagonalizes canonical gate, allowing calculation of + // canonical gate parameters later on auto uP = magicBasisTransform(u, MagicBasisTransform::OutOf); const matrix4x4 m2 = uP.transpose() * uP; - auto defaultEulerBasis = EulerBasis::ZYZ; // diagonalization yields eigenvectors (p) and eigenvalues (d); // p is used to calculate K1/K2 (and thus the single-qubit gates @@ -921,8 +928,9 @@ struct GateDecompositionPattern final // TODO: it may be possible to lower the precision auto [p, d] = diagonalizeComplexSymmetric(m2, 1e-13); + // extract Weyl coordinates from eigenvalues, map to [0, 2*pi) // NOLINTNEXTLINE(misc-include-cleaner) - Eigen::Vector cs; // weyl coordinates + Eigen::Vector cs; rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; dReal(3) = -dReal(0) - dReal(1) - dReal(2); for (int i = 0; i < static_cast(cs.size()); ++i) { @@ -947,7 +955,8 @@ struct GateDecompositionPattern final std::tie(dReal(0), dReal(1), dReal(2)) = std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2])}; - // update columns of p according to order + // update eigenvectors (columns of p) according to new order of + // weyl coordinates matrix4x4 pOrig = p; for (int i = 0; std::cmp_less(i, order.size()); ++i) { p.col(i) = pOrig.col(order[i]); @@ -959,33 +968,41 @@ struct GateDecompositionPattern final } assert(std::abs(p.determinant() - 1.0) < SANITY_CHECK_PRECISION); + // re-create complex eigenvalue matrix; this matrix contains the + // parameters of the canonical gate which is later used in the + // verification matrix4x4 temp = dReal.asDiagonal(); temp *= IM; temp = temp.exp(); + // combined matrix k1 of 1q gates after canonical gate matrix4x4 k1 = uP * p * temp; assert((k1.transpose() * k1).isIdentity()); // k1 must be orthogonal assert(k1.determinant().real() > 0.0); k1 = magicBasisTransform(k1, MagicBasisTransform::Into); + + // combined matrix k2 of 1q gates before canonical gate matrix4x4 k2 = p.transpose().conjugate(); assert((k2.transpose() * k2).isIdentity()); // k2 must be orthogonal assert(k2.determinant().real() > 0.0); k2 = magicBasisTransform(k2, MagicBasisTransform::Into); - // ensure k1 and k2 are correct + // ensure k1 and k2 are correct (when combined with the canonical gate + // parameters in-between, they are equivalent to u) assert((k1 * magicBasisTransform(temp.conjugate(), MagicBasisTransform::Into) * k2) .isApprox(u, SANITY_CHECK_PRECISION)); - // calculate k1 = K1l⊗ K1r + // calculate k1 = K1l ⊗ K1r auto [K1l, K1r, phase_l] = decomposeTwoQubitProductGate(k1); - // decompose k2 = K2l⊗ K2r + // decompose k2 = K2l ⊗ K2r auto [K2l, K2r, phase_r] = decomposeTwoQubitProductGate(k2); assert(helpers::kroneckerProduct(K1l, K1r).isApprox( k1, SANITY_CHECK_PRECISION)); assert(helpers::kroneckerProduct(K2l, K2r).isApprox( k2, SANITY_CHECK_PRECISION)); + // accumulate global phase globalPhase += phase_l + phase_r; // Flip into Weyl chamber @@ -1041,6 +1058,7 @@ struct GateDecompositionPattern final globalPhase -= qc::PI_2; } + // bind weyl coordinates as parameters of canonical gate auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); // make sure decomposition is equal to input assert((helpers::kroneckerProduct(K1l, K1r) * @@ -1058,8 +1076,9 @@ struct GateDecompositionPattern final .k1r = K1r, .k2r = K2r, .specialization = Specialization::General, - .defaultEulerBasis = defaultEulerBasis, + .defaultEulerBasis = EulerBasis::ZYZ, .requestedFidelity = fidelity, + // will be calculated if a specialization is used, set to -1 for now .calculatedFidelity = -1.0, .unitaryMatrix = unitaryMatrix, }; @@ -1085,6 +1104,8 @@ struct GateDecompositionPattern final qfp(std::cos(da) * std::cos(db) * std::cos(dc), std::sin(da) * std::sin(db) * std::sin(dc)); }; + // use trace to calculate fidelity of applied specialization and + // adjust global phase auto trace = getTrace(); decomposition.calculatedFidelity = traceToFidelity(trace); // final check if specialization is close enough to the original matrix to @@ -1194,7 +1215,7 @@ struct GateDecompositionPattern final // weyl coordinates and thus the parameters of the canonical gate // check that p is in SO(4) assert((p.transpose() * p).isIdentity(SANITY_CHECK_PRECISION)); - // make sure determinant of sqrt(eigenvalues) is 1.0 + // make sure determinant of eigenvalues is 1.0 assert(std::abs(matrix4x4{d.asDiagonal()}.determinant() - 1.0) < SANITY_CHECK_PRECISION); return std::make_pair(p, d); From ac4fb4197bfa428939a29b654bec3f6dedf71541 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 26 Nov 2025 00:21:22 +0100 Subject: [PATCH 106/237] extract TwoQubitWeylDecomposition::getTrace() --- .../Transforms/GateDecompositionPattern.cpp | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index b2a182b84f..4aa30ef819 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -1089,20 +1089,12 @@ struct GateDecompositionPattern final auto getTrace = [&]() { if (flippedFromOriginal) { - auto [da, db, dc] = std::array{ - qc::PI_2 - a - decomposition.a, - b - decomposition.b, - -c - decomposition.c, - }; - return static_cast(4.) * - qfp(std::cos(da) * std::cos(db) * std::cos(dc), - std::sin(da) * std::sin(db) * std::sin(dc)); + return TwoQubitWeylDecomposition::getTrace( + qc::PI_2 - a, b, -c, decomposition.a, decomposition.b, + decomposition.c); } - auto [da, db, dc] = std::array{a - decomposition.a, b - decomposition.b, - c - decomposition.c}; - return static_cast(4.) * - qfp(std::cos(da) * std::cos(db) * std::cos(dc), - std::sin(da) * std::sin(db) * std::sin(dc)); + return TwoQubitWeylDecomposition::getTrace( + a, b, c, decomposition.a, decomposition.b, decomposition.c); }; // use trace to calculate fidelity of applied specialization and // adjust global phase @@ -1225,14 +1217,27 @@ struct GateDecompositionPattern final "TwoQubitWeylDecomposition: failed to diagonalize M2."}; } + /** + * Calculate trace of two sets of parameters for the canonical gate. + * The trace has been defined in: https://arxiv.org/abs/1811.12926 + */ + [[nodiscard]] static qfp getTrace(fp a, fp b, fp c, fp ap, fp bp, fp cp) { + auto da = a - ap; + auto db = b - bp; + auto dc = c - cp; + return static_cast(4.) * + qfp(std::cos(da) * std::cos(db) * std::cos(dc), + std::sin(da) * std::sin(db) * std::sin(dc)); + } + + /** + * Choose the best specialization for the for the canonical gate. + * This will use the requestedFidelity to determine if a specialization is + * close enough to the actual canonical gate matrix. + */ [[nodiscard]] Specialization bestSpecialization() const { auto isClose = [this](fp ap, fp bp, fp cp) -> bool { - auto da = a - ap; - auto db = b - bp; - auto dc = c - cp; - auto tr = static_cast(4.) * - qfp(std::cos(da) * std::cos(db) * std::cos(dc), - std::sin(da) * std::sin(db) * std::sin(dc)); + auto tr = getTrace(a, b, c, ap, bp, cp); if (requestedFidelity) { return traceToFidelity(tr) >= *requestedFidelity; } From 56a29903ac8997dcf14d6104a848ffbe0e947aa3 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 26 Nov 2025 00:21:46 +0100 Subject: [PATCH 107/237] transform basis decomposer into actual class with private members --- .../Transforms/GateDecompositionPattern.cpp | 239 ++++++++++-------- 1 file changed, 135 insertions(+), 104 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 4aa30ef819..7c48b7c913 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -1500,31 +1500,7 @@ struct GateDecompositionPattern final * Decomposer that must be initialized with a two-qubit basis gate that will * be used to generate a circuit equivalent to a canonical gate (RXX+RYY+RZZ). */ - struct TwoQubitBasisDecomposer { - QubitGateSequence::Gate basisGate; - fp basisFidelity; - TwoQubitWeylDecomposition basisDecomposer; - bool superControlled; - matrix2x2 u0l; - matrix2x2 u0r; - matrix2x2 u1l; - matrix2x2 u1ra; - matrix2x2 u1rb; - matrix2x2 u2la; - matrix2x2 u2lb; - matrix2x2 u2ra; - matrix2x2 u2rb; - matrix2x2 u3l; - matrix2x2 u3r; - matrix2x2 q0l; - matrix2x2 q0r; - matrix2x2 q1la; - matrix2x2 q1lb; - matrix2x2 q1ra; - matrix2x2 q1rb; - matrix2x2 q2l; - matrix2x2 q2r; - + class TwoQubitBasisDecomposer { public: /** * Create decomposer that allows two-qubit decompositions based on the @@ -1577,7 +1553,7 @@ struct GateDecompositionPattern final const auto basisDecomposer = TwoQubitWeylDecomposition::create( getTwoQubitMatrix(basisGate), basisFidelity); - const auto superControlled = + const auto isSuperControlled = relativeEq(basisDecomposer.a, qc::PI_4, 1e-13, 1e-09) && relativeEq(basisDecomposer.c, 0.0, 1e-13, 1e-09); @@ -1653,29 +1629,29 @@ struct GateDecompositionPattern final auto q2r = k2rDagger * k12RArr; return TwoQubitBasisDecomposer{ - .basisGate = basisGate, - .basisFidelity = basisFidelity, - .basisDecomposer = basisDecomposer, - .superControlled = superControlled, - .u0l = u0l, - .u0r = u0r, - .u1l = u1l, - .u1ra = u1ra, - .u1rb = u1rb, - .u2la = u2la, - .u2lb = u2lb, - .u2ra = u2ra, - .u2rb = u2rb, - .u3l = u3l, - .u3r = u3r, - .q0l = q0l, - .q0r = q0r, - .q1la = q1la, - .q1lb = q1lb, - .q1ra = q1ra, - .q1rb = q1rb, - .q2l = q2l, - .q2r = q2r, + basisGate, + basisFidelity, + basisDecomposer, + isSuperControlled, + u0l, + u0r, + u1l, + u1ra, + u1rb, + u2la, + u2lb, + u2ra, + u2rb, + u3l, + u3r, + q0l, + q0r, + q1la, + q1lb, + q1ra, + q1rb, + q2l, + q2r, }; } @@ -1798,7 +1774,29 @@ struct GateDecompositionPattern final return gates; } - private: + protected: + /** + * Constructs decomposer instance. + */ + TwoQubitBasisDecomposer(QubitGateSequence::Gate basisGate, fp basisFidelity, + TwoQubitWeylDecomposition basisDecomposer, + bool isSuperControlled, matrix2x2 u0l, + matrix2x2 u0r, matrix2x2 u1l, matrix2x2 u1ra, + matrix2x2 u1rb, matrix2x2 u2la, matrix2x2 u2lb, + matrix2x2 u2ra, matrix2x2 u2rb, matrix2x2 u3l, + matrix2x2 u3r, matrix2x2 q0l, matrix2x2 q0r, + matrix2x2 q1la, matrix2x2 q1lb, matrix2x2 q1ra, + matrix2x2 q1rb, matrix2x2 q2l, matrix2x2 q2r) + : basisGate{std::move(basisGate)}, basisFidelity{basisFidelity}, + basisDecomposer{std::move(basisDecomposer)}, + isSuperControlled{isSuperControlled}, u0l{std::move(u0l)}, + u0r{std::move(u0r)}, u1l{std::move(u1l)}, u1ra{std::move(u1ra)}, + u1rb{std::move(u1rb)}, u2la{std::move(u2la)}, u2lb{std::move(u2lb)}, + u2ra{std::move(u2ra)}, u2rb{std::move(u2rb)}, u3l{std::move(u3l)}, + u3r{std::move(u3r)}, q0l{std::move(q0l)}, q0r{std::move(q0r)}, + q1la{std::move(q1la)}, q1lb{std::move(q1lb)}, q1ra{std::move(q1ra)}, + q1rb{std::move(q1rb)}, q2l{std::move(q2l)}, q2r{std::move(q2r)} {} + /** * Calculate decompositions when no basis gate is required. * @@ -1986,68 +1984,101 @@ struct GateDecompositionPattern final return bestCircuit; } - // TODO: copied+adapted from single-qubit decomposition - /** - * @note Adapted from circuit_kak() in the IBM Qiskit framework. - * (C) Copyright IBM 2022 - * - * This code is licensed under the Apache License, Version 2.0. You - * may obtain a copy of this license in the LICENSE.txt file in the root - * directory of this source tree or at - * http://www.apache.org/licenses/LICENSE-2.0. - * - * Any modifications or derivative works of this code must retain - * this copyright notice, and modified files need to carry a notice - * indicating that they have been altered from the originals. - */ - [[nodiscard]] static OneQubitGateSequence - calculateRotationGates(fp theta, fp phi, fp lambda, fp phase, - qc::OpType kGate, qc::OpType aGate, bool simplify, - std::optional atol) { - fp angleZeroEpsilon = atol.value_or(DEFAULT_ATOL); - if (!simplify) { - angleZeroEpsilon = -1.0; - } + private: + // basis gate of this decomposer instance + QubitGateSequence::Gate basisGate; + // fidelity with which the basis gate decomposition has been calculated + fp basisFidelity; + // cached decomposition for basis gate + TwoQubitWeylDecomposition basisDecomposer; + // true if basis gate is super-controlled + bool isSuperControlled; - OneQubitGateSequence sequence{ - .gates = {}, - .globalPhase = phase - ((phi + lambda) / 2.), - }; - if (std::abs(theta) <= angleZeroEpsilon) { - lambda += phi; - lambda = mod2pi(lambda); - if (std::abs(lambda) > angleZeroEpsilon) { - sequence.gates.push_back({.type = kGate, .parameter = {lambda}}); - sequence.globalPhase += lambda / 2.0; - } - return sequence; - } + // pre-built components for decomposition with 3 basis gates + matrix2x2 u0l; + matrix2x2 u0r; + matrix2x2 u1l; + matrix2x2 u1ra; + matrix2x2 u1rb; + matrix2x2 u2la; + matrix2x2 u2lb; + matrix2x2 u2ra; + matrix2x2 u2rb; + matrix2x2 u3l; + matrix2x2 u3r; - if (std::abs(theta - qc::PI) <= angleZeroEpsilon) { - sequence.globalPhase += phi; - lambda -= phi; - phi = 0.0; - } - if (std::abs(mod2pi(lambda + qc::PI)) <= angleZeroEpsilon || - std::abs(mod2pi(phi + qc::PI)) <= angleZeroEpsilon) { - lambda += qc::PI; - theta = -theta; - phi += qc::PI; - } + // pre-built components for decomposition with 2 basis gates + matrix2x2 q0l; + matrix2x2 q0r; + matrix2x2 q1la; + matrix2x2 q1lb; + matrix2x2 q1ra; + matrix2x2 q1rb; + matrix2x2 q2l; + matrix2x2 q2r; + }; + + // TODO: copied+adapted from single-qubit decomposition + /** + * @note Adapted from circuit_kak() in the IBM Qiskit framework. + * (C) Copyright IBM 2022 + * + * This code is licensed under the Apache License, Version 2.0. You + * may obtain a copy of this license in the LICENSE.txt file in the root + * directory of this source tree or at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain + * this copyright notice, and modified files need to carry a notice + * indicating that they have been altered from the originals. + */ + [[nodiscard]] static OneQubitGateSequence + calculateRotationGates(fp theta, fp phi, fp lambda, fp phase, + qc::OpType kGate, qc::OpType aGate, bool simplify, + std::optional atol) { + fp angleZeroEpsilon = atol.value_or(DEFAULT_ATOL); + if (!simplify) { + angleZeroEpsilon = -1.0; + } + + OneQubitGateSequence sequence{ + .gates = {}, + .globalPhase = phase - ((phi + lambda) / 2.), + }; + if (std::abs(theta) <= angleZeroEpsilon) { + lambda += phi; lambda = mod2pi(lambda); if (std::abs(lambda) > angleZeroEpsilon) { - sequence.globalPhase += lambda / 2.0; sequence.gates.push_back({.type = kGate, .parameter = {lambda}}); - } - sequence.gates.push_back({.type = aGate, .parameter = {theta}}); - phi = mod2pi(phi); - if (std::abs(phi) > angleZeroEpsilon) { - sequence.globalPhase += phi / 2.0; - sequence.gates.push_back({.type = kGate, .parameter = {phi}}); + sequence.globalPhase += lambda / 2.0; } return sequence; } - }; + + if (std::abs(theta - qc::PI) <= angleZeroEpsilon) { + sequence.globalPhase += phi; + lambda -= phi; + phi = 0.0; + } + if (std::abs(mod2pi(lambda + qc::PI)) <= angleZeroEpsilon || + std::abs(mod2pi(phi + qc::PI)) <= angleZeroEpsilon) { + lambda += qc::PI; + theta = -theta; + phi += qc::PI; + } + lambda = mod2pi(lambda); + if (std::abs(lambda) > angleZeroEpsilon) { + sequence.globalPhase += lambda / 2.0; + sequence.gates.push_back({.type = kGate, .parameter = {lambda}}); + } + sequence.gates.push_back({.type = aGate, .parameter = {theta}}); + phi = mod2pi(phi); + if (std::abs(phi) > angleZeroEpsilon) { + sequence.globalPhase += phi / 2.0; + sequence.gates.push_back({.type = kGate, .parameter = {phi}}); + } + return sequence; + } private: llvm::SmallVector decomposerBasisGate; From 76ca4d05b33ad3f776835080f74ced075ba690ed Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 26 Nov 2025 00:26:06 +0100 Subject: [PATCH 108/237] attempt to fix CI test --- .../Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp | 4 ++-- mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 7c48b7c913..fe949af738 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -1767,9 +1767,9 @@ struct GateDecompositionPattern final addEulerDecomposition((2UL * bestNbasis) + 1, 1); // large global phases can be generated by the decomposition, thus limit - // it to (-2*pi, +2*pi); TODO: can be removed, should be done by something + // it to [0, +2*pi); TODO: can be removed, should be done by something // like constant folding - gates.globalPhase = std::fmod(gates.globalPhase, qc::TAU); + gates.globalPhase = remEuclid(gates.globalPhase, qc::TAU); return gates; } diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index c38c2ad403..79510e6045 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -371,7 +371,7 @@ module { // CHECK: %[[C16:.*]] = arith.constant -0.03425899788 // CHECK: %[[C17:.*]] = arith.constant -1.57079632679 // CHECK: %[[C18:.*]] = arith.constant -2.39270110306 - // CHECK: %[[C19:.*]] = arith.constant -0.78539816339 + // CHECK: %[[C19:.*]] = arith.constant 5.497787143782 // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit @@ -494,7 +494,7 @@ module { // CHECK: %[[C4:.*]] = arith.constant -1.5707963 // CHECK: %[[C5:.*]] = arith.constant -1.5707963 // CHECK: %[[C6:.*]] = arith.constant 2.35619449 - // CHECK: %[[C7:.*]] = arith.constant -3.1415926 + // CHECK: %[[C7:.*]] = arith.constant 3.1415926 // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit From ec7f8c38a4bd497e5debc0774ea8a123b1d74e67 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 26 Nov 2025 13:24:49 +0100 Subject: [PATCH 109/237] add supercontrolled check --- .../MQTOpt/Transforms/GateDecompositionPattern.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index fe949af738..6c337438c2 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -1868,6 +1868,12 @@ struct GateDecompositionPattern final */ [[nodiscard]] llvm::SmallVector decomp2Supercontrolled(const TwoQubitWeylDecomposition& target) const { + if (!isSuperControlled) { + // TODO: make fatal error? check in constructor? + llvm::errs() + << "Basis gate of TwoQubitBasisDecomposer is not super-controlled " + "- no guarantee for exact decomposition with two basis gates\n"; + } return { q2r * target.k2r, q2l * target.k2l, @@ -1889,6 +1895,12 @@ struct GateDecompositionPattern final */ [[nodiscard]] llvm::SmallVector decomp3Supercontrolled(const TwoQubitWeylDecomposition& target) const { + if (!isSuperControlled) { + llvm::errs() + << "Basis gate of TwoQubitBasisDecomposer is not super-controlled " + "- no guarantee for exact decomposition with " + "three basis gates\n"; + } return { u3r * target.k2r, u3l * target.k2l, From 49a769a37c36aa4938a8de3c6747f0d092cfb119 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 26 Nov 2025 17:10:23 +0100 Subject: [PATCH 110/237] extract decomposition into headers --- .../Decomposition/BasisDecomposer.h | 547 ++++++ .../Transforms/Decomposition/EulerBasis.h | 40 + .../Decomposition/EulerDecomposition.h | 214 ++ .../MQTOpt/Transforms/Decomposition/Gate.h | 32 + .../Transforms/Decomposition/GateSequence.h | 99 + .../MQTOpt/Transforms/Decomposition/Helpers.h | 238 +++ .../Decomposition/UnitaryMatrices.h | 158 ++ .../Decomposition/WeylDecomposition.h | 786 ++++++++ .../Transforms/GateDecompositionPattern.cpp | 1745 +---------------- 9 files changed, 2158 insertions(+), 1701 deletions(-) create mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h create mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerBasis.h create mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerDecomposition.h create mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/Gate.h create mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h create mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h create mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h create mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h new file mode 100644 index 0000000000..6a41aec896 --- /dev/null +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include "EulerBasis.h" +#include "EulerDecomposition.h" +#include "GateSequence.h" +#include "Helpers.h" +#include "UnitaryMatrices.h" +#include "WeylDecomposition.h" +#include "ir/Definitions.hpp" + +#include // NOLINT(misc-include-cleaner) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // TODO: unstable, NOLINT(misc-include-cleaner) +#include + +namespace mqt::ir::opt::decomposition { + +/** + * Decomposer that must be initialized with a two-qubit basis gate that will + * be used to generate a circuit equivalent to a canonical gate (RXX+RYY+RZZ). + */ +class TwoQubitBasisDecomposer { +public: + /** + * Create decomposer that allows two-qubit decompositions based on the + * specified basis gate. + * This basis gate will appear between 0 and 3 times in each decompositions. + * The order of qubits is relevant and will change the results accordingly. + * The decomposer cannot handle different basis gates in the same + * decomposition (different order of the qubits also counts as a different + * basis gate). + */ + static TwoQubitBasisDecomposer create(const Gate& basisGate, + fp basisFidelity) { + auto relativeEq = [](auto&& lhs, auto&& rhs, auto&& epsilon, + auto&& maxRelative) { + // Handle same infinities + if (lhs == rhs) { + return true; + } + + // Handle remaining infinities + if (std::isinf(lhs) || std::isinf(rhs)) { + return false; + } + + auto absDiff = std::abs(lhs - rhs); + + // For when the numbers are really close together + if (absDiff <= epsilon) { + return true; + } + + auto absLhs = std::abs(lhs); + auto absRhs = std::abs(rhs); + if (absRhs > absLhs) { + return absDiff <= absRhs * maxRelative; + } + return absDiff <= absLhs * maxRelative; + }; + const matrix2x2 k12RArr{ + {qfp(0., FRAC1_SQRT2), qfp(FRAC1_SQRT2, 0.)}, + {qfp(-FRAC1_SQRT2, 0.), qfp(0., -FRAC1_SQRT2)}, + }; + const matrix2x2 k12LArr{ + {qfp(0.5, 0.5), qfp(0.5, 0.5)}, + {qfp(-0.5, 0.5), qfp(0.5, -0.5)}, + }; + + const auto basisDecomposer = + decomposition::TwoQubitWeylDecomposition::create( + getTwoQubitMatrix(basisGate), basisFidelity); + const auto isSuperControlled = + relativeEq(basisDecomposer.a, qc::PI_4, 1e-13, 1e-09) && + relativeEq(basisDecomposer.c, 0.0, 1e-13, 1e-09); + + // Create some useful matrices U1, U2, U3 are equivalent to the basis, + // expand as Ui = Ki1.Ubasis.Ki2 + auto b = basisDecomposer.b; + auto temp = qfp(0.5, -0.5); + const matrix2x2 k11l{ + {temp * (M_IM * std::exp(qfp(0., -b))), temp * std::exp(qfp(0., -b))}, + {temp * (M_IM * std::exp(qfp(0., b))), temp * -std::exp(qfp(0., b))}}; + const matrix2x2 k11r{{FRAC1_SQRT2 * (IM * std::exp(qfp(0., -b))), + FRAC1_SQRT2 * -std::exp(qfp(0., -b))}, + {FRAC1_SQRT2 * std::exp(qfp(0., b)), + FRAC1_SQRT2 * (M_IM * std::exp(qfp(0., b)))}}; + const matrix2x2 k32lK21l{{FRAC1_SQRT2 * qfp(1., std::cos(2. * b)), + FRAC1_SQRT2 * (IM * std::sin(2. * b))}, + {FRAC1_SQRT2 * (IM * std::sin(2. * b)), + FRAC1_SQRT2 * qfp(1., -std::cos(2. * b))}}; + temp = qfp(0.5, 0.5); + const matrix2x2 k21r{ + {temp * (M_IM * std::exp(qfp(0., -2. * b))), + temp * std::exp(qfp(0., -2. * b))}, + {temp * (IM * std::exp(qfp(0., 2. * b))), + temp * std::exp(qfp(0., 2. * b))}, + }; + const matrix2x2 k22l{ + {qfp(FRAC1_SQRT2, 0.), qfp(-FRAC1_SQRT2, 0.)}, + {qfp(FRAC1_SQRT2, 0.), qfp(FRAC1_SQRT2, 0.)}, + }; + const matrix2x2 k22r{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; + const matrix2x2 k31l{ + {FRAC1_SQRT2 * std::exp(qfp(0., -b)), + FRAC1_SQRT2 * std::exp(qfp(0., -b))}, + {FRAC1_SQRT2 * -std::exp(qfp(0., b)), + FRAC1_SQRT2 * std::exp(qfp(0., b))}, + }; + const matrix2x2 k31r{ + {IM * std::exp(qfp(0., b)), C_ZERO}, + {C_ZERO, M_IM * std::exp(qfp(0., -b))}, + }; + temp = qfp(0.5, 0.5); + const matrix2x2 k32r{ + {temp * std::exp(qfp(0., b)), temp * -std::exp(qfp(0., -b))}, + {temp * (M_IM * std::exp(qfp(0., b))), + temp * (M_IM * std::exp(qfp(0., -b)))}, + }; + auto k1lDagger = basisDecomposer.k1l.transpose().conjugate(); + auto k1rDagger = basisDecomposer.k1r.transpose().conjugate(); + auto k2lDagger = basisDecomposer.k2l.transpose().conjugate(); + auto k2rDagger = basisDecomposer.k2r.transpose().conjugate(); + // Pre-build the fixed parts of the matrices used in 3-part + // decomposition + auto u0l = k31l * k1lDagger; + auto u0r = k31r * k1rDagger; + auto u1l = k2lDagger * k32lK21l * k1lDagger; + auto u1ra = k2rDagger * k32r; + auto u1rb = k21r * k1rDagger; + auto u2la = k2lDagger * k22l; + auto u2lb = k11l * k1lDagger; + auto u2ra = k2rDagger * k22r; + auto u2rb = k11r * k1rDagger; + auto u3l = k2lDagger * k12LArr; + auto u3r = k2rDagger * k12RArr; + // Pre-build the fixed parts of the matrices used in the 2-part + // decomposition + auto q0l = k12LArr.transpose().conjugate() * k1lDagger; + auto q0r = k12RArr.transpose().conjugate() * IPZ * k1rDagger; + auto q1la = k2lDagger * k11l.transpose().conjugate(); + auto q1lb = k11l * k1lDagger; + auto q1ra = k2rDagger * IPZ * k11r.transpose().conjugate(); + auto q1rb = k11r * k1rDagger; + auto q2l = k2lDagger * k12LArr; + auto q2r = k2rDagger * k12RArr; + + return TwoQubitBasisDecomposer{ + basisGate, + basisFidelity, + basisDecomposer, + isSuperControlled, + u0l, + u0r, + u1l, + u1ra, + u1rb, + u2la, + u2lb, + u2ra, + u2rb, + u3l, + u3r, + q0l, + q0r, + q1la, + q1lb, + q1ra, + q1rb, + q2l, + q2r, + }; + } + + /** + * Perform decomposition using the basis gate of this decomposer. + * + * @param targetDecomposition Prepared Weyl decomposition of unitary matrix + * to be decomposed. + * @param target1qEulerBases List of euler bases that should be tried out to + * find the best one for each euler decomposition. + * All bases will be mixed to get the best overall + * result. + * @param basisFidelity Fidelity for lowering the number of basis gates + * required + * @param approximate If true, use basisFidelity or, if std::nullopt, use + * basisFidelity of this decomposer. If false, fidelity + * of 1.0 will be assumed. + * @param numBasisGateUses Force use of given number of basis gates. + */ + [[nodiscard]] std::optional twoQubitDecompose( + const decomposition::TwoQubitWeylDecomposition& targetDecomposition, + const llvm::SmallVector& target1qEulerBases, + std::optional basisFidelity, bool approximate, + std::optional numBasisGateUses) const { + auto getBasisFidelity = [&]() { + if (approximate) { + return basisFidelity.value_or(this->basisFidelity); + } + return static_cast(1.0); + }; + fp actualBasisFidelity = getBasisFidelity(); + auto traces = this->traces(targetDecomposition); + auto getDefaultNbasis = [&]() { + auto minValue = std::numeric_limits::min(); + auto minIndex = -1; + for (int i = 0; std::cmp_less(i, traces.size()); ++i) { + // lower fidelity means it becomes easier to choose a lower number of + // basis gates + auto value = helpers::traceToFidelity(traces[i]) * + std::pow(actualBasisFidelity, i); + if (value > minValue) { + minIndex = i; + minValue = value; + } + } + return minIndex; + }; + // number of basis gates that need to be used in the decomposition + auto bestNbasis = numBasisGateUses.value_or(getDefaultNbasis()); + auto chooseDecomposition = [&]() { + if (bestNbasis == 0) { + return decomp0(targetDecomposition); + } + if (bestNbasis == 1) { + return decomp1(targetDecomposition); + } + if (bestNbasis == 2) { + return decomp2Supercontrolled(targetDecomposition); + } + if (bestNbasis == 3) { + return decomp3Supercontrolled(targetDecomposition); + } + throw std::logic_error{"Invalid basis to use"}; + }; + auto decomposition = chooseDecomposition(); + llvm::SmallVector, 8> + eulerDecompositions; + for (auto&& decomp : decomposition) { + assert(helpers::isUnitaryMatrix(decomp)); + auto eulerDecomp = unitaryToGateSequenceInner(decomp, target1qEulerBases, + 0, true, std::nullopt); + eulerDecompositions.push_back(eulerDecomp); + } + TwoQubitGateSequence gates{ + .gates = {}, + .globalPhase = targetDecomposition.globalPhase, + }; + // Worst case length is 5x 1q gates for each 1q decomposition + 1x 2q + // gate We might overallocate a bit if the euler basis is different but + // the worst case is just 16 extra elements with just a String and 2 + // smallvecs each. This is only transient though as the circuit + // sequences aren't long lived and are just used to create a + // QuantumCircuit or DAGCircuit when we return to Python space. + constexpr auto twoQubitSequenceDefaultCapacity = 21; + gates.gates.reserve(twoQubitSequenceDefaultCapacity); + gates.globalPhase -= bestNbasis * basisDecomposer.globalPhase; + if (bestNbasis == 2) { + gates.globalPhase += qc::PI; + } + + auto addEulerDecomposition = [&](std::size_t index, QubitId qubitId) { + if (auto&& eulerDecomp = eulerDecompositions[index]) { + for (auto&& gate : eulerDecomp->gates) { + gates.gates.push_back({.type = gate.type, + .parameter = gate.parameter, + .qubitId = {qubitId}}); + } + gates.globalPhase += eulerDecomp->globalPhase; + } + }; + + for (std::size_t i = 0; i < bestNbasis; ++i) { + // add single-qubit decompositions before basis gate + addEulerDecomposition(2 * i, 0); + addEulerDecomposition((2 * i) + 1, 1); + + // add basis gate + gates.gates.push_back(basisGate); + } + + // add single-qubit decompositions after basis gate + addEulerDecomposition(2UL * bestNbasis, 0); + addEulerDecomposition((2UL * bestNbasis) + 1, 1); + + // large global phases can be generated by the decomposition, thus limit + // it to [0, +2*pi); TODO: can be removed, should be done by something + // like constant folding + gates.globalPhase = helpers::remEuclid(gates.globalPhase, qc::TAU); + + return gates; + } + +protected: + /** + * Constructs decomposer instance. + */ + TwoQubitBasisDecomposer( + Gate basisGate, fp basisFidelity, + decomposition::TwoQubitWeylDecomposition basisDecomposer, + bool isSuperControlled, matrix2x2 u0l, matrix2x2 u0r, matrix2x2 u1l, + matrix2x2 u1ra, matrix2x2 u1rb, matrix2x2 u2la, matrix2x2 u2lb, + matrix2x2 u2ra, matrix2x2 u2rb, matrix2x2 u3l, matrix2x2 u3r, + matrix2x2 q0l, matrix2x2 q0r, matrix2x2 q1la, matrix2x2 q1lb, + matrix2x2 q1ra, matrix2x2 q1rb, matrix2x2 q2l, matrix2x2 q2r) + : basisGate{std::move(basisGate)}, basisFidelity{basisFidelity}, + basisDecomposer{std::move(basisDecomposer)}, + isSuperControlled{isSuperControlled}, u0l{std::move(u0l)}, + u0r{std::move(u0r)}, u1l{std::move(u1l)}, u1ra{std::move(u1ra)}, + u1rb{std::move(u1rb)}, u2la{std::move(u2la)}, u2lb{std::move(u2lb)}, + u2ra{std::move(u2ra)}, u2rb{std::move(u2rb)}, u3l{std::move(u3l)}, + u3r{std::move(u3r)}, q0l{std::move(q0l)}, q0r{std::move(q0r)}, + q1la{std::move(q1la)}, q1lb{std::move(q1lb)}, q1ra{std::move(q1ra)}, + q1rb{std::move(q1rb)}, q2l{std::move(q2l)}, q2r{std::move(q2r)} {} + + /** + * Calculate decompositions when no basis gate is required. + * + * Decompose target :math:`\sim U_d(x, y, z)` with 0 uses of the + * basis gate. Result :math:`U_r` has trace: + * + * .. math:: + * + * \Big\vert\text{Tr}(U_r\cdot U_\text{target}^{\dag})\Big\vert = + * 4\Big\vert (\cos(x)\cos(y)\cos(z)+ j \sin(x)\sin(y)\sin(z)\Big\vert + * + * which is optimal for all targets and bases + */ + [[nodiscard]] static llvm::SmallVector + decomp0(const decomposition::TwoQubitWeylDecomposition& target) { + return { + target.k1r * target.k2r, + target.k1l * target.k2l, + }; + } + + /** + * Calculate decompositions when one basis gate is required. + * + * Decompose target :math:`\sim U_d(x, y, z)` with 1 use of the + * basis gate math:`\sim U_d(a, b, c)`. Result :math:`U_r` has trace: + * + * .. math:: + * + * \Big\vert\text{Tr}(U_r \cdot U_\text{target}^{\dag})\Big\vert = + * 4\Big\vert \cos(x-a)\cos(y-b)\cos(z-c) + j + * \sin(x-a)\sin(y-b)\sin(z-c)\Big\vert + * + * which is optimal for all targets and bases with ``z==0`` or ``c==0``. + */ + [[nodiscard]] llvm::SmallVector + decomp1(const decomposition::TwoQubitWeylDecomposition& target) const { + // may not work for z != 0 and c != 0 (not always in Weyl chamber) + return { + basisDecomposer.k2r.transpose().conjugate() * target.k2r, + basisDecomposer.k2l.transpose().conjugate() * target.k2l, + target.k1r * basisDecomposer.k1r.transpose().conjugate(), + target.k1l * basisDecomposer.k1l.transpose().conjugate(), + }; + } + + /** + * Calculate decompositions when two basis gates are required. + * + * Decompose target :math:`\sim U_d(x, y, z)` with 2 uses of the + * basis gate. + * + * For supercontrolled basis :math:`\sim U_d(\pi/4, b, 0)`, all b, result + * :math:`U_r` has trace + * + * .. math:: + * + * \Big\vert\text{Tr}(U_r \cdot U_\text{target}^\dag) \Big\vert = + * 4\cos(z) + * + * which is the optimal approximation for basis of CNOT-class + * :math:`\sim U_d(\pi/4, 0, 0)` or DCNOT-class + * :math:`\sim U_d(\pi/4, \pi/4, 0)` and any target. It may be sub-optimal + * for :math:`b \neq 0` (i.e. there exists an exact decomposition for any + * target using :math:`B \sim U_d(\pi/4, \pi/8, 0)`, but it may not be this + * decomposition). This is an exact decomposition for supercontrolled basis + * and target :math:`\sim U_d(x, y, 0)`. No guarantees for + * non-supercontrolled basis. + */ + [[nodiscard]] llvm::SmallVector decomp2Supercontrolled( + const decomposition::TwoQubitWeylDecomposition& target) const { + if (!isSuperControlled) { + // TODO: make fatal error? check in constructor? + llvm::errs() + << "Basis gate of TwoQubitBasisDecomposer is not super-controlled " + "- no guarantee for exact decomposition with two basis gates\n"; + } + return { + q2r * target.k2r, + q2l * target.k2l, + q1ra * rzMatrix(2. * target.b) * q1rb, + q1la * rzMatrix(-2. * target.a) * q1lb, + target.k1r * q0r, + target.k1l * q0l, + }; + } + + /** + * Calculate decompositions when three basis gates are required. + * + * Decompose target with 3 uses of the basis. + * + * This is an exact decomposition for supercontrolled basis + * :math:`\sim U_d(\pi/4, b, 0)`, all b, and any target. No guarantees for + * non-supercontrolled basis. + */ + [[nodiscard]] llvm::SmallVector decomp3Supercontrolled( + const decomposition::TwoQubitWeylDecomposition& target) const { + if (!isSuperControlled) { + llvm::errs() + << "Basis gate of TwoQubitBasisDecomposer is not super-controlled " + "- no guarantee for exact decomposition with " + "three basis gates\n"; + } + return { + u3r * target.k2r, + u3l * target.k2l, + u2ra * rzMatrix(2. * target.b) * u2rb, + u2la * rzMatrix(-2. * target.a) * u2lb, + u1ra * rzMatrix(-2. * target.c) * u1rb, + u1l, + target.k1r * u0r, + target.k1l * u0l, + }; + } + + /** + * Calculate traces for a combination of the parameters of the canonical + * gates of the target and basis decompositions. + * This can be used to determine the smallest number of basis gates that are + * necessary to construct an equivalent to the canonical gate. + */ + [[nodiscard]] std::array + traces(const decomposition::TwoQubitWeylDecomposition& target) const { + return { + static_cast(4.) * + qfp(std::cos(target.a) * std::cos(target.b) * std::cos(target.c), + std::sin(target.a) * std::sin(target.b) * std::sin(target.c)), + static_cast(4.) * + qfp(std::cos(qc::PI_4 - target.a) * + std::cos(basisDecomposer.b - target.b) * std::cos(target.c), + std::sin(qc::PI_4 - target.a) * + std::sin(basisDecomposer.b - target.b) * + std::sin(target.c)), + qfp(4. * std::cos(target.c), 0.), + qfp(4., 0.), + }; + } + + /** + * Decompose a single-qubit unitary matrix into a single-qubit gate + * sequence. Multiple euler bases may be specified and the one with the + * least complexity will be chosen. + */ + [[nodiscard]] static OneQubitGateSequence unitaryToGateSequenceInner( + const matrix2x2& unitaryMat, + const llvm::SmallVector& targetBasisList, QubitId /*qubit*/, + // TODO: add error map here: per qubit a mapping of operation to error + // value for better calculateError() + bool simplify, std::optional atol) { + auto calculateError = [](const OneQubitGateSequence& sequence) -> fp { + return static_cast(sequence.complexity()); + }; + + auto minError = std::numeric_limits::max(); + OneQubitGateSequence bestCircuit; + for (auto targetBasis : targetBasisList) { + auto circuit = EulerDecomposition::generateCircuit( + targetBasis, unitaryMat, simplify, atol); + assert(circuit.getUnitaryMatrix().isApprox( + helpers::kroneckerProduct(IDENTITY_GATE, unitaryMat), + SANITY_CHECK_PRECISION)); + auto error = calculateError(circuit); + if (error < minError) { + bestCircuit = circuit; + minError = error; + } + } + return bestCircuit; + } + +private: + // basis gate of this decomposer instance + Gate basisGate{}; + // fidelity with which the basis gate decomposition has been calculated + fp basisFidelity; + // cached decomposition for basis gate + decomposition::TwoQubitWeylDecomposition basisDecomposer; + // true if basis gate is super-controlled + bool isSuperControlled; + + // pre-built components for decomposition with 3 basis gates + matrix2x2 u0l; + matrix2x2 u0r; + matrix2x2 u1l; + matrix2x2 u1ra; + matrix2x2 u1rb; + matrix2x2 u2la; + matrix2x2 u2lb; + matrix2x2 u2ra; + matrix2x2 u2rb; + matrix2x2 u3l; + matrix2x2 u3r; + + // pre-built components for decomposition with 2 basis gates + matrix2x2 q0l; + matrix2x2 q0r; + matrix2x2 q1la; + matrix2x2 q1lb; + matrix2x2 q1ra; + matrix2x2 q1rb; + matrix2x2 q2l; + matrix2x2 q2r; +}; + +} // namespace mqt::ir::opt::decomposition diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerBasis.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerBasis.h new file mode 100644 index 0000000000..5924ba530e --- /dev/null +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerBasis.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include + +namespace mqt::ir::opt::decomposition { +/** + * Largest number that will be assumed as zero for the euler decompositions. + */ +static constexpr auto DEFAULT_ATOL = 1e-12; + +/** + * EulerBasis for a euler decomposition. + * + * @note only the following bases are supported for now: ZYZ, ZXZ and XZX + */ +enum class EulerBasis : std::uint8_t { + U3 = 0, + U321 = 1, + U = 2, + PSX = 3, + U1X = 4, + RR = 5, + ZYZ = 6, + ZXZ = 7, + XZX = 8, + XYX = 9, + ZSXX = 10, + ZSX = 11, +}; +} // namespace mqt::ir::opt::decomposition diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerDecomposition.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerDecomposition.h new file mode 100644 index 0000000000..bd99a2d301 --- /dev/null +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerDecomposition.h @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include "EulerBasis.h" +#include "GateSequence.h" +#include "Helpers.h" +#include "ir/Definitions.hpp" +#include "ir/operations/OpType.hpp" + +#include // NOLINT(misc-include-cleaner) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // TODO: unstable, NOLINT(misc-include-cleaner) + +namespace mqt::ir::opt::decomposition { + +/** + * Decomposition of single-qubit matrices into rotation gates using a KAK + * decomposition. + */ +class EulerDecomposition { +public: + /** + * Perform single-qubit decomposition of a 2x2 unitary matrix based on a + * given euler basis. + */ + [[nodiscard]] static OneQubitGateSequence + generateCircuit(EulerBasis targetBasis, const matrix2x2& unitaryMatrix, + bool simplify, std::optional atol) { + auto [theta, phi, lambda, phase] = + anglesFromUnitary(unitaryMatrix, targetBasis); + + switch (targetBasis) { + case EulerBasis::ZYZ: + return decomposeKAK(theta, phi, lambda, phase, qc::RZ, qc::RY, simplify, + atol); + case EulerBasis::ZXZ: + return decomposeKAK(theta, phi, lambda, phase, qc::RZ, qc::RX, simplify, + atol); + case EulerBasis::XZX: + return decomposeKAK(theta, phi, lambda, phase, qc::RX, qc::RZ, simplify, + atol); + case EulerBasis::XYX: + return decomposeKAK(theta, phi, lambda, phase, qc::RX, qc::RY, simplify, + atol); + default: + // TODO: allow other bases + throw std::invalid_argument{"Unsupported base for circuit generation!"}; + } + } + + /** + * Calculate angles of a single-qubit matrix according to the given + * EulerBasis. + * + * @return array containing (theta, phi, lambda, phase) in this order + */ + static std::array anglesFromUnitary(const matrix2x2& matrix, + EulerBasis basis) { + if (basis == EulerBasis::XYX) { + return paramsXyxInner(matrix); + } + if (basis == EulerBasis::XZX) { + return paramsXzxInner(matrix); + } + if (basis == EulerBasis::ZYZ) { + return paramsZyzInner(matrix); + } + if (basis == EulerBasis::ZXZ) { + return paramsZxzInner(matrix); + } + throw std::invalid_argument{"Unknown EulerBasis for angles_from_unitary"}; + } + +private: + static std::array paramsZyzInner(const matrix2x2& matrix) { + const auto detArg = std::arg(matrix.determinant()); + const auto phase = 0.5 * detArg; + const auto theta = + 2. * std::atan2(std::abs(matrix(1, 0)), std::abs(matrix(0, 0))); + const auto ang1 = std::arg(matrix(1, 1)); + const auto ang2 = std::arg(matrix(1, 0)); + const auto phi = ang1 + ang2 - detArg; + const auto lam = ang1 - ang2; + return {theta, phi, lam, phase}; + } + + static std::array paramsZxzInner(const matrix2x2& matrix) { + const auto [theta, phi, lam, phase] = paramsZyzInner(matrix); + return {theta, phi + (qc::PI / 2.), lam - (qc::PI / 2.), phase}; + } + + static std::array paramsXyxInner(const matrix2x2& matrix) { + const matrix2x2 matZyz{ + {static_cast(0.5) * + (matrix(0, 0) + matrix(0, 1) + matrix(1, 0) + matrix(1, 1)), + static_cast(0.5) * + (matrix(0, 0) - matrix(0, 1) + matrix(1, 0) - matrix(1, 1))}, + {static_cast(0.5) * + (matrix(0, 0) + matrix(0, 1) - matrix(1, 0) - matrix(1, 1)), + static_cast(0.5) * + (matrix(0, 0) - matrix(0, 1) - matrix(1, 0) + matrix(1, 1))}, + }; + auto [theta, phi, lam, phase] = paramsZyzInner(matZyz); + auto newPhi = helpers::mod2pi(phi + qc::PI, 0.); + auto newLam = helpers::mod2pi(lam + qc::PI, 0.); + return { + theta, + newPhi, + newLam, + phase + ((newPhi + newLam - phi - lam) / 2.), + }; + } + + static std::array paramsXzxInner(const matrix2x2& matrix) { + auto det = matrix.determinant(); + auto phase = std::imag(std::log(det)) / 2.0; + auto sqrtDet = std::sqrt(det); + const matrix2x2 matZyz{ + { + {(matrix(0, 0) / sqrtDet).real(), (matrix(1, 0) / sqrtDet).imag()}, + {(matrix(1, 0) / sqrtDet).real(), (matrix(0, 0) / sqrtDet).imag()}, + }, + { + {-(matrix(1, 0) / sqrtDet).real(), (matrix(0, 0) / sqrtDet).imag()}, + {(matrix(0, 0) / sqrtDet).real(), -(matrix(1, 0) / sqrtDet).imag()}, + }, + }; + auto [theta, phi, lam, phase_zxz] = paramsZxzInner(matZyz); + return {theta, phi, lam, phase + phase_zxz}; + } + + /** + * @note Adapted from circuit_kak() in the IBM Qiskit framework. + * (C) Copyright IBM 2022 + * + * This code is licensed under the Apache License, Version 2.0. You + * may obtain a copy of this license in the LICENSE.txt file in the root + * directory of this source tree or at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain + * this copyright notice, and modified files need to carry a notice + * indicating that they have been altered from the originals. + */ + [[nodiscard]] static OneQubitGateSequence + decomposeKAK(fp theta, fp phi, fp lambda, fp phase, qc::OpType kGate, + qc::OpType aGate, bool simplify, std::optional atol) { + fp angleZeroEpsilon = atol.value_or(DEFAULT_ATOL); + if (!simplify) { + angleZeroEpsilon = -1.0; + } + + OneQubitGateSequence sequence{ + .gates = {}, + .globalPhase = phase - ((phi + lambda) / 2.), + }; + if (std::abs(theta) <= angleZeroEpsilon) { + lambda += phi; + lambda = helpers::mod2pi(lambda); + if (std::abs(lambda) > angleZeroEpsilon) { + sequence.gates.push_back({.type = kGate, .parameter = {lambda}}); + sequence.globalPhase += lambda / 2.0; + } + return sequence; + } + + if (std::abs(theta - qc::PI) <= angleZeroEpsilon) { + sequence.globalPhase += phi; + lambda -= phi; + phi = 0.0; + } + if (std::abs(helpers::mod2pi(lambda + qc::PI)) <= angleZeroEpsilon || + std::abs(helpers::mod2pi(phi + qc::PI)) <= angleZeroEpsilon) { + lambda += qc::PI; + theta = -theta; + phi += qc::PI; + } + lambda = helpers::mod2pi(lambda); + if (std::abs(lambda) > angleZeroEpsilon) { + sequence.globalPhase += lambda / 2.0; + sequence.gates.push_back({.type = kGate, .parameter = {lambda}}); + } + sequence.gates.push_back({.type = aGate, .parameter = {theta}}); + phi = helpers::mod2pi(phi); + if (std::abs(phi) > angleZeroEpsilon) { + sequence.globalPhase += phi / 2.0; + sequence.gates.push_back({.type = kGate, .parameter = {phi}}); + } + return sequence; + } +}; +} // namespace mqt::ir::opt::decomposition diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/Gate.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/Gate.h new file mode 100644 index 0000000000..1fdae210c0 --- /dev/null +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/Gate.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include "Helpers.h" +#include "ir/operations/OpType.hpp" + +#include + +namespace mqt::ir::opt::decomposition { + +using QubitId = std::size_t; + +/** + * Gate description which should be able to represent every possible + * one-qubit or two-qubit operation. + */ +struct Gate { + qc::OpType type{qc::I}; + llvm::SmallVector parameter; + llvm::SmallVector qubitId = {0}; +}; + +} // namespace mqt::ir::opt::decomposition diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h new file mode 100644 index 0000000000..98b1676a96 --- /dev/null +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include "Gate.h" +#include "Helpers.h" +#include "UnitaryMatrices.h" +#include "ir/operations/OpType.hpp" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h" + +#include // NOLINT(misc-include-cleaner) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // TODO: unstable, NOLINT(misc-include-cleaner) + +namespace mqt::ir::opt::decomposition { +/** + * Gate sequence of single-qubit and/or two-qubit gates. + */ +struct QubitGateSequence { + /** + * Container sorting the gate sequence in order. + */ + llvm::SmallVector gates; + + /** + * Global phase adjustment required for the sequence. + */ + fp globalPhase{}; + /** + * @return true if the global phase adjustment is not zero. + */ + [[nodiscard]] bool hasGlobalPhase() const { + return std::abs(globalPhase) > DEFAULT_ATOL; + } + + /** + * Calculate complexity of sequence according to getComplexity(). + */ + [[nodiscard]] std::size_t complexity() const { + // TODO: add more sophisticated metric to determine complexity of + // series/sequence + // TODO: caching mechanism? + std::size_t c{}; + for (auto&& gate : gates) { + c += helpers::getComplexity(gate.type, gate.qubitId.size()); + } + if (hasGlobalPhase()) { + // need to add a global phase gate if a global phase needs to be applied + c += helpers::getComplexity(qc::GPhase, 0); + } + return c; + } + + /** + * Calculate overall unitary matrix of the sequence. + */ + [[nodiscard]] matrix4x4 getUnitaryMatrix() const { + matrix4x4 unitaryMatrix = + helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); + for (auto&& gate : gates) { + auto gateMatrix = getTwoQubitMatrix(gate); + unitaryMatrix = gateMatrix * unitaryMatrix; + } + unitaryMatrix *= std::exp(IM * globalPhase); + assert(helpers::isUnitaryMatrix(unitaryMatrix)); + return unitaryMatrix; + } +}; +/** + * Helper type to show that a gate sequence is supposed to only contain + * single-qubit gates. + */ +using OneQubitGateSequence = QubitGateSequence; +/** + * Helper type to show that the gate sequence may contain two-qubit gates. + */ +using TwoQubitGateSequence = QubitGateSequence; + +} // namespace mqt::ir::opt::decomposition diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h new file mode 100644 index 0000000000..c2dfe8dc38 --- /dev/null +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include "ir/Definitions.hpp" +#include "ir/operations/OpType.hpp" +#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" + +#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // TODO: unstable, NOLINT(misc-include-cleaner) + +namespace mqt::ir::opt { +using fp = qc::fp; +using qfp = std::complex; +// NOLINTBEGIN(misc-include-cleaner) +using matrix2x2 = Eigen::Matrix2; +using matrix4x4 = Eigen::Matrix4; +using rmatrix4x4 = Eigen::Matrix4; +using diagonal4x4 = Eigen::Vector; +using rdiagonal4x4 = Eigen::Vector; +// NOLINTEND(misc-include-cleaner) + +constexpr qfp C_ZERO{0., 0.}; +constexpr qfp C_ONE{1., 0.}; +constexpr qfp C_M_ONE{-1., 0.}; +constexpr qfp IM{0., 1.}; +constexpr qfp M_IM{0., -1.}; + +} // namespace mqt::ir::opt + +namespace mqt::ir::opt::helpers { + +std::optional mlirValueToFp(mlir::Value value); + +template +[[nodiscard]] std::optional performMlirFloatBinaryOp(mlir::Value value, + Func&& func) { + if (auto op = value.getDefiningOp()) { + auto lhs = mlirValueToFp(op.getLhs()); + auto rhs = mlirValueToFp(op.getRhs()); + if (lhs && rhs) { + return std::invoke(std::forward(func), *lhs, *rhs); + } + } + return std::nullopt; +} + +template +[[nodiscard]] std::optional performMlirFloatUnaryOp(mlir::Value value, + Func&& func) { + if (auto op = value.getDefiningOp()) { + if (auto operand = mlirValueToFp(op.getOperand())) { + return std::invoke(std::forward(func), *operand); + } + } + return std::nullopt; +} + +[[nodiscard]] inline std::optional mlirValueToFp(mlir::Value value) { + if (auto op = value.getDefiningOp()) { + if (auto attr = llvm::dyn_cast(op.getValue())) { + return attr.getValueAsDouble(); + } + return std::nullopt; + } + if (auto result = performMlirFloatUnaryOp( + value, [](fp a) { return -a; })) { + return result; + } + if (auto result = performMlirFloatUnaryOp( + value, [](fp a) { return a; })) { + return result; + } + if (auto result = performMlirFloatUnaryOp( + value, [](fp a) { return a; })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](fp a, fp b) { return std::max(a, b); })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](fp a, fp b) { return std::max(a, b); })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](fp a, fp b) { return std::min(a, b); })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](fp a, fp b) { return std::min(a, b); })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](fp a, fp b) { return std::fmod(a, b); })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](fp a, fp b) { return a + b; })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](fp a, fp b) { return a * b; })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](fp a, fp b) { return a / b; })) { + return result; + } + if (auto result = performMlirFloatBinaryOp( + value, [](fp a, fp b) { return a - b; })) { + return result; + } + return std::nullopt; +} + +[[nodiscard]] inline llvm::SmallVector +getParameters(UnitaryInterface op) { + llvm::SmallVector parameters; + for (auto&& param : op.getParams()) { + if (auto value = helpers::mlirValueToFp(param)) { + parameters.push_back(*value); + } + } + return parameters; +} + +[[nodiscard]] inline qc::OpType getQcType(UnitaryInterface op) { + try { + const std::string type = op->getName().stripDialect().str(); + return qc::opTypeFromString(type); + } catch (const std::invalid_argument& /*exception*/) { + return qc::OpType::None; + } +} + +[[nodiscard]] inline bool isSingleQubitOperation(UnitaryInterface op) { + auto&& inQubits = op.getInQubits(); + auto&& outQubits = op.getOutQubits(); + const bool isSingleQubitOp = + inQubits.size() == 1 && outQubits.size() == 1 && !op.isControlled(); + return isSingleQubitOp; +} + +[[nodiscard]] inline bool isTwoQubitOperation(UnitaryInterface op) { + auto&& inQubits = op.getInQubits(); + auto&& inPosCtrlQubits = op.getPosCtrlInQubits(); + auto&& inNegCtrlQubits = op.getNegCtrlInQubits(); + auto inQubitSize = + inQubits.size() + inPosCtrlQubits.size() + inNegCtrlQubits.size(); + auto&& outQubits = op.getOutQubits(); + auto&& outPosCtrlQubits = op.getPosCtrlOutQubits(); + auto&& outNegCtrlQubits = op.getNegCtrlOutQubits(); + auto outQubitSize = + outQubits.size() + outPosCtrlQubits.size() + outNegCtrlQubits.size(); + const bool isTwoQubitOp = inQubitSize == 2 && outQubitSize == 2; + return isTwoQubitOp; +} + +// NOLINTBEGIN(misc-include-cleaner) +template +[[nodiscard]] inline Eigen::Matrix4 +kroneckerProduct(const Eigen::Matrix2& lhs, const Eigen::Matrix2& rhs) { + return Eigen::kroneckerProduct(lhs, rhs); +} + +template +[[nodiscard]] inline auto selfAdjointEvd(Eigen::Matrix a) { + Eigen::SelfAdjointEigenSolver s; + s.compute(a); // TODO: computeDirect is faster + auto vecs = s.eigenvectors().eval(); + auto vals = s.eigenvalues(); + return std::make_pair(vecs, vals); +} + +template +[[nodiscard]] bool isUnitaryMatrix(const Eigen::Matrix& matrix) { + return (matrix.transpose().conjugate() * matrix).isIdentity(); +} +// NOLINTEND(misc-include-cleaner) + +[[nodiscard]] inline fp remEuclid(fp a, fp b) { + auto r = std::fmod(a, b); + return (r < 0.0) ? r + std::abs(b) : r; +} + +// Wrap angle into interval [-π,π). If within atol of the endpoint, clamp +// to -π +[[nodiscard]] inline fp mod2pi(fp angle, fp angleZeroEpsilon = 1e-13) { + // remEuclid() isn't exactly the same as Python's % operator, but + // because the RHS here is a constant and positive it is effectively + // equivalent for this case + auto wrapped = remEuclid(angle + qc::PI, qc::TAU) - qc::PI; + if (std::abs(wrapped - qc::PI) < angleZeroEpsilon) { + return -qc::PI; + } + return wrapped; +} + +[[nodiscard]] inline fp traceToFidelity(const qfp& x) { + auto xAbs = std::abs(x); + return (4.0 + xAbs * xAbs) / 20.0; +} + +[[nodiscard]] inline std::size_t getComplexity(qc::OpType type, + std::size_t numOfQubits) { + if (numOfQubits > 1) { + constexpr std::size_t multiQubitFactor = 10; + return (numOfQubits - 1) * multiQubitFactor; + } + if (type == qc::GPhase) { + return 2; + } + return 1; +} + +} // namespace mqt::ir::opt::helpers diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h new file mode 100644 index 0000000000..4393e67dd4 --- /dev/null +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include "Gate.h" +#include "Helpers.h" +#include "ir/operations/OpType.hpp" + +namespace mqt::ir::opt::decomposition { + +inline matrix2x2 rxMatrix(fp theta) { + auto halfTheta = theta / 2.; + auto cos = qfp(std::cos(halfTheta), 0.); + auto isin = qfp(0., -std::sin(halfTheta)); + return matrix2x2{{cos, isin}, {isin, cos}}; +} + +inline matrix2x2 ryMatrix(fp theta) { + auto halfTheta = theta / 2.; + auto cos = qfp(std::cos(halfTheta), 0.); + auto sin = qfp(std::sin(halfTheta), 0.); + return matrix2x2{{cos, -sin}, {sin, cos}}; +} + +inline matrix2x2 rzMatrix(fp theta) { + return matrix2x2{{qfp{std::cos(theta / 2.), -std::sin(theta / 2.)}, 0}, + {0, qfp{std::cos(theta / 2.), std::sin(theta / 2.)}}}; +} + +inline matrix4x4 rxxMatrix(const fp theta) { + const auto cosTheta = std::cos(theta / 2.); + const auto sinTheta = std::sin(theta / 2.); + + return matrix4x4{{cosTheta, C_ZERO, C_ZERO, {0., -sinTheta}}, + {C_ZERO, cosTheta, {0., -sinTheta}, C_ZERO}, + {C_ZERO, {0., -sinTheta}, cosTheta, C_ZERO}, + {{0., -sinTheta}, C_ZERO, C_ZERO, cosTheta}}; +} + +inline matrix4x4 ryyMatrix(const fp theta) { + const auto cosTheta = std::cos(theta / 2.); + const auto sinTheta = std::sin(theta / 2.); + + return matrix4x4{{{cosTheta, 0, 0, {0., sinTheta}}, + {0, cosTheta, {0., -sinTheta}, 0}, + {0, {0., -sinTheta}, cosTheta, 0}, + {{0., sinTheta}, 0, 0, cosTheta}}}; +} + +inline matrix4x4 rzzMatrix(const fp theta) { + const auto cosTheta = std::cos(theta / 2.); + const auto sinTheta = std::sin(theta / 2.); + + return matrix4x4{{qfp{cosTheta, -sinTheta}, C_ZERO, C_ZERO, C_ZERO}, + {C_ZERO, {cosTheta, sinTheta}, C_ZERO, C_ZERO}, + {C_ZERO, C_ZERO, {cosTheta, sinTheta}, C_ZERO}, + {C_ZERO, C_ZERO, C_ZERO, {cosTheta, -sinTheta}}}; +} + +inline matrix2x2 pMatrix(const fp lambda) { + return matrix2x2{{1, 0}, {0, {std::cos(lambda), std::sin(lambda)}}}; +} +inline constexpr auto SQRT2 = std::numbers::sqrt2_v; +inline constexpr auto FRAC1_SQRT2 = static_cast( + 0.707106781186547524400844362104849039284835937688474036588L); +const matrix2x2 IDENTITY_GATE = matrix2x2::Identity(); +const matrix2x2 H_GATE{{1.0 / SQRT2, 1.0 / SQRT2}, {1.0 / SQRT2, -1.0 / SQRT2}}; +const matrix2x2 IPZ{{IM, C_ZERO}, {C_ZERO, M_IM}}; +const matrix2x2 IPY{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; +const matrix2x2 IPX{{C_ZERO, IM}, {IM, C_ZERO}}; + +inline matrix2x2 getSingleQubitMatrix(const Gate& gate) { + if (gate.type == qc::SX) { + return matrix2x2{{qfp{0.5, 0.5}, qfp{0.5, -0.5}}, + {qfp{0.5, -0.5}, qfp{0.5, 0.5}}}; + } + if (gate.type == qc::RX) { + return rxMatrix(gate.parameter[0]); + } + if (gate.type == qc::RY) { + return ryMatrix(gate.parameter[0]); + } + if (gate.type == qc::RZ) { + return rzMatrix(gate.parameter[0]); + } + if (gate.type == qc::X) { + return matrix2x2{{0, 1}, {1, 0}}; + } + if (gate.type == qc::I) { + return IDENTITY_GATE; + } + if (gate.type == qc::P) { + return pMatrix(gate.parameter[0]); + } + if (gate.type == qc::H) { + return matrix2x2{{FRAC1_SQRT2, FRAC1_SQRT2}, {FRAC1_SQRT2, -FRAC1_SQRT2}}; + } + throw std::invalid_argument{ + "unsupported gate type for single qubit matrix (" + + qc::toString(gate.type) + ")"}; +} + +inline matrix4x4 getTwoQubitMatrix(const Gate& gate) { + using helpers::kroneckerProduct; + + if (gate.qubitId.empty()) { + return kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); + } + if (gate.qubitId.size() == 1) { + if (gate.qubitId[0] == 0) { + return kroneckerProduct(IDENTITY_GATE, getSingleQubitMatrix(gate)); + } + if (gate.qubitId[0] == 1) { + return kroneckerProduct(getSingleQubitMatrix(gate), IDENTITY_GATE); + } + throw std::logic_error{"Invalid qubit ID in getTwoQubitMatrix"}; + } + if (gate.qubitId.size() == 2) { + if (gate.type == qc::X) { + // controlled X (CX) + if (gate.qubitId == llvm::SmallVector{0, 1}) { + return matrix4x4{ + {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}}; + } + if (gate.qubitId == llvm::SmallVector{1, 0}) { + return matrix4x4{ + {1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}, {0, 1, 0, 0}}; + } + } + if (gate.type == qc::RXX) { + // TODO: check qubit order? + return rxxMatrix(gate.parameter[0]); + } + if (gate.type == qc::RYY) { + // TODO: check qubit order? + return ryyMatrix(gate.parameter[0]); + } + if (gate.type == qc::RZZ) { + // TODO: check qubit order? + return rzzMatrix(gate.parameter[0]); + } + if (gate.type == qc::I) { + return kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); + } + throw std::invalid_argument{"unsupported gate type for two qubit matrix "}; + } + throw std::logic_error{"Invalid number of qubit IDs in compute_unitary"}; +} + +} // namespace mqt::ir::opt::decomposition diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h new file mode 100644 index 0000000000..43b83c2c48 --- /dev/null +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h @@ -0,0 +1,786 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include "EulerBasis.h" +#include "EulerDecomposition.h" +#include "Helpers.h" +#include "UnitaryMatrices.h" +#include "ir/Definitions.hpp" +#include "ir/operations/OpType.hpp" + +#include // NOLINT(misc-include-cleaner) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // TODO: unstable, NOLINT(misc-include-cleaner) +#include + +namespace mqt::ir::opt::decomposition { +/** + * Allowed deviation for internal assert statements which ensure the correctness + * of the decompositions. + */ +constexpr fp SANITY_CHECK_PRECISION = 1e-12; + +/** + * Weyl decomposition of a 2-qubit unitary matrix (4x4). + * The result consists of four 2x2 1-qubit matrices (k1l, k2l, k1r, k2r) and + * three parameters for a canonical gate (a, b, c). The matrices can then be + * decomposed using a single-qubit decomposition into e.g. rotation gates and + * the canonical gate is RXX(-2 * a), RYY(-2 * b), RZZ (-2 * c). + */ +struct TwoQubitWeylDecomposition { + enum class Specialization : std::uint8_t { + General, // canonical gate has no special symmetry. + IdEquiv, // canonical gate is identity. + SWAPEquiv, // canonical gate is SWAP. + PartialSWAPEquiv, // canonical gate is partial SWAP. + PartialSWAPFlipEquiv, // canonical gate is flipped partial SWAP. + ControlledEquiv, // canonical gate is a controlled gate. + MirrorControlledEquiv, // canonical gate is swap + controlled gate. + + // These next 3 gates use the definition of fSim from eq (1) in: + // https://arxiv.org/pdf/2001.08343.pdf + FSimaabEquiv, + FSimabbEquiv, + FSimabmbEquiv, + }; + + // a, b, c are the parameters of the canonical gate (CAN) + fp a; // rotation of RXX gate in CAN + fp b; // rotation of RYY gate in CAN + fp c; // rotation of RZZ gate in CAN + fp globalPhase; // global phase adjustment + /** + * q1 - k2r - C - k1r - + * A + * q0 - k2l - N - k1l - + */ + matrix2x2 k1l; // "left" qubit after canonical gate + matrix2x2 k2l; // "left" qubit before canonical gate + matrix2x2 k1r; // "right" qubit after canonical gate + matrix2x2 k2r; // "right" qubit before canonical gate + Specialization specialization; // detected symmetries in the matrix + EulerBasis defaultEulerBasis; // recommended euler basis for k1l/k2l/k1r/k2r + std::optional requestedFidelity; // desired fidelity; + // if set to std::nullopt, no automatic + // specialization will be applied + fp calculatedFidelity; // actual fidelity of decomposition + matrix4x4 unitaryMatrix; // original matrix for this decomposition + + /** + * Create Weyl decomposition. + * + * @param unitaryMatrix Matrix of the two-qubit operation/series to be + * decomposed. + * @param fidelity Tolerance to assume a specialization which is used to + * reduce the number of parameters required by the canonical + * gate and thus potentially decreasing the number of basis + * gates. + * @param specialization Force the use this specialization. + */ + static TwoQubitWeylDecomposition create(const matrix4x4& unitaryMatrix, + std::optional fidelity) { + auto u = unitaryMatrix; + auto detU = u.determinant(); + auto detPow = std::pow(detU, static_cast(-0.25)); + u *= detPow; // remove global phase from unitary matrix + auto globalPhase = std::arg(detU) / 4.; + + // this should have normalized determinant of u, so that u ∈ SU(4) + assert(std::abs(u.determinant() - 1.0) < SANITY_CHECK_PRECISION); + + // transform unitary matrix to magic basis; this enables two properties: + // 1. if uP ∈ SO(4), V = A ⊗ B (SO(4) → SU(2) ⊗ SU(2)) + // 2. magic basis diagonalizes canonical gate, allowing calculation of + // canonical gate parameters later on + auto uP = magicBasisTransform(u, MagicBasisTransform::OutOf); + const matrix4x4 m2 = uP.transpose() * uP; + + // diagonalization yields eigenvectors (p) and eigenvalues (d); + // p is used to calculate K1/K2 (and thus the single-qubit gates + // surrounding the canonical gate); d is is used to determine the weyl + // coordinates and thus the parameters of the canonical gate + // TODO: it may be possible to lower the precision + auto [p, d] = diagonalizeComplexSymmetric(m2, 1e-13); + + // extract Weyl coordinates from eigenvalues, map to [0, 2*pi) + // NOLINTNEXTLINE(misc-include-cleaner) + Eigen::Vector cs; + rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; + dReal(3) = -dReal(0) - dReal(1) - dReal(2); + for (int i = 0; i < static_cast(cs.size()); ++i) { + assert(i < dReal.size()); + cs[i] = helpers::remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); + } + + // re-order coordinates and according to min(a, pi/2 - a) with + // a = x mod pi/2 for each weyl coordinate x + decltype(cs) cstemp; + llvm::transform(cs, cstemp.begin(), [](auto&& x) { + auto tmp = helpers::remEuclid(x, qc::PI_2); + return std::min(tmp, qc::PI_2 - tmp); + }); + std::array order{0, 1, 2}; + llvm::stable_sort(order, + [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); + std::tie(order[0], order[1], order[2]) = + std::tuple{order[1], order[2], order[0]}; + std::tie(cs[0], cs[1], cs[2]) = + std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; + std::tie(dReal(0), dReal(1), dReal(2)) = + std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2])}; + + // update eigenvectors (columns of p) according to new order of + // weyl coordinates + matrix4x4 pOrig = p; + for (int i = 0; std::cmp_less(i, order.size()); ++i) { + p.col(i) = pOrig.col(order[i]); + } + // apply correction for determinant if necessary + if (p.determinant().real() < 0.0) { + auto lastColumnIndex = p.cols() - 1; + p.col(lastColumnIndex) *= -1.0; + } + assert(std::abs(p.determinant() - 1.0) < SANITY_CHECK_PRECISION); + + // re-create complex eigenvalue matrix; this matrix contains the + // parameters of the canonical gate which is later used in the + // verification + matrix4x4 temp = dReal.asDiagonal(); + temp *= IM; + temp = temp.exp(); + + // combined matrix k1 of 1q gates after canonical gate + matrix4x4 k1 = uP * p * temp; + assert((k1.transpose() * k1).isIdentity()); // k1 must be orthogonal + assert(k1.determinant().real() > 0.0); + k1 = magicBasisTransform(k1, MagicBasisTransform::Into); + + // combined matrix k2 of 1q gates before canonical gate + matrix4x4 k2 = p.transpose().conjugate(); + assert((k2.transpose() * k2).isIdentity()); // k2 must be orthogonal + assert(k2.determinant().real() > 0.0); + k2 = magicBasisTransform(k2, MagicBasisTransform::Into); + + // ensure k1 and k2 are correct (when combined with the canonical gate + // parameters in-between, they are equivalent to u) + assert( + (k1 * magicBasisTransform(temp.conjugate(), MagicBasisTransform::Into) * + k2) + .isApprox(u, SANITY_CHECK_PRECISION)); + + // calculate k1 = K1l ⊗ K1r + auto [K1l, K1r, phase_l] = decomposeTwoQubitProductGate(k1); + // decompose k2 = K2l ⊗ K2r + auto [K2l, K2r, phase_r] = decomposeTwoQubitProductGate(k2); + assert(helpers::kroneckerProduct(K1l, K1r).isApprox( + k1, SANITY_CHECK_PRECISION)); + assert(helpers::kroneckerProduct(K2l, K2r).isApprox( + k2, SANITY_CHECK_PRECISION)); + // accumulate global phase + globalPhase += phase_l + phase_r; + + // Flip into Weyl chamber + if (cs[0] > qc::PI_2) { + cs[0] -= 3.0 * qc::PI_2; + K1l = K1l * IPY; + K1r = K1r * IPY; + globalPhase += qc::PI_2; + } + if (cs[1] > qc::PI_2) { + cs[1] -= 3.0 * qc::PI_2; + K1l = K1l * IPX; + K1r = K1r * IPX; + globalPhase += qc::PI_2; + } + auto conjs = 0; + if (cs[0] > qc::PI_4) { + cs[0] = qc::PI_2 - cs[0]; + K1l = K1l * IPY; + K2r = IPY * K2r; + conjs += 1; + globalPhase -= qc::PI_2; + } + if (cs[1] > qc::PI_4) { + cs[1] = qc::PI_2 - cs[1]; + K1l = K1l * IPX; + K2r = IPX * K2r; + conjs += 1; + globalPhase += qc::PI_2; + if (conjs == 1) { + globalPhase -= qc::PI; + } + } + if (cs[2] > qc::PI_2) { + cs[2] -= 3.0 * qc::PI_2; + K1l = K1l * IPZ; + K1r = K1r * IPZ; + globalPhase += qc::PI_2; + if (conjs == 1) { + globalPhase -= qc::PI; + } + } + if (conjs == 1) { + cs[2] = qc::PI_2 - cs[2]; + K1l = K1l * IPZ; + K2r = IPZ * K2r; + globalPhase += qc::PI_2; + } + if (cs[2] > qc::PI_4) { + cs[2] -= qc::PI_2; + K1l = K1l * IPZ; + K1r = K1r * IPZ; + globalPhase -= qc::PI_2; + } + + // bind weyl coordinates as parameters of canonical gate + auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); + // make sure decomposition is equal to input + assert((helpers::kroneckerProduct(K1l, K1r) * + getCanonicalMatrix(a * -2.0, b * -2.0, c * -2.0) * + helpers::kroneckerProduct(K2l, K2r) * std::exp(IM * globalPhase)) + .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); + + TwoQubitWeylDecomposition decomposition{ + .a = a, + .b = b, + .c = c, + .globalPhase = globalPhase, + .k1l = K1l, + .k2l = K2l, + .k1r = K1r, + .k2r = K2r, + .specialization = Specialization::General, + .defaultEulerBasis = EulerBasis::ZYZ, + .requestedFidelity = fidelity, + // will be calculated if a specialization is used, set to -1 for now + .calculatedFidelity = -1.0, + .unitaryMatrix = unitaryMatrix, + }; + + // determine actual specialization of canonical gate so that the 1q + // matrices can potentially be simplified + auto flippedFromOriginal = decomposition.applySpecialization(); + + auto getTrace = [&]() { + if (flippedFromOriginal) { + return TwoQubitWeylDecomposition::getTrace( + qc::PI_2 - a, b, -c, decomposition.a, decomposition.b, + decomposition.c); + } + return TwoQubitWeylDecomposition::getTrace( + a, b, c, decomposition.a, decomposition.b, decomposition.c); + }; + // use trace to calculate fidelity of applied specialization and + // adjust global phase + auto trace = getTrace(); + decomposition.calculatedFidelity = helpers::traceToFidelity(trace); + // final check if specialization is close enough to the original matrix to + // satisfy the requested fidelity; since no forced specialization is + // allowed, this should never fail + if (decomposition.requestedFidelity) { + if (decomposition.calculatedFidelity + 1.0e-13 < + *decomposition.requestedFidelity) { + throw std::runtime_error{ + "TwoQubitWeylDecomposition: Calculated fidelity of " + "specialization is worse than requested fidelity!"}; + } + } + decomposition.globalPhase += std::arg(trace); + + // final check if decomposition is still valid after specialization + assert((helpers::kroneckerProduct(decomposition.k1l, decomposition.k1r) * + getCanonicalMatrix(decomposition.a * -2.0, decomposition.b * -2.0, + decomposition.c * -2.0) * + helpers::kroneckerProduct(decomposition.k2l, decomposition.k2r) * + std::exp(IM * decomposition.globalPhase)) + .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); + + return decomposition; + } + + /** + * Calculate matrix of canonical gate based on its parameters a, b, c. + */ + static matrix4x4 getCanonicalMatrix(fp a, fp b, fp c) { + auto xx = getTwoQubitMatrix({ + .type = qc::RXX, + .parameter = {a}, + .qubitId = {0, 1}, + }); + auto yy = getTwoQubitMatrix({ + .type = qc::RYY, + .parameter = {b}, + .qubitId = {0, 1}, + }); + auto zz = getTwoQubitMatrix({ + .type = qc::RZZ, + .parameter = {c}, + .qubitId = {0, 1}, + }); + return zz * yy * xx; + } + +protected: + static constexpr fp SANITY_CHECK_PRECISION = 1e-12; + + // https://docs.rs/faer/latest/faer/mat/generic/struct.Mat.html#method.self_adjoint_eigen + template static auto selfAdjointEigenLower(T&& a) { + auto [U, S] = helpers::selfAdjointEvd(std::forward(a)); + + return std::make_pair(U, S); + } + + enum class MagicBasisTransform : std::uint8_t { + Into, + OutOf, + }; + + static matrix4x4 magicBasisTransform(const matrix4x4& unitary, + MagicBasisTransform direction) { + const matrix4x4 bNonNormalized{ + {C_ONE, IM, C_ZERO, C_ZERO}, + {C_ZERO, C_ZERO, IM, C_ONE}, + {C_ZERO, C_ZERO, IM, C_M_ONE}, + {C_ONE, M_IM, C_ZERO, C_ZERO}, + }; + + const matrix4x4 bNonNormalizedDagger{ + {qfp(0.5, 0.), C_ZERO, C_ZERO, qfp(0.5, 0.)}, + {qfp(0., -0.5), C_ZERO, C_ZERO, qfp(0., 0.5)}, + {C_ZERO, qfp(0., -0.5), qfp(0., -0.5), C_ZERO}, + {C_ZERO, qfp(0.5, 0.), qfp(-0.5, 0.), C_ZERO}, + }; + if (direction == MagicBasisTransform::OutOf) { + return bNonNormalizedDagger * unitary * bNonNormalized; + } + if (direction == MagicBasisTransform::Into) { + return bNonNormalized * unitary * bNonNormalizedDagger; + } + throw std::logic_error{"Unknown MagicBasisTransform direction!"}; + } + + static fp closestPartialSwap(fp a, fp b, fp c) { + auto m = (a + b + c) / 3.; + auto [am, bm, cm] = std::array{a - m, b - m, c - m}; + auto [ab, bc, ca] = std::array{a - b, b - c, c - a}; + return m + (am * bm * cm * (6. + ab * ab + bc * bc + ca * ca) / 18.); + } + + /** + * Diagonalize given complex symmetric matrix M into (P, d) using a + * randomized algorithm. + * This approach is used in both qiskit and quantumflow. + * + * P is the matrix of real or orthogonal eigenvectors of M with P ∈ SO(4) + * d is a vector containing sqrt(eigenvalues) of M with unit-magnitude + * elements (for each element, complex magnitude is 1.0). + * D is d as a diagonal matrix. + * + * M = P * D * P^T + * + * @return pair of (P, D.diagonal()) + */ + [[nodiscard]] static std::pair + diagonalizeComplexSymmetric(const matrix4x4& m, fp precision) { + // We can't use raw `eig` directly because it isn't guaranteed to give + // us real or orthogonal eigenvectors. Instead, since `M` is + // complex-symmetric, + // M = A + iB + // for real-symmetric `A` and `B`, and as + // M^+ @ M2 = A^2 + B^2 + i [A, B] = 1 + // we must have `A` and `B` commute, and consequently they are + // simultaneously diagonalizable. Mixing them together _should_ account + // for any degeneracy problems, but it's not guaranteed, so we repeat it + // a little bit. The fixed seed is to make failures deterministic; the + // value is not important. + auto state = std::mt19937{2023}; + std::normal_distribution dist; + + for (int i = 0; i < 100; ++i) { + fp randA{}; + fp randB{}; + // For debugging the algorithm use the same RNG values as the + // Qiskit implementation for the first random trial. + // In most cases this loop only executes a single iteration and + // using the same rng values rules out possible RNG differences + // as the root cause of a test failure + if (i == 0) { + randA = 1.2602066112249388; + randB = 0.22317849046722027; + } else { + randA = dist(state); + randB = dist(state); + } + const rmatrix4x4 m2Real = randA * m.real() + randB * m.imag(); + const rmatrix4x4 pReal = selfAdjointEigenLower(m2Real).first; + const matrix4x4 p = pReal; + const diagonal4x4 d = (p.transpose() * m * p).diagonal(); + + const matrix4x4 diagD = d.asDiagonal(); + + const matrix4x4 compare = p * diagD * p.transpose(); + if (compare.isApprox(m, precision)) { + // p are the eigenvectors which are decomposed into the + // single-qubit gates surrounding the canonical gate + // d is the sqrt of the eigenvalues that are used to determine the + // weyl coordinates and thus the parameters of the canonical gate + // check that p is in SO(4) + assert((p.transpose() * p).isIdentity(SANITY_CHECK_PRECISION)); + // make sure determinant of eigenvalues is 1.0 + assert(std::abs(matrix4x4{d.asDiagonal()}.determinant() - 1.0) < + SANITY_CHECK_PRECISION); + return std::make_pair(p, d); + } + } + throw std::runtime_error{ + "TwoQubitWeylDecomposition: failed to diagonalize M2."}; + } + + /** + * Decompose a special unitary matrix C that is the combination of two + * single-qubit gates A and B into its single-qubit matrices. + * + * C = A ⊗ B + * + * @param specialUnitary Special unitary matrix C + * + * @return single-qubit matrices A and B and the required + * global phase adjustment + */ + static std::tuple + decomposeTwoQubitProductGate(matrix4x4 specialUnitary) { + // for alternative approaches, see + // pennylane's math.decomposition.su2su2_to_tensor_products + // or quantumflow.kronecker_decomposition + + // first quadrant + matrix2x2 r{{specialUnitary(0, 0), specialUnitary(0, 1)}, + {specialUnitary(1, 0), specialUnitary(1, 1)}}; + auto detR = r.determinant(); + if (std::abs(detR) < 0.1) { + // third quadrant + r = matrix2x2{{specialUnitary(2, 0), specialUnitary(2, 1)}, + {specialUnitary(3, 0), specialUnitary(3, 1)}}; + detR = r.determinant(); + } + if (std::abs(detR) < 0.1) { + throw std::runtime_error{ + "decompose_two_qubit_product_gate: unable to decompose: det_r < 0.1"}; + } + r /= std::sqrt(detR); + // transpose with complex conjugate of each element + const matrix2x2 rTConj = r.transpose().conjugate(); + + auto temp = helpers::kroneckerProduct(IDENTITY_GATE, rTConj); + temp = specialUnitary * temp; + + // [[a, b, c, d], + // [e, f, g, h], => [[a, c], + // [i, j, k, l], [i, k]] + // [m, n, o, p]] + matrix2x2 l{{temp(0, 0), temp(0, 2)}, {temp(2, 0), temp(2, 2)}}; + auto detL = l.determinant(); + if (std::abs(detL) < 0.9) { + throw std::runtime_error{ + "decompose_two_qubit_product_gate: unable to decompose: detL < 0.9"}; + } + l /= std::sqrt(detL); + auto phase = std::arg(detL) / 2.; + + return {l, r, phase}; + } + + /** + * Calculate trace of two sets of parameters for the canonical gate. + * The trace has been defined in: https://arxiv.org/abs/1811.12926 + */ + [[nodiscard]] static qfp getTrace(fp a, fp b, fp c, fp ap, fp bp, fp cp) { + auto da = a - ap; + auto db = b - bp; + auto dc = c - cp; + return static_cast(4.) * + qfp(std::cos(da) * std::cos(db) * std::cos(dc), + std::sin(da) * std::sin(db) * std::sin(dc)); + } + + /** + * Choose the best specialization for the for the canonical gate. + * This will use the requestedFidelity to determine if a specialization is + * close enough to the actual canonical gate matrix. + */ + [[nodiscard]] Specialization bestSpecialization() const { + auto isClose = [this](fp ap, fp bp, fp cp) -> bool { + auto tr = getTrace(a, b, c, ap, bp, cp); + if (requestedFidelity) { + return helpers::traceToFidelity(tr) >= *requestedFidelity; + } + return false; + }; + + auto closestAbc = closestPartialSwap(a, b, c); + auto closestAbMinusC = closestPartialSwap(a, b, -c); + + if (isClose(0., 0., 0.)) { + return Specialization::IdEquiv; + } + if (isClose(qc::PI_4, qc::PI_4, qc::PI_4) || + isClose(qc::PI_4, qc::PI_4, -qc::PI_4)) { + return Specialization::SWAPEquiv; + } + if (isClose(closestAbc, closestAbc, closestAbc)) { + return Specialization::PartialSWAPEquiv; + } + if (isClose(closestAbMinusC, closestAbMinusC, -closestAbMinusC)) { + return Specialization::PartialSWAPFlipEquiv; + } + if (isClose(a, 0., 0.)) { + return Specialization::ControlledEquiv; + } + if (isClose(qc::PI_4, qc::PI_4, c)) { + return Specialization::MirrorControlledEquiv; + } + if (isClose((a + b) / 2., (a + b) / 2., c)) { + return Specialization::FSimaabEquiv; + } + if (isClose(a, (b + c) / 2., (b + c) / 2.)) { + return Specialization::FSimabbEquiv; + } + if (isClose(a, (b - c) / 2., (c - b) / 2.)) { + return Specialization::FSimabmbEquiv; + } + return Specialization::General; + } + + /** + * @return true if the specialization flipped the original decomposition + */ + bool applySpecialization() { + if (specialization != Specialization::General) { + throw std::logic_error{"Application of specialization only works on " + "general decomposition!"}; + } + bool flippedFromOriginal = false; + auto newSpecialization = bestSpecialization(); + if (newSpecialization == Specialization::General) { + // U has no special symmetry. + // + // This gate binds all 6 possible parameters, so there is no need to + // make the single-qubit pre-/post-gates canonical. + return flippedFromOriginal; + } + specialization = newSpecialization; + + if (newSpecialization == Specialization::IdEquiv) { + // :math:`U \sim U_d(0,0,0)` + // Thus, :math:`\sim Id` + // + // This gate binds 0 parameters, we make it canonical by setting: + // + // :math:`K2_l = Id` , :math:`K2_r = Id`. + a = 0.; + b = 0.; + c = 0.; + // unmodified global phase + k1l = k1l * k2l; + k2l = IDENTITY_GATE; + k1r = k1r * k2r; + k2r = IDENTITY_GATE; + } else if (newSpecialization == Specialization::SWAPEquiv) { + // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, -\pi/4)` + // Thus, :math:`U \sim \text{SWAP}` + // + // This gate binds 0 parameters, we make it canonical by setting: + // + // :math:`K2_l = Id` , :math:`K2_r = Id`. + a = qc::PI_4; + b = qc::PI_4; + c = qc::PI_4; + if (c > 0.) { + // unmodified global phase + k1l = k1l * k2r; + k1r = k1r * k2l; + k2l = IDENTITY_GATE; + k2r = IDENTITY_GATE; + } else { + flippedFromOriginal = true; + + globalPhase += qc::PI_2; + k1l = k1l * IPZ * k2r; + k1r = k1r * IPZ * k2l; + k2l = IDENTITY_GATE; + k2r = IDENTITY_GATE; + } + } else if (newSpecialization == Specialization::PartialSWAPEquiv) { + // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4)` + // Thus, :math:`U \sim \text{SWAP}^\alpha` + // + // This gate binds 3 parameters, we make it canonical by setting: + // + // :math:`K2_l = Id`. + auto closest = closestPartialSwap(a, b, c); + auto k2lDagger = k2l.transpose().conjugate(); + + a = closest; + b = closest; + c = closest; + // unmodified global phase + k1l = k1l * k2l; + k1r = k1r * k2l; + k2r = k2lDagger * k2r; + k2l = IDENTITY_GATE; + } else if (newSpecialization == Specialization::PartialSWAPFlipEquiv) { + // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4)` + // Thus, :math:`U \sim \text{SWAP}^\alpha` + // + // (a non-equivalent root of SWAP from the TwoQubitWeylPartialSWAPEquiv + // similar to how :math:`x = (\pm \sqrt(x))^2`) + // + // This gate binds 3 parameters, we make it canonical by setting: + // + // :math:`K2_l = Id` + auto closest = closestPartialSwap(a, b, -c); + auto k2lDagger = k2l.transpose().conjugate(); + + a = closest; + b = closest; + c = -closest; + // unmodified global phase + k1l = k1l * k2l; + k1r = k1r * IPZ * k2l * IPZ; + k2r = IPZ * k2lDagger * IPZ * k2r; + k2l = IDENTITY_GATE; + } else if (newSpecialization == Specialization::ControlledEquiv) { + // :math:`U \sim U_d(\alpha, 0, 0)` + // Thus, :math:`U \sim \text{Ctrl-U}` + // + // This gate binds 4 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l) Rx(\lambda_l)` + // :math:`K2_r = Ry(\theta_r) Rx(\lambda_r)` + auto eulerBasis = EulerBasis::XYX; + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + EulerDecomposition::anglesFromUnitary(k2l, eulerBasis); + auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = + EulerDecomposition::anglesFromUnitary(k2r, eulerBasis); + + // unmodified parameter a + b = 0.; + c = 0.; + globalPhase = globalPhase + k2lphase + k2rphase; + k1l = k1l * rxMatrix(k2lphi); + k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda); + k1r = k1r * rxMatrix(k2rphi); + k2r = ryMatrix(k2rtheta) * rxMatrix(k2rlambda); + defaultEulerBasis = eulerBasis; + } else if (newSpecialization == Specialization::MirrorControlledEquiv) { + // :math:`U \sim U_d(\pi/4, \pi/4, \alpha)` + // Thus, :math:`U \sim \text{SWAP} \cdot \text{Ctrl-U}` + // + // This gate binds 4 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` + // :math:`K2_r = Ry(\theta_r)\cdot Rz(\lambda_r)` + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + EulerDecomposition::anglesFromUnitary(k2l, EulerBasis::ZYZ); + auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = + EulerDecomposition::anglesFromUnitary(k2r, EulerBasis::ZYZ); + + a = qc::PI_4; + b = qc::PI_4; + // unmodified parameter c + globalPhase = globalPhase + k2lphase + k2rphase; + k1l = k1l * rzMatrix(k2rphi); + k2l = ryMatrix(k2ltheta) * rzMatrix(k2llambda); + k1r = k1r * rzMatrix(k2lphi); + k2r = ryMatrix(k2rtheta) * rzMatrix(k2rlambda); + } else if (newSpecialization == Specialization::FSimaabEquiv) { + // :math:`U \sim U_d(\alpha, \alpha, \beta), \alpha \geq |\beta|` + // + // This gate binds 5 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)`. + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + EulerDecomposition::anglesFromUnitary(k2l, EulerBasis::ZYZ); + auto ab = (a + b) / 2.; + + a = ab; + b = ab; + // unmodified parameter c + globalPhase = globalPhase + k2lphase; + k1l = k1l * rzMatrix(k2lphi); + k2l = ryMatrix(k2ltheta) * rzMatrix(k2llambda); + k1r = k1r * rzMatrix(k2lphi); + k2r = rzMatrix(-k2lphi) * k2r; + } else if (newSpecialization == Specialization::FSimabbEquiv) { + // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` + // + // This gate binds 5 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` + auto eulerBasis = EulerBasis::XYX; + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + EulerDecomposition::anglesFromUnitary(k2l, eulerBasis); + auto bc = (b + c) / 2.; + + // unmodified parameter a + b = bc; + c = bc; + globalPhase = globalPhase + k2lphase; + k1l = k1l * rxMatrix(k2lphi); + k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda); + k1r = k1r * rxMatrix(k2lphi); + k2r = rxMatrix(-k2lphi) * k2r; + defaultEulerBasis = eulerBasis; + } else if (newSpecialization == Specialization::FSimabmbEquiv) { + // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` + // + // This gate binds 5 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` + auto eulerBasis = EulerBasis::XYX; + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + EulerDecomposition::anglesFromUnitary(k2l, eulerBasis); + auto bc = (b - c) / 2.; + + // unmodified parameter a + b = bc; + c = -bc; + globalPhase = globalPhase + k2lphase; + k1l = k1l * rxMatrix(k2lphi); + k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda); + k1r = k1r * IPZ * rxMatrix(k2lphi) * IPZ; + k2r = IPZ * rxMatrix(-k2lphi) * IPZ * k2r; + defaultEulerBasis = eulerBasis; + } else { + throw std::logic_error{"Unknown specialization"}; + } + return flippedFromOriginal; + } +}; +} // namespace mqt::ir::opt::decomposition diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 6c337438c2..32aa3c5a7c 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -8,22 +8,24 @@ * Licensed under the MIT License */ -#include "Helpers.h" -#include "ir/Definitions.hpp" #include "ir/operations/OpType.hpp" #include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerBasis.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Gate.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h" #include "mlir/Dialect/MQTOpt/Transforms/Passes.h" #include // NOLINT(misc-include-cleaner) -#include #include #include #include #include #include -#include #include -#include #include #include #include @@ -33,10 +35,7 @@ #include #include #include -#include #include -#include -#include #include // TODO: unstable, NOLINT(misc-include-cleaner) #include @@ -49,109 +48,23 @@ namespace mqt::ir::opt { */ struct GateDecompositionPattern final : mlir::OpInterfaceRewritePattern { - enum class EulerBasis : std::uint8_t { - U3 = 0, - U321 = 1, - U = 2, - PSX = 3, - U1X = 4, - RR = 5, - ZYZ = 6, - ZXZ = 7, - XZX = 8, - XYX = 9, - ZSXX = 10, - ZSX = 11, - }; - - using QubitId = std::size_t; - /** - * Gate sequence of single-qubit and/or two-qubit gates. - */ - struct QubitGateSequence { - /** - * Gate description which should be able to represent every possible - * one-qubit or two-qubit operation. - */ - struct Gate { - qc::OpType type{qc::I}; - llvm::SmallVector parameter; - llvm::SmallVector qubitId = {0}; - }; - /** - * Container sorting the gate sequence in order. - */ - llvm::SmallVector gates; - - /** - * Global phase adjustment required for the sequence. - */ - fp globalPhase{}; - /** - * @return true if the global phase adjustment is not zero. - */ - [[nodiscard]] bool hasGlobalPhase() const { - return std::abs(globalPhase) > DEFAULT_ATOL; - } - - /** - * Calculate complexity of sequence according to getComplexity(). - */ - [[nodiscard]] std::size_t complexity() const { - // TODO: add more sophisticated metric to determine complexity of - // series/sequence - // TODO: caching mechanism? - std::size_t c{}; - for (auto&& gate : gates) { - c += getComplexity(gate.type, gate.qubitId.size()); - } - if (hasGlobalPhase()) { - // need to add a global phase gate if a global phase needs to be applied - c += getComplexity(qc::GPhase, 0); - } - return c; - } - - /** - * Calculate overall unitary matrix of the sequence. - */ - [[nodiscard]] matrix4x4 getUnitaryMatrix() const { - matrix4x4 unitaryMatrix = - helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); - for (auto&& gate : gates) { - auto gateMatrix = getTwoQubitMatrix(gate); - unitaryMatrix = gateMatrix * unitaryMatrix; - } - unitaryMatrix *= std::exp(IM * globalPhase); - assert(helpers::isUnitaryMatrix(unitaryMatrix)); - return unitaryMatrix; - } - }; - /** - * Helper type to show that a gate sequence is supposed to only contain - * single-qubit gates. - */ - using OneQubitGateSequence = QubitGateSequence; - /** - * Helper type to show that the gate sequence may contain two-qubit gates. - */ - using TwoQubitGateSequence = QubitGateSequence; + using EulerBasis = decomposition::EulerBasis; + using Gate = decomposition::Gate; /** * Initialize pattern with a set of basis gates and euler bases. * The best combination of (basis gate, euler basis) will be evaluated for * each decomposition. */ - explicit GateDecompositionPattern( - mlir::MLIRContext* context, - llvm::SmallVector basisGate, - llvm::SmallVector eulerBasis) + explicit GateDecompositionPattern(mlir::MLIRContext* context, + llvm::SmallVector basisGate, + llvm::SmallVector eulerBasis) : OpInterfaceRewritePattern(context), decomposerBasisGate{std::move(basisGate)}, decomposerEulerBases{std::move(eulerBasis)} { for (auto&& basisGate : decomposerBasisGate) { - basisDecomposers.push_back( - TwoQubitBasisDecomposer::create(basisGate, DEFAULT_FIDELITY)); + basisDecomposers.push_back(decomposition::TwoQubitBasisDecomposer::create( + basisGate, DEFAULT_FIDELITY)); } } @@ -172,9 +85,10 @@ struct GateDecompositionPattern final const matrix4x4 unitaryMatrix = series.getUnitaryMatrix(); const auto targetDecomposition = - TwoQubitWeylDecomposition::create(unitaryMatrix, DEFAULT_FIDELITY); + decomposition::TwoQubitWeylDecomposition::create(unitaryMatrix, + DEFAULT_FIDELITY); - std::optional bestSequence; + std::optional bestSequence; for (const auto& decomposer : basisDecomposers) { auto sequence = decomposer.twoQubitDecompose( targetDecomposition, decomposerEulerBases, DEFAULT_FIDELITY, false, @@ -201,19 +115,15 @@ struct GateDecompositionPattern final } protected: - static constexpr fp SANITY_CHECK_PRECISION = 1e-12; - [[nodiscard]] static std::size_t getComplexity(qc::OpType type, - std::size_t numOfQubits) { - if (numOfQubits > 1) { - constexpr std::size_t multiQubitFactor = 10; - return (numOfQubits - 1) * multiQubitFactor; - } - if (type == qc::GPhase) { - return 2; - } - return 1; - } + /** + * Factor by which two matrices are considered to be the same when simplifying + * during a decomposition. + */ + static constexpr auto DEFAULT_FIDELITY = 1.0 - 1e-15; + static constexpr auto SANITY_CHECK_PRECISION = + decomposition::SANITY_CHECK_PRECISION; + using QubitId = decomposition::QubitId; struct TwoQubitSeries { /** * Complexity of series using getComplexity() for each gate. @@ -235,11 +145,11 @@ struct GateDecompositionPattern final std::array outQubits; fp globalPhase{}; - struct Gate { + struct MlirGate { UnitaryInterface op; llvm::SmallVector qubitIds; }; - llvm::SmallVector gates; + llvm::SmallVector gates; [[nodiscard]] static TwoQubitSeries getTwoQubitSeries(UnitaryInterface op) { TwoQubitSeries result(op); @@ -281,13 +191,13 @@ struct GateDecompositionPattern final } [[nodiscard]] matrix4x4 getUnitaryMatrix() const { - matrix4x4 unitaryMatrix = - helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); + matrix4x4 unitaryMatrix = helpers::kroneckerProduct( + decomposition::IDENTITY_GATE, decomposition::IDENTITY_GATE); for (auto&& gate : gates) { - auto gateMatrix = - getTwoQubitMatrix({.type = helpers::getQcType(gate.op), - .parameter = helpers::getParameters(gate.op), - .qubitId = gate.qubitIds}); + auto gateMatrix = decomposition::getTwoQubitMatrix( + {.type = helpers::getQcType(gate.op), + .parameter = helpers::getParameters(gate.op), + .qubitId = gate.qubitIds}); unitaryMatrix = gateMatrix * unitaryMatrix; } unitaryMatrix *= std::exp(IM * globalPhase); @@ -314,8 +224,8 @@ struct GateDecompositionPattern final outQubits = {out[0], out[1]}; gates.push_back({.op = initialOperation, .qubitIds = {0, 1}}); } - complexity += - getComplexity(helpers::getQcType(initialOperation), in.size()); + complexity += helpers::getComplexity(helpers::getQcType(initialOperation), + in.size()); } /** @@ -333,7 +243,7 @@ struct GateDecompositionPattern final *it = nextGate->getResult(0); gates.push_back({.op = nextGate, .qubitIds = {qubitId}}); - complexity += getComplexity(helpers::getQcType(nextGate), 1); + complexity += helpers::getComplexity(helpers::getQcType(nextGate), 1); return true; } @@ -383,7 +293,7 @@ struct GateDecompositionPattern final gates.push_back( {.op = nextGate, .qubitIds = {firstQubitId, secondQubitId}}); - complexity += getComplexity(helpers::getQcType(nextGate), 2); + complexity += helpers::getComplexity(helpers::getQcType(nextGate), 2); return true; } @@ -454,7 +364,7 @@ struct GateDecompositionPattern final static void applySeries(mlir::PatternRewriter& rewriter, TwoQubitSeries& series, - const TwoQubitGateSequence& sequence) { + const decomposition::TwoQubitGateSequence& sequence) { auto& lastSeriesOp = series.gates.back().op; auto location = lastSeriesOp->getLoc(); rewriter.setInsertionPointAfter(lastSeriesOp); @@ -480,10 +390,10 @@ struct GateDecompositionPattern final {}); } - matrix4x4 unitaryMatrix = - helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); + matrix4x4 unitaryMatrix = helpers::kroneckerProduct( + decomposition::IDENTITY_GATE, decomposition::IDENTITY_GATE); for (auto&& gate : sequence.gates) { - auto gateMatrix = getTwoQubitMatrix(gate); + auto gateMatrix = decomposition::getTwoQubitMatrix(gate); unitaryMatrix = gateMatrix * unitaryMatrix; // TODO: need to add each basis gate we want to use @@ -534,1585 +444,18 @@ struct GateDecompositionPattern final } } - enum class Specialization : std::uint8_t { - General, // canonical gate has no special symmetry. - IdEquiv, // canonical gate is identity. - SWAPEquiv, // canonical gate is SWAP. - PartialSWAPEquiv, // canonical gate is partial SWAP. - PartialSWAPFlipEquiv, // canonical gate is flipped partial SWAP. - ControlledEquiv, // canonical gate is a controlled gate. - MirrorControlledEquiv, // canonical gate is swap + controlled gate. - - // These next 3 gates use the definition of fSim from eq (1) in: - // https://arxiv.org/pdf/2001.08343.pdf - FSimaabEquiv, - FSimabbEquiv, - FSimabmbEquiv, - }; - - enum class MagicBasisTransform : std::uint8_t { - Into, - OutOf, - }; - - static constexpr auto SQRT2 = std::numbers::sqrt2_v; - static constexpr auto FRAC1_SQRT2 = - static_cast(0.707106781186547524400844362104849039); - static const matrix2x2 IDENTITY_GATE; - static const matrix2x2 H_GATE; - - static fp remEuclid(fp a, fp b) { - auto r = std::fmod(a, b); - return (r < 0.0) ? r + std::abs(b) : r; - } - - // Wrap angle into interval [-π,π). If within atol of the endpoint, clamp - // to -π - static fp mod2pi(fp angle, fp angleZeroEpsilon = 1e-13) { - // remEuclid() isn't exactly the same as Python's % operator, but - // because the RHS here is a constant and positive it is effectively - // equivalent for this case - auto wrapped = remEuclid(angle + qc::PI, 2. * qc::PI) - qc::PI; - if (std::abs(wrapped - qc::PI) < angleZeroEpsilon) { - return -qc::PI; - } - return wrapped; - } - - // https://docs.rs/faer/latest/faer/mat/generic/struct.Mat.html#method.self_adjoint_eigen - template static auto selfAdjointEigenLower(T&& a) { - // rdiagonal4x4 S; - // auto U = self_adjoint_evd(A, S); - - // rmatrix4x4 U; - // jacobi_eigen_decomposition(A, U, S); - - auto [U, S] = helpers::selfAdjointEvd(std::forward(a)); - - return std::make_pair(U, S); - } - - static std::tuple - decomposeTwoQubitProductGate(matrix4x4 specialUnitary) { - // see pennylane math.decomposition.su2su2_to_tensor_products - // or "7.1 Kronecker decomposition" in on_gates.pdf - // or quantumflow.kronecker_decomposition - - // first quadrant - matrix2x2 r{{specialUnitary(0, 0), specialUnitary(0, 1)}, - {specialUnitary(1, 0), specialUnitary(1, 1)}}; - auto detR = r.determinant(); - if (std::abs(detR) < 0.1) { - // third quadrant - r = matrix2x2{{specialUnitary(2, 0), specialUnitary(2, 1)}, - {specialUnitary(3, 0), specialUnitary(3, 1)}}; - detR = r.determinant(); - } - if (std::abs(detR) < 0.1) { - throw std::runtime_error{ - "decompose_two_qubit_product_gate: unable to decompose: det_r < 0.1"}; - } - r /= std::sqrt(detR); - // transpose with complex conjugate of each element - const matrix2x2 rTConj = r.transpose().conjugate(); - - auto temp = helpers::kroneckerProduct(IDENTITY_GATE, rTConj); - temp = specialUnitary * temp; - - // [[a, b, c, d], - // [e, f, g, h], => [[a, c], - // [i, j, k, l], [i, k]] - // [m, n, o, p]] - matrix2x2 l{{temp(0, 0), temp(0, 2)}, {temp(2, 0), temp(2, 2)}}; - auto detL = l.determinant(); - if (std::abs(detL) < 0.9) { - throw std::runtime_error{ - "decompose_two_qubit_product_gate: unable to decompose: detL < 0.9"}; - } - l /= std::sqrt(detL); - auto phase = std::arg(detL) / 2.; - - return {l, r, phase}; - } - - static matrix4x4 magicBasisTransform(const matrix4x4& unitary, - MagicBasisTransform direction) { - const matrix4x4 bNonNormalized{ - {C_ONE, IM, C_ZERO, C_ZERO}, - {C_ZERO, C_ZERO, IM, C_ONE}, - {C_ZERO, C_ZERO, IM, C_M_ONE}, - {C_ONE, M_IM, C_ZERO, C_ZERO}, - }; - - const matrix4x4 bNonNormalizedDagger{ - {qfp(0.5, 0.), C_ZERO, C_ZERO, qfp(0.5, 0.)}, - {qfp(0., -0.5), C_ZERO, C_ZERO, qfp(0., 0.5)}, - {C_ZERO, qfp(0., -0.5), qfp(0., -0.5), C_ZERO}, - {C_ZERO, qfp(0.5, 0.), qfp(-0.5, 0.), C_ZERO}, - }; - if (direction == MagicBasisTransform::OutOf) { - return bNonNormalizedDagger * unitary * bNonNormalized; - } - if (direction == MagicBasisTransform::Into) { - return bNonNormalized * unitary * bNonNormalizedDagger; - } - throw std::logic_error{"Unknown MagicBasisTransform direction!"}; - } - - static fp traceToFidelity(const qfp& x) { - auto xAbs = std::abs(x); - return (4.0 + xAbs * xAbs) / 20.0; - } - - static fp closestPartialSwap(fp a, fp b, fp c) { - auto m = (a + b + c) / 3.; - auto [am, bm, cm] = std::array{a - m, b - m, c - m}; - auto [ab, bc, ca] = std::array{a - b, b - c, c - a}; - return m + (am * bm * cm * (6. + ab * ab + bc * bc + ca * ca) / 18.); - } - - static matrix2x2 rxMatrix(fp theta) { - auto halfTheta = theta / 2.; - auto cos = qfp(std::cos(halfTheta), 0.); - auto isin = qfp(0., -std::sin(halfTheta)); - return matrix2x2{{cos, isin}, {isin, cos}}; - } - - static matrix2x2 ryMatrix(fp theta) { - auto halfTheta = theta / 2.; - auto cos = qfp(std::cos(halfTheta), 0.); - auto sin = qfp(std::sin(halfTheta), 0.); - return matrix2x2{{cos, -sin}, {sin, cos}}; - } - - static matrix2x2 rzMatrix(fp theta) { - return matrix2x2{{qfp{std::cos(theta / 2.), -std::sin(theta / 2.)}, 0}, - {0, qfp{std::cos(theta / 2.), std::sin(theta / 2.)}}}; - } - - static matrix4x4 rxxMatrix(const fp theta) { - const auto cosTheta = std::cos(theta / 2.); - const auto sinTheta = std::sin(theta / 2.); - - return matrix4x4{{cosTheta, C_ZERO, C_ZERO, {0., -sinTheta}}, - {C_ZERO, cosTheta, {0., -sinTheta}, C_ZERO}, - {C_ZERO, {0., -sinTheta}, cosTheta, C_ZERO}, - {{0., -sinTheta}, C_ZERO, C_ZERO, cosTheta}}; - } - - static matrix4x4 ryyMatrix(const fp theta) { - const auto cosTheta = std::cos(theta / 2.); - const auto sinTheta = std::sin(theta / 2.); - - return matrix4x4{{{cosTheta, 0, 0, {0., sinTheta}}, - {0, cosTheta, {0., -sinTheta}, 0}, - {0, {0., -sinTheta}, cosTheta, 0}, - {{0., sinTheta}, 0, 0, cosTheta}}}; - } - - static matrix4x4 rzzMatrix(const fp theta) { - const auto cosTheta = std::cos(theta / 2.); - const auto sinTheta = std::sin(theta / 2.); - - return matrix4x4{{qfp{cosTheta, -sinTheta}, C_ZERO, C_ZERO, C_ZERO}, - {C_ZERO, {cosTheta, sinTheta}, C_ZERO, C_ZERO}, - {C_ZERO, C_ZERO, {cosTheta, sinTheta}, C_ZERO}, - {C_ZERO, C_ZERO, C_ZERO, {cosTheta, -sinTheta}}}; - } - - static matrix2x2 pMatrix(const fp lambda) { - return matrix2x2{{1, 0}, {0, {std::cos(lambda), std::sin(lambda)}}}; - } - - static std::array anglesFromUnitary(const matrix2x2& matrix, - EulerBasis basis) { - if (basis == EulerBasis::XYX) { - return paramsXyxInner(matrix); - } - if (basis == EulerBasis::ZYZ) { - return paramsZyzInner(matrix); - } - if (basis == EulerBasis::ZXZ) { - return paramsZxzInner(matrix); - } - throw std::invalid_argument{"Unknown EulerBasis for angles_from_unitary"}; - } - - static std::array paramsZyzInner(const matrix2x2& matrix) { - const auto detArg = std::arg(matrix.determinant()); - const auto phase = 0.5 * detArg; - const auto theta = - 2. * std::atan2(std::abs(matrix(1, 0)), std::abs(matrix(0, 0))); - const auto ang1 = std::arg(matrix(1, 1)); - const auto ang2 = std::arg(matrix(1, 0)); - const auto phi = ang1 + ang2 - detArg; - const auto lam = ang1 - ang2; - return {theta, phi, lam, phase}; - } - - static std::array paramsZxzInner(const matrix2x2& matrix) { - const auto [theta, phi, lam, phase] = paramsZyzInner(matrix); - return {theta, phi + (qc::PI / 2.), lam - (qc::PI / 2.), phase}; - } - - static std::array paramsXyxInner(const matrix2x2& matrix) { - const matrix2x2 matZyz{ - {static_cast(0.5) * - (matrix(0, 0) + matrix(0, 1) + matrix(1, 0) + matrix(1, 1)), - static_cast(0.5) * - (matrix(0, 0) - matrix(0, 1) + matrix(1, 0) - matrix(1, 1))}, - {static_cast(0.5) * - (matrix(0, 0) + matrix(0, 1) - matrix(1, 0) - matrix(1, 1)), - static_cast(0.5) * - (matrix(0, 0) - matrix(0, 1) - matrix(1, 0) + matrix(1, 1))}, - }; - auto [theta, phi, lam, phase] = paramsZyzInner(matZyz); - auto newPhi = mod2pi(phi + qc::PI, 0.); - auto newLam = mod2pi(lam + qc::PI, 0.); - return { - theta, - newPhi, - newLam, - phase + ((newPhi + newLam - phi - lam) / 2.), - }; - } - - static const matrix2x2 IPZ; - static const matrix2x2 IPY; - static const matrix2x2 IPX; - - static matrix2x2 getSingleQubitMatrix(const QubitGateSequence::Gate& gate) { - if (gate.type == qc::SX) { - return matrix2x2{{qfp{0.5, 0.5}, qfp{0.5, -0.5}}, - {qfp{0.5, -0.5}, qfp{0.5, 0.5}}}; - } - if (gate.type == qc::RX) { - return rxMatrix(gate.parameter[0]); - } - if (gate.type == qc::RY) { - return ryMatrix(gate.parameter[0]); - } - if (gate.type == qc::RZ) { - return rzMatrix(gate.parameter[0]); - } - if (gate.type == qc::X) { - return matrix2x2{{0, 1}, {1, 0}}; - } - if (gate.type == qc::I) { - return IDENTITY_GATE; - } - if (gate.type == qc::P) { - return pMatrix(gate.parameter[0]); - } - if (gate.type == qc::H) { - static constexpr fp SQRT2_2 = static_cast( - 0.707106781186547524400844362104849039284835937688474036588L); - return matrix2x2{{SQRT2_2, SQRT2_2}, {SQRT2_2, -SQRT2_2}}; - } - throw std::invalid_argument{ - "unsupported gate type for single qubit matrix (" + - qc::toString(gate.type) + ")"}; - } - - static matrix4x4 getTwoQubitMatrix(const QubitGateSequence::Gate& gate) { - using helpers::kroneckerProduct; - - if (gate.qubitId.empty()) { - return kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); - } - if (gate.qubitId.size() == 1) { - if (gate.qubitId[0] == 0) { - return kroneckerProduct(IDENTITY_GATE, getSingleQubitMatrix(gate)); - } - if (gate.qubitId[0] == 1) { - return kroneckerProduct(getSingleQubitMatrix(gate), IDENTITY_GATE); - } - throw std::logic_error{"Invalid qubit ID in getTwoQubitMatrix"}; - } - if (gate.qubitId.size() == 2) { - if (gate.type == qc::X) { - // controlled X (CX) - if (gate.qubitId == llvm::SmallVector{0, 1}) { - return matrix4x4{ - {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}}; - } - if (gate.qubitId == llvm::SmallVector{1, 0}) { - return matrix4x4{ - {1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}, {0, 1, 0, 0}}; - } - } - if (gate.type == qc::RXX) { - // TODO: check qubit order? - return rxxMatrix(gate.parameter[0]); - } - if (gate.type == qc::RYY) { - // TODO: check qubit order? - return ryyMatrix(gate.parameter[0]); - } - if (gate.type == qc::RZZ) { - // TODO: check qubit order? - return rzzMatrix(gate.parameter[0]); - } - if (gate.type == qc::I) { - return kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); - } - throw std::invalid_argument{ - "unsupported gate type for two qubit matrix "}; - } - throw std::logic_error{"Invalid number of qubit IDs in compute_unitary"}; - } - - /** - * Weyl decomposition of a 2-qubit unitary matrix (4x4). - * The result consists of four 2x2 1-qubit matrices (k1l, k2l, k1r, k2r) and - * three parameters for a canonical gate (a, b, c). The matrices can then be - * decomposed using a single-qubit decomposition into e.g. rotation gates and - * the canonical gate is RXX(-2 * a), RYY(-2 * b), RZZ (-2 * c). - */ - struct TwoQubitWeylDecomposition { - // a, b, c are the parameters of the canonical gate (CAN) - fp a; // rotation of RXX gate in CAN - fp b; // rotation of RYY gate in CAN - fp c; // rotation of RZZ gate in CAN - fp globalPhase; // global phase adjustment - /** - * q1 - k2r - C - k1r - - * A - * q0 - k2l - N - k1l - - */ - matrix2x2 k1l; // "left" qubit after canonical gate - matrix2x2 k2l; // "left" qubit before canonical gate - matrix2x2 k1r; // "right" qubit after canonical gate - matrix2x2 k2r; // "right" qubit before canonical gate - Specialization specialization; // detected symmetries in the matrix - EulerBasis defaultEulerBasis; // recommended euler basis for k1l/k2l/k1r/k2r - std::optional requestedFidelity; // desired fidelity; - // if set to std::nullopt, no automatic - // specialization will be applied - fp calculatedFidelity; // actual fidelity of decomposition - matrix4x4 unitaryMatrix; // original matrix for this decomposition - - /** - * Create Weyl decomposition. - * - * @param unitaryMatrix Matrix of the two-qubit operation/series to be - * decomposed. - * @param fidelity Tolerance to assume a specialization which is used to - * reduce the number of parameters required by the canonical - * gate and thus potentially decreasing the number of basis - * gates. - * @param specialization Force the use this specialization. - */ - static TwoQubitWeylDecomposition create(const matrix4x4& unitaryMatrix, - std::optional fidelity) { - auto u = unitaryMatrix; - auto detU = u.determinant(); - auto detPow = std::pow(detU, static_cast(-0.25)); - u *= detPow; // remove global phase from unitary matrix - auto globalPhase = std::arg(detU) / 4.; - - // this should have normalized determinant of u, so that u ∈ SU(4) - assert(std::abs(u.determinant() - 1.0) < SANITY_CHECK_PRECISION); - - // transform unitary matrix to magic basis; this enables two properties: - // 1. if uP ∈ SO(4), V = A ⊗ B (SO(4) → SU(2) ⊗ SU(2)) - // 2. magic basis diagonalizes canonical gate, allowing calculation of - // canonical gate parameters later on - auto uP = magicBasisTransform(u, MagicBasisTransform::OutOf); - const matrix4x4 m2 = uP.transpose() * uP; - - // diagonalization yields eigenvectors (p) and eigenvalues (d); - // p is used to calculate K1/K2 (and thus the single-qubit gates - // surrounding the canonical gate); d is is used to determine the weyl - // coordinates and thus the parameters of the canonical gate - // TODO: it may be possible to lower the precision - auto [p, d] = diagonalizeComplexSymmetric(m2, 1e-13); - - // extract Weyl coordinates from eigenvalues, map to [0, 2*pi) - // NOLINTNEXTLINE(misc-include-cleaner) - Eigen::Vector cs; - rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; - dReal(3) = -dReal(0) - dReal(1) - dReal(2); - for (int i = 0; i < static_cast(cs.size()); ++i) { - assert(i < dReal.size()); - cs[i] = remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); - } - - // re-order coordinates and according to min(a, pi/2 - a) with - // a = x mod pi/2 for each weyl coordinate x - decltype(cs) cstemp; - llvm::transform(cs, cstemp.begin(), [](auto&& x) { - auto tmp = remEuclid(x, qc::PI_2); - return std::min(tmp, qc::PI_2 - tmp); - }); - std::array order{0, 1, 2}; - llvm::stable_sort(order, - [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); - std::tie(order[0], order[1], order[2]) = - std::tuple{order[1], order[2], order[0]}; - std::tie(cs[0], cs[1], cs[2]) = - std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; - std::tie(dReal(0), dReal(1), dReal(2)) = - std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2])}; - - // update eigenvectors (columns of p) according to new order of - // weyl coordinates - matrix4x4 pOrig = p; - for (int i = 0; std::cmp_less(i, order.size()); ++i) { - p.col(i) = pOrig.col(order[i]); - } - // apply correction for determinant if necessary - if (p.determinant().real() < 0.0) { - auto lastColumnIndex = p.cols() - 1; - p.col(lastColumnIndex) *= -1.0; - } - assert(std::abs(p.determinant() - 1.0) < SANITY_CHECK_PRECISION); - - // re-create complex eigenvalue matrix; this matrix contains the - // parameters of the canonical gate which is later used in the - // verification - matrix4x4 temp = dReal.asDiagonal(); - temp *= IM; - temp = temp.exp(); - - // combined matrix k1 of 1q gates after canonical gate - matrix4x4 k1 = uP * p * temp; - assert((k1.transpose() * k1).isIdentity()); // k1 must be orthogonal - assert(k1.determinant().real() > 0.0); - k1 = magicBasisTransform(k1, MagicBasisTransform::Into); - - // combined matrix k2 of 1q gates before canonical gate - matrix4x4 k2 = p.transpose().conjugate(); - assert((k2.transpose() * k2).isIdentity()); // k2 must be orthogonal - assert(k2.determinant().real() > 0.0); - k2 = magicBasisTransform(k2, MagicBasisTransform::Into); - - // ensure k1 and k2 are correct (when combined with the canonical gate - // parameters in-between, they are equivalent to u) - assert((k1 * - magicBasisTransform(temp.conjugate(), MagicBasisTransform::Into) * - k2) - .isApprox(u, SANITY_CHECK_PRECISION)); - - // calculate k1 = K1l ⊗ K1r - auto [K1l, K1r, phase_l] = decomposeTwoQubitProductGate(k1); - // decompose k2 = K2l ⊗ K2r - auto [K2l, K2r, phase_r] = decomposeTwoQubitProductGate(k2); - assert(helpers::kroneckerProduct(K1l, K1r).isApprox( - k1, SANITY_CHECK_PRECISION)); - assert(helpers::kroneckerProduct(K2l, K2r).isApprox( - k2, SANITY_CHECK_PRECISION)); - // accumulate global phase - globalPhase += phase_l + phase_r; - - // Flip into Weyl chamber - if (cs[0] > qc::PI_2) { - cs[0] -= 3.0 * qc::PI_2; - K1l = K1l * IPY; - K1r = K1r * IPY; - globalPhase += qc::PI_2; - } - if (cs[1] > qc::PI_2) { - cs[1] -= 3.0 * qc::PI_2; - K1l = K1l * IPX; - K1r = K1r * IPX; - globalPhase += qc::PI_2; - } - auto conjs = 0; - if (cs[0] > qc::PI_4) { - cs[0] = qc::PI_2 - cs[0]; - K1l = K1l * IPY; - K2r = IPY * K2r; - conjs += 1; - globalPhase -= qc::PI_2; - } - if (cs[1] > qc::PI_4) { - cs[1] = qc::PI_2 - cs[1]; - K1l = K1l * IPX; - K2r = IPX * K2r; - conjs += 1; - globalPhase += qc::PI_2; - if (conjs == 1) { - globalPhase -= qc::PI; - } - } - if (cs[2] > qc::PI_2) { - cs[2] -= 3.0 * qc::PI_2; - K1l = K1l * IPZ; - K1r = K1r * IPZ; - globalPhase += qc::PI_2; - if (conjs == 1) { - globalPhase -= qc::PI; - } - } - if (conjs == 1) { - cs[2] = qc::PI_2 - cs[2]; - K1l = K1l * IPZ; - K2r = IPZ * K2r; - globalPhase += qc::PI_2; - } - if (cs[2] > qc::PI_4) { - cs[2] -= qc::PI_2; - K1l = K1l * IPZ; - K1r = K1r * IPZ; - globalPhase -= qc::PI_2; - } - - // bind weyl coordinates as parameters of canonical gate - auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); - // make sure decomposition is equal to input - assert((helpers::kroneckerProduct(K1l, K1r) * - getCanonicalMatrix(a * -2.0, b * -2.0, c * -2.0) * - helpers::kroneckerProduct(K2l, K2r) * std::exp(IM * globalPhase)) - .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); - - TwoQubitWeylDecomposition decomposition{ - .a = a, - .b = b, - .c = c, - .globalPhase = globalPhase, - .k1l = K1l, - .k2l = K2l, - .k1r = K1r, - .k2r = K2r, - .specialization = Specialization::General, - .defaultEulerBasis = EulerBasis::ZYZ, - .requestedFidelity = fidelity, - // will be calculated if a specialization is used, set to -1 for now - .calculatedFidelity = -1.0, - .unitaryMatrix = unitaryMatrix, - }; - - // determine actual specialization of canonical gate so that the 1q - // matrices can potentially be simplified - auto flippedFromOriginal = decomposition.applySpecialization(); - - auto getTrace = [&]() { - if (flippedFromOriginal) { - return TwoQubitWeylDecomposition::getTrace( - qc::PI_2 - a, b, -c, decomposition.a, decomposition.b, - decomposition.c); - } - return TwoQubitWeylDecomposition::getTrace( - a, b, c, decomposition.a, decomposition.b, decomposition.c); - }; - // use trace to calculate fidelity of applied specialization and - // adjust global phase - auto trace = getTrace(); - decomposition.calculatedFidelity = traceToFidelity(trace); - // final check if specialization is close enough to the original matrix to - // satisfy the requested fidelity; since no forced specialization is - // allowed, this should never fail - if (decomposition.requestedFidelity) { - if (decomposition.calculatedFidelity + 1.0e-13 < - *decomposition.requestedFidelity) { - throw std::runtime_error{ - "TwoQubitWeylDecomposition: Calculated fidelity of " - "specialization is worse than requested fidelity!"}; - } - } - decomposition.globalPhase += std::arg(trace); - - // final check if decomposition is still valid after specialization - assert((helpers::kroneckerProduct(decomposition.k1l, decomposition.k1r) * - getCanonicalMatrix(decomposition.a * -2.0, decomposition.b * -2.0, - decomposition.c * -2.0) * - helpers::kroneckerProduct(decomposition.k2l, decomposition.k2r) * - std::exp(IM * decomposition.globalPhase)) - .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); - - return decomposition; - } - - /** - * Calculate matrix of canonical gate based on its parameters a, b, c. - */ - static matrix4x4 getCanonicalMatrix(fp a, fp b, fp c) { - auto xx = getTwoQubitMatrix({ - .type = qc::RXX, - .parameter = {a}, - .qubitId = {0, 1}, - }); - auto yy = getTwoQubitMatrix({ - .type = qc::RYY, - .parameter = {b}, - .qubitId = {0, 1}, - }); - auto zz = getTwoQubitMatrix({ - .type = qc::RZZ, - .parameter = {c}, - .qubitId = {0, 1}, - }); - return zz * yy * xx; - } - - private: - /** - * Diagonalize given complex symmetric matrix M into (P, d) using a - * randomized algorithm. - * This approach is used in both qiskit and quantumflow. - * - * P is the matrix of real or orthogonal eigenvectors of M with P ∈ SO(4) - * d is a vector containing sqrt(eigenvalues) of M with unit-magnitude - * elements (for each element, complex magnitude is 1.0). - * D is d as a diagonal matrix. - * - * M = P * D * P^T - * - * @return pair of (P, D.diagonal()) - */ - [[nodiscard]] static std::pair - diagonalizeComplexSymmetric(const matrix4x4& m, fp precision) { - // We can't use raw `eig` directly because it isn't guaranteed to give - // us real or orthogonal eigenvectors. Instead, since `M` is - // complex-symmetric, - // M = A + iB - // for real-symmetric `A` and `B`, and as - // M^+ @ M2 = A^2 + B^2 + i [A, B] = 1 - // we must have `A` and `B` commute, and consequently they are - // simultaneously diagonalizable. Mixing them together _should_ account - // for any degeneracy problems, but it's not guaranteed, so we repeat it - // a little bit. The fixed seed is to make failures deterministic; the - // value is not important. - auto state = std::mt19937{2023}; - std::normal_distribution dist; - - for (int i = 0; i < 100; ++i) { - fp randA{}; - fp randB{}; - // For debugging the algorithm use the same RNG values as the - // Qiskit implementation for the first random trial. - // In most cases this loop only executes a single iteration and - // using the same rng values rules out possible RNG differences - // as the root cause of a test failure - if (i == 0) { - randA = 1.2602066112249388; - randB = 0.22317849046722027; - } else { - randA = dist(state); - randB = dist(state); - } - const rmatrix4x4 m2Real = randA * m.real() + randB * m.imag(); - const rmatrix4x4 pReal = selfAdjointEigenLower(m2Real).first; - const matrix4x4 p = pReal; - const diagonal4x4 d = (p.transpose() * m * p).diagonal(); - - const matrix4x4 diagD = d.asDiagonal(); - - const matrix4x4 compare = p * diagD * p.transpose(); - if (compare.isApprox(m, precision)) { - // p are the eigenvectors which are decomposed into the - // single-qubit gates surrounding the canonical gate - // d is the sqrt of the eigenvalues that are used to determine the - // weyl coordinates and thus the parameters of the canonical gate - // check that p is in SO(4) - assert((p.transpose() * p).isIdentity(SANITY_CHECK_PRECISION)); - // make sure determinant of eigenvalues is 1.0 - assert(std::abs(matrix4x4{d.asDiagonal()}.determinant() - 1.0) < - SANITY_CHECK_PRECISION); - return std::make_pair(p, d); - } - } - throw std::runtime_error{ - "TwoQubitWeylDecomposition: failed to diagonalize M2."}; - } - - /** - * Calculate trace of two sets of parameters for the canonical gate. - * The trace has been defined in: https://arxiv.org/abs/1811.12926 - */ - [[nodiscard]] static qfp getTrace(fp a, fp b, fp c, fp ap, fp bp, fp cp) { - auto da = a - ap; - auto db = b - bp; - auto dc = c - cp; - return static_cast(4.) * - qfp(std::cos(da) * std::cos(db) * std::cos(dc), - std::sin(da) * std::sin(db) * std::sin(dc)); - } - - /** - * Choose the best specialization for the for the canonical gate. - * This will use the requestedFidelity to determine if a specialization is - * close enough to the actual canonical gate matrix. - */ - [[nodiscard]] Specialization bestSpecialization() const { - auto isClose = [this](fp ap, fp bp, fp cp) -> bool { - auto tr = getTrace(a, b, c, ap, bp, cp); - if (requestedFidelity) { - return traceToFidelity(tr) >= *requestedFidelity; - } - return false; - }; - - auto closestAbc = closestPartialSwap(a, b, c); - auto closestAbMinusC = closestPartialSwap(a, b, -c); - - if (isClose(0., 0., 0.)) { - return Specialization::IdEquiv; - } - if (isClose(qc::PI_4, qc::PI_4, qc::PI_4) || - isClose(qc::PI_4, qc::PI_4, -qc::PI_4)) { - return Specialization::SWAPEquiv; - } - if (isClose(closestAbc, closestAbc, closestAbc)) { - return Specialization::PartialSWAPEquiv; - } - if (isClose(closestAbMinusC, closestAbMinusC, -closestAbMinusC)) { - return Specialization::PartialSWAPFlipEquiv; - } - if (isClose(a, 0., 0.)) { - return Specialization::ControlledEquiv; - } - if (isClose(qc::PI_4, qc::PI_4, c)) { - return Specialization::MirrorControlledEquiv; - } - if (isClose((a + b) / 2., (a + b) / 2., c)) { - return Specialization::FSimaabEquiv; - } - if (isClose(a, (b + c) / 2., (b + c) / 2.)) { - return Specialization::FSimabbEquiv; - } - if (isClose(a, (b - c) / 2., (c - b) / 2.)) { - return Specialization::FSimabmbEquiv; - } - return Specialization::General; - } - - /** - * @return true if the specialization flipped the original decomposition - */ - bool applySpecialization() { - if (specialization != Specialization::General) { - throw std::logic_error{"Application of specialization only works on " - "general decomposition!"}; - } - bool flippedFromOriginal = false; - auto newSpecialization = bestSpecialization(); - if (newSpecialization == Specialization::General) { - // U has no special symmetry. - // - // This gate binds all 6 possible parameters, so there is no need to - // make the single-qubit pre-/post-gates canonical. - return flippedFromOriginal; - } - specialization = newSpecialization; - - if (newSpecialization == Specialization::IdEquiv) { - // :math:`U \sim U_d(0,0,0)` - // Thus, :math:`\sim Id` - // - // This gate binds 0 parameters, we make it canonical by setting: - // - // :math:`K2_l = Id` , :math:`K2_r = Id`. - a = 0.; - b = 0.; - c = 0.; - // unmodified global phase - k1l = k1l * k2l; - k2l = IDENTITY_GATE; - k1r = k1r * k2r; - k2r = IDENTITY_GATE; - } else if (newSpecialization == Specialization::SWAPEquiv) { - // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, -\pi/4)` - // Thus, :math:`U \sim \text{SWAP}` - // - // This gate binds 0 parameters, we make it canonical by setting: - // - // :math:`K2_l = Id` , :math:`K2_r = Id`. - a = qc::PI_4; - b = qc::PI_4; - c = qc::PI_4; - if (c > 0.) { - // unmodified global phase - k1l = k1l * k2r; - k1r = k1r * k2l; - k2l = IDENTITY_GATE; - k2r = IDENTITY_GATE; - } else { - flippedFromOriginal = true; - - globalPhase += qc::PI_2; - k1l = k1l * IPZ * k2r; - k1r = k1r * IPZ * k2l; - k2l = IDENTITY_GATE; - k2r = IDENTITY_GATE; - } - } else if (newSpecialization == Specialization::PartialSWAPEquiv) { - // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4)` - // Thus, :math:`U \sim \text{SWAP}^\alpha` - // - // This gate binds 3 parameters, we make it canonical by setting: - // - // :math:`K2_l = Id`. - auto closest = closestPartialSwap(a, b, c); - auto k2lDagger = k2l.transpose().conjugate(); - - a = closest; - b = closest; - c = closest; - // unmodified global phase - k1l = k1l * k2l; - k1r = k1r * k2l; - k2r = k2lDagger * k2r; - k2l = IDENTITY_GATE; - } else if (newSpecialization == Specialization::PartialSWAPFlipEquiv) { - // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4)` - // Thus, :math:`U \sim \text{SWAP}^\alpha` - // - // (a non-equivalent root of SWAP from the TwoQubitWeylPartialSWAPEquiv - // similar to how :math:`x = (\pm \sqrt(x))^2`) - // - // This gate binds 3 parameters, we make it canonical by setting: - // - // :math:`K2_l = Id` - auto closest = closestPartialSwap(a, b, -c); - auto k2lDagger = k2l.transpose().conjugate(); - - a = closest; - b = closest; - c = -closest; - // unmodified global phase - k1l = k1l * k2l; - k1r = k1r * IPZ * k2l * IPZ; - k2r = IPZ * k2lDagger * IPZ * k2r; - k2l = IDENTITY_GATE; - } else if (newSpecialization == Specialization::ControlledEquiv) { - // :math:`U \sim U_d(\alpha, 0, 0)` - // Thus, :math:`U \sim \text{Ctrl-U}` - // - // This gate binds 4 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l) Rx(\lambda_l)` - // :math:`K2_r = Ry(\theta_r) Rx(\lambda_r)` - auto eulerBasis = EulerBasis::XYX; - auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - anglesFromUnitary(k2l, eulerBasis); - auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = - anglesFromUnitary(k2r, eulerBasis); - - // unmodified parameter a - b = 0.; - c = 0.; - globalPhase = globalPhase + k2lphase + k2rphase; - k1l = k1l * rxMatrix(k2lphi); - k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda); - k1r = k1r * rxMatrix(k2rphi); - k2r = ryMatrix(k2rtheta) * rxMatrix(k2rlambda); - defaultEulerBasis = eulerBasis; - } else if (newSpecialization == Specialization::MirrorControlledEquiv) { - // :math:`U \sim U_d(\pi/4, \pi/4, \alpha)` - // Thus, :math:`U \sim \text{SWAP} \cdot \text{Ctrl-U}` - // - // This gate binds 4 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` - // :math:`K2_r = Ry(\theta_r)\cdot Rz(\lambda_r)` - auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - anglesFromUnitary(k2l, EulerBasis::ZYZ); - auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = - anglesFromUnitary(k2r, EulerBasis::ZYZ); - - a = qc::PI_4; - b = qc::PI_4; - // unmodified parameter c - globalPhase = globalPhase + k2lphase + k2rphase; - k1l = k1l * rzMatrix(k2rphi); - k2l = ryMatrix(k2ltheta) * rzMatrix(k2llambda); - k1r = k1r * rzMatrix(k2lphi); - k2r = ryMatrix(k2rtheta) * rzMatrix(k2rlambda); - } else if (newSpecialization == Specialization::FSimaabEquiv) { - // :math:`U \sim U_d(\alpha, \alpha, \beta), \alpha \geq |\beta|` - // - // This gate binds 5 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)`. - auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - anglesFromUnitary(k2l, EulerBasis::ZYZ); - auto ab = (a + b) / 2.; - - a = ab; - b = ab; - // unmodified parameter c - globalPhase = globalPhase + k2lphase; - k1l = k1l * rzMatrix(k2lphi); - k2l = ryMatrix(k2ltheta) * rzMatrix(k2llambda); - k1r = k1r * rzMatrix(k2lphi); - k2r = rzMatrix(-k2lphi) * k2r; - } else if (newSpecialization == Specialization::FSimabbEquiv) { - // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` - // - // This gate binds 5 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` - auto eulerBasis = EulerBasis::XYX; - auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - anglesFromUnitary(k2l, eulerBasis); - auto bc = (b + c) / 2.; - - // unmodified parameter a - b = bc; - c = bc; - globalPhase = globalPhase + k2lphase; - k1l = k1l * rxMatrix(k2lphi); - k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda); - k1r = k1r * rxMatrix(k2lphi); - k2r = rxMatrix(-k2lphi) * k2r; - defaultEulerBasis = eulerBasis; - } else if (newSpecialization == Specialization::FSimabmbEquiv) { - // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` - // - // This gate binds 5 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` - auto eulerBasis = EulerBasis::XYX; - auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - anglesFromUnitary(k2l, eulerBasis); - auto bc = (b - c) / 2.; - - // unmodified parameter a - b = bc; - c = -bc; - globalPhase = globalPhase + k2lphase; - k1l = k1l * rxMatrix(k2lphi); - k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda); - k1r = k1r * IPZ * rxMatrix(k2lphi) * IPZ; - k2r = IPZ * rxMatrix(-k2lphi) * IPZ * k2r; - defaultEulerBasis = eulerBasis; - } else { - throw std::logic_error{"Unknown specialization"}; - } - return flippedFromOriginal; - } - }; - - /** - * Factor by which two matrices are considered to be the same when simplifying - * during a decomposition. - */ - static constexpr auto DEFAULT_FIDELITY = 1.0 - 1e-15; - /** - * Largest number that will be assumed as zero for the euler decompositions - * and the global phase. - */ - static constexpr auto DEFAULT_ATOL = 1e-12; - - /** - * Decomposer that must be initialized with a two-qubit basis gate that will - * be used to generate a circuit equivalent to a canonical gate (RXX+RYY+RZZ). - */ - class TwoQubitBasisDecomposer { - public: - /** - * Create decomposer that allows two-qubit decompositions based on the - * specified basis gate. - * This basis gate will appear between 0 and 3 times in each decompositions. - * The order of qubits is relevant and will change the results accordingly. - * The decomposer cannot handle different basis gates in the same - * decomposition (different order of the qubits also counts as a different - * basis gate). - */ - static TwoQubitBasisDecomposer - create(const OneQubitGateSequence::Gate& basisGate = {.type = qc::X, - .parameter = {}, - .qubitId = {0, 1}}, - fp basisFidelity = DEFAULT_FIDELITY) { - auto relativeEq = [](auto&& lhs, auto&& rhs, auto&& epsilon, - auto&& maxRelative) { - // Handle same infinities - if (lhs == rhs) { - return true; - } - - // Handle remaining infinities - if (std::isinf(lhs) || std::isinf(rhs)) { - return false; - } - - auto absDiff = std::abs(lhs - rhs); - - // For when the numbers are really close together - if (absDiff <= epsilon) { - return true; - } - - auto absLhs = std::abs(lhs); - auto absRhs = std::abs(rhs); - if (absRhs > absLhs) { - return absDiff <= absRhs * maxRelative; - } - return absDiff <= absLhs * maxRelative; - }; - const matrix2x2 k12RArr{ - {qfp(0., FRAC1_SQRT2), qfp(FRAC1_SQRT2, 0.)}, - {qfp(-FRAC1_SQRT2, 0.), qfp(0., -FRAC1_SQRT2)}, - }; - const matrix2x2 k12LArr{ - {qfp(0.5, 0.5), qfp(0.5, 0.5)}, - {qfp(-0.5, 0.5), qfp(0.5, -0.5)}, - }; - - const auto basisDecomposer = TwoQubitWeylDecomposition::create( - getTwoQubitMatrix(basisGate), basisFidelity); - const auto isSuperControlled = - relativeEq(basisDecomposer.a, qc::PI_4, 1e-13, 1e-09) && - relativeEq(basisDecomposer.c, 0.0, 1e-13, 1e-09); - - // Create some useful matrices U1, U2, U3 are equivalent to the basis, - // expand as Ui = Ki1.Ubasis.Ki2 - auto b = basisDecomposer.b; - auto temp = qfp(0.5, -0.5); - const matrix2x2 k11l{ - {temp * (M_IM * std::exp(qfp(0., -b))), temp * std::exp(qfp(0., -b))}, - {temp * (M_IM * std::exp(qfp(0., b))), temp * -std::exp(qfp(0., b))}}; - const matrix2x2 k11r{{FRAC1_SQRT2 * (IM * std::exp(qfp(0., -b))), - FRAC1_SQRT2 * -std::exp(qfp(0., -b))}, - {FRAC1_SQRT2 * std::exp(qfp(0., b)), - FRAC1_SQRT2 * (M_IM * std::exp(qfp(0., b)))}}; - const matrix2x2 k32lK21l{{FRAC1_SQRT2 * qfp(1., std::cos(2. * b)), - FRAC1_SQRT2 * (IM * std::sin(2. * b))}, - {FRAC1_SQRT2 * (IM * std::sin(2. * b)), - FRAC1_SQRT2 * qfp(1., -std::cos(2. * b))}}; - temp = qfp(0.5, 0.5); - const matrix2x2 k21r{ - {temp * (M_IM * std::exp(qfp(0., -2. * b))), - temp * std::exp(qfp(0., -2. * b))}, - {temp * (IM * std::exp(qfp(0., 2. * b))), - temp * std::exp(qfp(0., 2. * b))}, - }; - const matrix2x2 k22l{ - {qfp(FRAC1_SQRT2, 0.), qfp(-FRAC1_SQRT2, 0.)}, - {qfp(FRAC1_SQRT2, 0.), qfp(FRAC1_SQRT2, 0.)}, - }; - const matrix2x2 k22r{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; - const matrix2x2 k31l{ - {FRAC1_SQRT2 * std::exp(qfp(0., -b)), - FRAC1_SQRT2 * std::exp(qfp(0., -b))}, - {FRAC1_SQRT2 * -std::exp(qfp(0., b)), - FRAC1_SQRT2 * std::exp(qfp(0., b))}, - }; - const matrix2x2 k31r{ - {IM * std::exp(qfp(0., b)), C_ZERO}, - {C_ZERO, M_IM * std::exp(qfp(0., -b))}, - }; - temp = qfp(0.5, 0.5); - const matrix2x2 k32r{ - {temp * std::exp(qfp(0., b)), temp * -std::exp(qfp(0., -b))}, - {temp * (M_IM * std::exp(qfp(0., b))), - temp * (M_IM * std::exp(qfp(0., -b)))}, - }; - auto k1lDagger = basisDecomposer.k1l.transpose().conjugate(); - auto k1rDagger = basisDecomposer.k1r.transpose().conjugate(); - auto k2lDagger = basisDecomposer.k2l.transpose().conjugate(); - auto k2rDagger = basisDecomposer.k2r.transpose().conjugate(); - // Pre-build the fixed parts of the matrices used in 3-part - // decomposition - auto u0l = k31l * k1lDagger; - auto u0r = k31r * k1rDagger; - auto u1l = k2lDagger * k32lK21l * k1lDagger; - auto u1ra = k2rDagger * k32r; - auto u1rb = k21r * k1rDagger; - auto u2la = k2lDagger * k22l; - auto u2lb = k11l * k1lDagger; - auto u2ra = k2rDagger * k22r; - auto u2rb = k11r * k1rDagger; - auto u3l = k2lDagger * k12LArr; - auto u3r = k2rDagger * k12RArr; - // Pre-build the fixed parts of the matrices used in the 2-part - // decomposition - auto q0l = k12LArr.transpose().conjugate() * k1lDagger; - auto q0r = k12RArr.transpose().conjugate() * IPZ * k1rDagger; - auto q1la = k2lDagger * k11l.transpose().conjugate(); - auto q1lb = k11l * k1lDagger; - auto q1ra = k2rDagger * IPZ * k11r.transpose().conjugate(); - auto q1rb = k11r * k1rDagger; - auto q2l = k2lDagger * k12LArr; - auto q2r = k2rDagger * k12RArr; - - return TwoQubitBasisDecomposer{ - basisGate, - basisFidelity, - basisDecomposer, - isSuperControlled, - u0l, - u0r, - u1l, - u1ra, - u1rb, - u2la, - u2lb, - u2ra, - u2rb, - u3l, - u3r, - q0l, - q0r, - q1la, - q1lb, - q1ra, - q1rb, - q2l, - q2r, - }; - } - - /** - * Perform decomposition using the basis gate of this decomposer. - * - * @param targetDecomposition Prepared Weyl decomposition of unitary matrix - * to be decomposed. - * @param target1qEulerBases List of euler bases that should be tried out to - * find the best one for each euler decomposition. - * All bases will be mixed to get the best overall - * result. - * @param basisFidelity Fidelity for lowering the number of basis gates - * required - * @param approximate If true, use basisFidelity or, if std::nullopt, use - * basisFidelity of this decomposer. If false, fidelity - * of 1.0 will be assumed. - * @param numBasisGateUses Force use of given number of basis gates. - */ - [[nodiscard]] std::optional - twoQubitDecompose(const TwoQubitWeylDecomposition& targetDecomposition, - const llvm::SmallVector& target1qEulerBases, - std::optional basisFidelity, bool approximate, - std::optional numBasisGateUses) const { - auto getBasisFidelity = [&]() { - if (approximate) { - return basisFidelity.value_or(this->basisFidelity); - } - return static_cast(1.0); - }; - fp actualBasisFidelity = getBasisFidelity(); - auto traces = this->traces(targetDecomposition); - auto getDefaultNbasis = [&]() { - auto minValue = std::numeric_limits::min(); - auto minIndex = -1; - for (int i = 0; std::cmp_less(i, traces.size()); ++i) { - // lower fidelity means it becomes easier to choose a lower number of - // basis gates - auto value = - traceToFidelity(traces[i]) * std::pow(actualBasisFidelity, i); - if (value > minValue) { - minIndex = i; - minValue = value; - } - } - return minIndex; - }; - // number of basis gates that need to be used in the decomposition - auto bestNbasis = numBasisGateUses.value_or(getDefaultNbasis()); - auto chooseDecomposition = [&]() { - if (bestNbasis == 0) { - return decomp0(targetDecomposition); - } - if (bestNbasis == 1) { - return decomp1(targetDecomposition); - } - if (bestNbasis == 2) { - return decomp2Supercontrolled(targetDecomposition); - } - if (bestNbasis == 3) { - return decomp3Supercontrolled(targetDecomposition); - } - throw std::logic_error{"Invalid basis to use"}; - }; - auto decomposition = chooseDecomposition(); - llvm::SmallVector, 8> - eulerDecompositions; - for (auto&& decomp : decomposition) { - assert(helpers::isUnitaryMatrix(decomp)); - auto eulerDecomp = unitaryToGateSequenceInner( - decomp, target1qEulerBases, 0, true, std::nullopt); - eulerDecompositions.push_back(eulerDecomp); - } - TwoQubitGateSequence gates{ - .gates = {}, - .globalPhase = targetDecomposition.globalPhase, - }; - // Worst case length is 5x 1q gates for each 1q decomposition + 1x 2q - // gate We might overallocate a bit if the euler basis is different but - // the worst case is just 16 extra elements with just a String and 2 - // smallvecs each. This is only transient though as the circuit - // sequences aren't long lived and are just used to create a - // QuantumCircuit or DAGCircuit when we return to Python space. - constexpr auto twoQubitSequenceDefaultCapacity = 21; - gates.gates.reserve(twoQubitSequenceDefaultCapacity); - gates.globalPhase -= bestNbasis * basisDecomposer.globalPhase; - if (bestNbasis == 2) { - gates.globalPhase += qc::PI; - } - - auto addEulerDecomposition = [&](std::size_t index, QubitId qubitId) { - if (auto&& eulerDecomp = eulerDecompositions[index]) { - for (auto&& gate : eulerDecomp->gates) { - gates.gates.push_back({.type = gate.type, - .parameter = gate.parameter, - .qubitId = {qubitId}}); - } - gates.globalPhase += eulerDecomp->globalPhase; - } - }; - - for (std::size_t i = 0; i < bestNbasis; ++i) { - // add single-qubit decompositions before basis gate - addEulerDecomposition(2 * i, 0); - addEulerDecomposition((2 * i) + 1, 1); - - // add basis gate - gates.gates.push_back(basisGate); - } - - // add single-qubit decompositions after basis gate - addEulerDecomposition(2UL * bestNbasis, 0); - addEulerDecomposition((2UL * bestNbasis) + 1, 1); - - // large global phases can be generated by the decomposition, thus limit - // it to [0, +2*pi); TODO: can be removed, should be done by something - // like constant folding - gates.globalPhase = remEuclid(gates.globalPhase, qc::TAU); - - return gates; - } - - protected: - /** - * Constructs decomposer instance. - */ - TwoQubitBasisDecomposer(QubitGateSequence::Gate basisGate, fp basisFidelity, - TwoQubitWeylDecomposition basisDecomposer, - bool isSuperControlled, matrix2x2 u0l, - matrix2x2 u0r, matrix2x2 u1l, matrix2x2 u1ra, - matrix2x2 u1rb, matrix2x2 u2la, matrix2x2 u2lb, - matrix2x2 u2ra, matrix2x2 u2rb, matrix2x2 u3l, - matrix2x2 u3r, matrix2x2 q0l, matrix2x2 q0r, - matrix2x2 q1la, matrix2x2 q1lb, matrix2x2 q1ra, - matrix2x2 q1rb, matrix2x2 q2l, matrix2x2 q2r) - : basisGate{std::move(basisGate)}, basisFidelity{basisFidelity}, - basisDecomposer{std::move(basisDecomposer)}, - isSuperControlled{isSuperControlled}, u0l{std::move(u0l)}, - u0r{std::move(u0r)}, u1l{std::move(u1l)}, u1ra{std::move(u1ra)}, - u1rb{std::move(u1rb)}, u2la{std::move(u2la)}, u2lb{std::move(u2lb)}, - u2ra{std::move(u2ra)}, u2rb{std::move(u2rb)}, u3l{std::move(u3l)}, - u3r{std::move(u3r)}, q0l{std::move(q0l)}, q0r{std::move(q0r)}, - q1la{std::move(q1la)}, q1lb{std::move(q1lb)}, q1ra{std::move(q1ra)}, - q1rb{std::move(q1rb)}, q2l{std::move(q2l)}, q2r{std::move(q2r)} {} - - /** - * Calculate decompositions when no basis gate is required. - * - * Decompose target :math:`\sim U_d(x, y, z)` with 0 uses of the - * basis gate. Result :math:`U_r` has trace: - * - * .. math:: - * - * \Big\vert\text{Tr}(U_r\cdot U_\text{target}^{\dag})\Big\vert = - * 4\Big\vert (\cos(x)\cos(y)\cos(z)+ j \sin(x)\sin(y)\sin(z)\Big\vert - * - * which is optimal for all targets and bases - */ - [[nodiscard]] static llvm::SmallVector - decomp0(const TwoQubitWeylDecomposition& target) { - return { - target.k1r * target.k2r, - target.k1l * target.k2l, - }; - } - - /** - * Calculate decompositions when one basis gate is required. - * - * Decompose target :math:`\sim U_d(x, y, z)` with 1 use of the - * basis gate math:`\sim U_d(a, b, c)`. Result :math:`U_r` has trace: - * - * .. math:: - * - * \Big\vert\text{Tr}(U_r \cdot U_\text{target}^{\dag})\Big\vert = - * 4\Big\vert \cos(x-a)\cos(y-b)\cos(z-c) + j - * \sin(x-a)\sin(y-b)\sin(z-c)\Big\vert - * - * which is optimal for all targets and bases with ``z==0`` or ``c==0``. - */ - [[nodiscard]] llvm::SmallVector - decomp1(const TwoQubitWeylDecomposition& target) const { - // may not work for z != 0 and c != 0 (not always in Weyl chamber) - return { - basisDecomposer.k2r.transpose().conjugate() * target.k2r, - basisDecomposer.k2l.transpose().conjugate() * target.k2l, - target.k1r * basisDecomposer.k1r.transpose().conjugate(), - target.k1l * basisDecomposer.k1l.transpose().conjugate(), - }; - } - - /** - * Calculate decompositions when two basis gates are required. - * - * Decompose target :math:`\sim U_d(x, y, z)` with 2 uses of the - * basis gate. - * - * For supercontrolled basis :math:`\sim U_d(\pi/4, b, 0)`, all b, result - * :math:`U_r` has trace - * - * .. math:: - * - * \Big\vert\text{Tr}(U_r \cdot U_\text{target}^\dag) \Big\vert = - * 4\cos(z) - * - * which is the optimal approximation for basis of CNOT-class - * :math:`\sim U_d(\pi/4, 0, 0)` or DCNOT-class - * :math:`\sim U_d(\pi/4, \pi/4, 0)` and any target. It may be sub-optimal - * for :math:`b \neq 0` (i.e. there exists an exact decomposition for any - * target using :math:`B \sim U_d(\pi/4, \pi/8, 0)`, but it may not be this - * decomposition). This is an exact decomposition for supercontrolled basis - * and target :math:`\sim U_d(x, y, 0)`. No guarantees for - * non-supercontrolled basis. - */ - [[nodiscard]] llvm::SmallVector - decomp2Supercontrolled(const TwoQubitWeylDecomposition& target) const { - if (!isSuperControlled) { - // TODO: make fatal error? check in constructor? - llvm::errs() - << "Basis gate of TwoQubitBasisDecomposer is not super-controlled " - "- no guarantee for exact decomposition with two basis gates\n"; - } - return { - q2r * target.k2r, - q2l * target.k2l, - q1ra * rzMatrix(2. * target.b) * q1rb, - q1la * rzMatrix(-2. * target.a) * q1lb, - target.k1r * q0r, - target.k1l * q0l, - }; - } - - /** - * Calculate decompositions when three basis gates are required. - * - * Decompose target with 3 uses of the basis. - * - * This is an exact decomposition for supercontrolled basis - * :math:`\sim U_d(\pi/4, b, 0)`, all b, and any target. No guarantees for - * non-supercontrolled basis. - */ - [[nodiscard]] llvm::SmallVector - decomp3Supercontrolled(const TwoQubitWeylDecomposition& target) const { - if (!isSuperControlled) { - llvm::errs() - << "Basis gate of TwoQubitBasisDecomposer is not super-controlled " - "- no guarantee for exact decomposition with " - "three basis gates\n"; - } - return { - u3r * target.k2r, - u3l * target.k2l, - u2ra * rzMatrix(2. * target.b) * u2rb, - u2la * rzMatrix(-2. * target.a) * u2lb, - u1ra * rzMatrix(-2. * target.c) * u1rb, - u1l, - target.k1r * u0r, - target.k1l * u0l, - }; - } - - /** - * Calculate traces for a combination of the parameters of the canonical - * gates of the target and basis decompositions. - * This can be used to determine the smallest number of basis gates that are - * necessary to construct an equivalent to the canonical gate. - */ - [[nodiscard]] std::array - traces(const TwoQubitWeylDecomposition& target) const { - return { - static_cast(4.) * - qfp(std::cos(target.a) * std::cos(target.b) * std::cos(target.c), - std::sin(target.a) * std::sin(target.b) * std::sin(target.c)), - static_cast(4.) * qfp(std::cos(qc::PI_4 - target.a) * - std::cos(basisDecomposer.b - target.b) * - std::cos(target.c), - std::sin(qc::PI_4 - target.a) * - std::sin(basisDecomposer.b - target.b) * - std::sin(target.c)), - qfp(4. * std::cos(target.c), 0.), - qfp(4., 0.), - }; - } - - /** - * Perform single-qubit decomposition of a 2x2 unitary matrix based on a - * given euler basis. - */ - [[nodiscard]] static OneQubitGateSequence - generateCircuit(EulerBasis targetBasis, const matrix2x2& unitaryMatrix, - bool simplify, std::optional atol) { - auto [theta, phi, lambda, phase] = - anglesFromUnitary(unitaryMatrix, targetBasis); - - switch (targetBasis) { - case EulerBasis::ZYZ: - return calculateRotationGates(theta, phi, lambda, phase, qc::RZ, qc::RY, - simplify, atol); - case EulerBasis::ZXZ: - return calculateRotationGates(theta, phi, lambda, phase, qc::RZ, qc::RX, - simplify, atol); - case EulerBasis::XZX: - return calculateRotationGates(theta, phi, lambda, phase, qc::RX, qc::RZ, - simplify, atol); - case EulerBasis::XYX: - return calculateRotationGates(theta, phi, lambda, phase, qc::RX, qc::RY, - simplify, atol); - default: - // TODO: allow other bases - throw std::invalid_argument{"Unsupported base for circuit generation!"}; - } - } - - /** - * Decompose a single-qubit unitary matrix into a single-qubit gate - * sequence. Multiple euler bases may be specified and the one with the - * least complexity will be chosen. - */ - [[nodiscard]] static OneQubitGateSequence unitaryToGateSequenceInner( - const matrix2x2& unitaryMat, - const llvm::SmallVector& targetBasisList, QubitId /*qubit*/, - // TODO: add error map here: per qubit a mapping of operation to error - // value for better calculateError() - bool simplify, std::optional atol) { - auto calculateError = [](const OneQubitGateSequence& sequence) -> fp { - return static_cast(sequence.complexity()); - }; - - auto minError = std::numeric_limits::max(); - OneQubitGateSequence bestCircuit; - for (auto targetBasis : targetBasisList) { - auto circuit = generateCircuit(targetBasis, unitaryMat, simplify, atol); - assert(circuit.getUnitaryMatrix().isApprox( - helpers::kroneckerProduct(IDENTITY_GATE, unitaryMat), - SANITY_CHECK_PRECISION)); - auto error = calculateError(circuit); - if (error < minError) { - bestCircuit = circuit; - minError = error; - } - } - return bestCircuit; - } - - private: - // basis gate of this decomposer instance - QubitGateSequence::Gate basisGate; - // fidelity with which the basis gate decomposition has been calculated - fp basisFidelity; - // cached decomposition for basis gate - TwoQubitWeylDecomposition basisDecomposer; - // true if basis gate is super-controlled - bool isSuperControlled; - - // pre-built components for decomposition with 3 basis gates - matrix2x2 u0l; - matrix2x2 u0r; - matrix2x2 u1l; - matrix2x2 u1ra; - matrix2x2 u1rb; - matrix2x2 u2la; - matrix2x2 u2lb; - matrix2x2 u2ra; - matrix2x2 u2rb; - matrix2x2 u3l; - matrix2x2 u3r; - - // pre-built components for decomposition with 2 basis gates - matrix2x2 q0l; - matrix2x2 q0r; - matrix2x2 q1la; - matrix2x2 q1lb; - matrix2x2 q1ra; - matrix2x2 q1rb; - matrix2x2 q2l; - matrix2x2 q2r; - }; - - // TODO: copied+adapted from single-qubit decomposition - /** - * @note Adapted from circuit_kak() in the IBM Qiskit framework. - * (C) Copyright IBM 2022 - * - * This code is licensed under the Apache License, Version 2.0. You - * may obtain a copy of this license in the LICENSE.txt file in the root - * directory of this source tree or at - * http://www.apache.org/licenses/LICENSE-2.0. - * - * Any modifications or derivative works of this code must retain - * this copyright notice, and modified files need to carry a notice - * indicating that they have been altered from the originals. - */ - [[nodiscard]] static OneQubitGateSequence - calculateRotationGates(fp theta, fp phi, fp lambda, fp phase, - qc::OpType kGate, qc::OpType aGate, bool simplify, - std::optional atol) { - fp angleZeroEpsilon = atol.value_or(DEFAULT_ATOL); - if (!simplify) { - angleZeroEpsilon = -1.0; - } - - OneQubitGateSequence sequence{ - .gates = {}, - .globalPhase = phase - ((phi + lambda) / 2.), - }; - if (std::abs(theta) <= angleZeroEpsilon) { - lambda += phi; - lambda = mod2pi(lambda); - if (std::abs(lambda) > angleZeroEpsilon) { - sequence.gates.push_back({.type = kGate, .parameter = {lambda}}); - sequence.globalPhase += lambda / 2.0; - } - return sequence; - } - - if (std::abs(theta - qc::PI) <= angleZeroEpsilon) { - sequence.globalPhase += phi; - lambda -= phi; - phi = 0.0; - } - if (std::abs(mod2pi(lambda + qc::PI)) <= angleZeroEpsilon || - std::abs(mod2pi(phi + qc::PI)) <= angleZeroEpsilon) { - lambda += qc::PI; - theta = -theta; - phi += qc::PI; - } - lambda = mod2pi(lambda); - if (std::abs(lambda) > angleZeroEpsilon) { - sequence.globalPhase += lambda / 2.0; - sequence.gates.push_back({.type = kGate, .parameter = {lambda}}); - } - sequence.gates.push_back({.type = aGate, .parameter = {theta}}); - phi = mod2pi(phi); - if (std::abs(phi) > angleZeroEpsilon) { - sequence.globalPhase += phi / 2.0; - sequence.gates.push_back({.type = kGate, .parameter = {phi}}); - } - return sequence; - } - private: - llvm::SmallVector decomposerBasisGate; - llvm::SmallVector basisDecomposers; + llvm::SmallVector decomposerBasisGate; + llvm::SmallVector basisDecomposers; llvm::SmallVector decomposerEulerBases; }; -const matrix2x2 GateDecompositionPattern::IDENTITY_GATE = matrix2x2::Identity(); -const matrix2x2 GateDecompositionPattern::H_GATE{{1.0 / SQRT2, 1.0 / SQRT2}, - {1.0 / SQRT2, -1.0 / SQRT2}}; -const matrix2x2 GateDecompositionPattern::IPZ{{IM, C_ZERO}, {C_ZERO, M_IM}}; -const matrix2x2 GateDecompositionPattern::IPY{{C_ZERO, C_ONE}, - {C_M_ONE, C_ZERO}}; -const matrix2x2 GateDecompositionPattern::IPX{{C_ZERO, IM}, {IM, C_ZERO}}; - /** * @brief Populates the given pattern set with patterns for gate * decomposition. */ void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns) { - llvm::SmallVector - basisGates; + llvm::SmallVector basisGates; llvm::SmallVector eulerBases; basisGates.push_back({.type = qc::X, .parameter = {}, .qubitId = {0, 1}}); basisGates.push_back({.type = qc::X, .parameter = {}, .qubitId = {1, 0}}); From 910384e00b458301b044d344237dacc7f7814c1c Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 26 Nov 2025 18:11:23 +0100 Subject: [PATCH 111/237] slight improvement to weyl decomposition for testing purposes --- .../Decomposition/WeylDecomposition.h | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h index 43b83c2c48..e8632533c3 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h @@ -71,9 +71,9 @@ struct TwoQubitWeylDecomposition { }; // a, b, c are the parameters of the canonical gate (CAN) - fp a; // rotation of RXX gate in CAN - fp b; // rotation of RYY gate in CAN - fp c; // rotation of RZZ gate in CAN + fp a; // rotation of RXX gate in CAN (must be taken times -2.0) + fp b; // rotation of RYY gate in CAN (must be taken times -2.0) + fp c; // rotation of RZZ gate in CAN (must be taken times -2.0) fp globalPhase; // global phase adjustment /** * q1 - k2r - C - k1r - @@ -101,7 +101,6 @@ struct TwoQubitWeylDecomposition { * reduce the number of parameters required by the canonical * gate and thus potentially decreasing the number of basis * gates. - * @param specialization Force the use this specialization. */ static TwoQubitWeylDecomposition create(const matrix4x4& unitaryMatrix, std::optional fidelity) { @@ -260,11 +259,6 @@ struct TwoQubitWeylDecomposition { // bind weyl coordinates as parameters of canonical gate auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); - // make sure decomposition is equal to input - assert((helpers::kroneckerProduct(K1l, K1r) * - getCanonicalMatrix(a * -2.0, b * -2.0, c * -2.0) * - helpers::kroneckerProduct(K2l, K2r) * std::exp(IM * globalPhase)) - .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); TwoQubitWeylDecomposition decomposition{ .a = a, @@ -282,6 +276,11 @@ struct TwoQubitWeylDecomposition { .calculatedFidelity = -1.0, .unitaryMatrix = unitaryMatrix, }; + // make sure decomposition is equal to input + assert((helpers::kroneckerProduct(K1l, K1r) * + decomposition.getCanonicalMatrix() * + helpers::kroneckerProduct(K2l, K2r) * std::exp(IM * globalPhase)) + .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); // determine actual specialization of canonical gate so that the 1q // matrices can potentially be simplified @@ -315,8 +314,7 @@ struct TwoQubitWeylDecomposition { // final check if decomposition is still valid after specialization assert((helpers::kroneckerProduct(decomposition.k1l, decomposition.k1r) * - getCanonicalMatrix(decomposition.a * -2.0, decomposition.b * -2.0, - decomposition.c * -2.0) * + decomposition.getCanonicalMatrix() * helpers::kroneckerProduct(decomposition.k2l, decomposition.k2r) * std::exp(IM * decomposition.globalPhase)) .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); @@ -327,20 +325,20 @@ struct TwoQubitWeylDecomposition { /** * Calculate matrix of canonical gate based on its parameters a, b, c. */ - static matrix4x4 getCanonicalMatrix(fp a, fp b, fp c) { + [[nodiscard]] matrix4x4 getCanonicalMatrix() const { auto xx = getTwoQubitMatrix({ .type = qc::RXX, - .parameter = {a}, + .parameter = {-2.0 * a}, .qubitId = {0, 1}, }); auto yy = getTwoQubitMatrix({ .type = qc::RYY, - .parameter = {b}, + .parameter = {-2.0 * b}, .qubitId = {0, 1}, }); auto zz = getTwoQubitMatrix({ .type = qc::RZZ, - .parameter = {c}, + .parameter = {-2.0 * c}, .qubitId = {0, 1}, }); return zz * yy * xx; From 4c42850d9400a1bd3b51710b2803f6461538037f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 26 Nov 2025 19:13:47 +0100 Subject: [PATCH 112/237] add unittest for weyl decomposition --- mlir/unittests/CMakeLists.txt | 1 + mlir/unittests/decomposition/CMakeLists.txt | 21 +++ .../decomposition/test_weyl_decomposition.cpp | 120 ++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 mlir/unittests/decomposition/CMakeLists.txt create mode 100644 mlir/unittests/decomposition/test_weyl_decomposition.cpp diff --git a/mlir/unittests/CMakeLists.txt b/mlir/unittests/CMakeLists.txt index 43ffbbd241..b8bf8eb187 100644 --- a/mlir/unittests/CMakeLists.txt +++ b/mlir/unittests/CMakeLists.txt @@ -6,6 +6,7 @@ # # Licensed under the MIT License +add_subdirectory(decomposition) add_subdirectory(dialect) add_subdirectory(pipeline) add_subdirectory(translation) diff --git a/mlir/unittests/decomposition/CMakeLists.txt b/mlir/unittests/decomposition/CMakeLists.txt new file mode 100644 index 0000000000..9aa82c9edb --- /dev/null +++ b/mlir/unittests/decomposition/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +set(testname "mqt-core-mlir-decomposition-test") +file(GLOB_RECURSE DECOMPOSITION_TEST_SOURCES *.cpp) + +if(NOT TARGET ${testname}) + # create an executable in which the tests will be stored + add_executable(${testname} ${DECOMPOSITION_TEST_SOURCES}) + # link the Google test infrastructure and a default main function to the test executable. + target_link_libraries(${testname} PRIVATE GTest::gmock GTest::gtest_main LLVMFileCheck MLIRPass + MLIRTransforms MLIRMQTOptTransforms) + # discover tests + gtest_discover_tests(${testname} DISCOVERY_TIMEOUT 60) + set_target_properties(${testname} PROPERTIES FOLDER unittests) +endif() diff --git a/mlir/unittests/decomposition/test_weyl_decomposition.cpp b/mlir/unittests/decomposition/test_weyl_decomposition.cpp new file mode 100644 index 0000000000..4ec8969565 --- /dev/null +++ b/mlir/unittests/decomposition/test_weyl_decomposition.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h" + +#include + +using namespace mqt::ir::opt; +using namespace mqt::ir::opt::decomposition; + +namespace { +[[nodiscard]] matrix4x4 randomUnitaryMatrix() { + [[maybe_unused]] static auto initializeRandom = []() { + // Eigen uses std::rand() internally, use fixed seed for deterministic + // testing behavior + std::srand(123456UL); + return true; + }(); + matrix4x4 randomMatrix = matrix4x4::Random(); + Eigen::HouseholderQR qr{}; + qr.compute(randomMatrix); + matrix4x4 unitaryMatrix = qr.householderQ(); + assert(helpers::isUnitaryMatrix(unitaryMatrix)); + return unitaryMatrix; +} +} // namespace + +class WeylDecompositionTest : public testing::TestWithParam { +public: + [[nodiscard]] static matrix4x4 + restore(const TwoQubitWeylDecomposition& decomposition) { + return k1(decomposition) * can(decomposition) * k2(decomposition) * + globalPhaseFactor(decomposition); + } + + [[nodiscard]] static qfp + globalPhaseFactor(const TwoQubitWeylDecomposition& decomposition) { + return std::exp(IM * decomposition.globalPhase); + } + [[nodiscard]] static matrix4x4 + can(const TwoQubitWeylDecomposition& decomposition) { + return decomposition.getCanonicalMatrix(); + } + [[nodiscard]] static matrix4x4 + k1(const TwoQubitWeylDecomposition& decomposition) { + return helpers::kroneckerProduct(decomposition.k1l, decomposition.k1r); + } + [[nodiscard]] static matrix4x4 + k2(const TwoQubitWeylDecomposition& decomposition) { + return helpers::kroneckerProduct(decomposition.k2l, decomposition.k2r); + } +}; + +TEST_P(WeylDecompositionTest, TestExact) { + const auto& originalMatrix = GetParam(); + auto decomposition = TwoQubitWeylDecomposition::create(originalMatrix, 1.0); + auto restoredMatrix = restore(decomposition); + + EXPECT_TRUE(restoredMatrix.isApprox(originalMatrix)) + << "RESULT:\n" + << restoredMatrix << '\n'; +} + +TEST_P(WeylDecompositionTest, TestApproximation) { + const auto& originalMatrix = GetParam(); + auto decomposition = + TwoQubitWeylDecomposition::create(originalMatrix, 1.0 - 1e-12); + auto restoredMatrix = restore(decomposition); + + EXPECT_TRUE(restoredMatrix.isApprox(originalMatrix)) + << "RESULT:\n" + << restoredMatrix << '\n'; +} + +TEST(WeylDecompositionTest, Random) { + auto stopTime = std::chrono::steady_clock::now() + std::chrono::seconds{10}; + auto iterations = 0; + while (std::chrono::steady_clock::now() < stopTime) { + auto originalMatrix = randomUnitaryMatrix(); + auto decomposition = + TwoQubitWeylDecomposition::create(originalMatrix, 1.0 - 1e-12); + auto restoredMatrix = WeylDecompositionTest::restore(decomposition); + + EXPECT_TRUE(restoredMatrix.isApprox(originalMatrix)) + << "ORIGINAL:\n" + << originalMatrix << '\n' + << "RESULT:\n" + << restoredMatrix << '\n'; + ++iterations; + } + + RecordProperty("iterations", iterations); + std::cerr << "Iterations: " << iterations << '\n'; +} + +INSTANTIATE_TEST_CASE_P( + SingleQubitMatrices, WeylDecompositionTest, + ::testing::Values(helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE), + helpers::kroneckerProduct(rzMatrix(1.0), ryMatrix(3.1)), + helpers::kroneckerProduct(IDENTITY_GATE, rxMatrix(0.1)))); + +INSTANTIATE_TEST_CASE_P( + TwoQubitMatrices, WeylDecompositionTest, + ::testing::Values( + rzzMatrix(2.0), ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0), + TwoQubitWeylDecomposition{1.5, -0.2, 0.0}.getCanonicalMatrix() * + helpers::kroneckerProduct(rxMatrix(1.0), IDENTITY_GATE), + helpers::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * + TwoQubitWeylDecomposition{1.1, 0.2, 3.0}.getCanonicalMatrix() * + helpers::kroneckerProduct(rxMatrix(1.0), IDENTITY_GATE), + helpers::kroneckerProduct(H_GATE, IPZ) * + getTwoQubitMatrix({.type = qc::X, .qubitId = {0, 1}}) * + helpers::kroneckerProduct(IPX, IPY))); From 3453d2c4852e98809c8bc77ebc20e566bb391770 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 26 Nov 2025 21:26:56 +0100 Subject: [PATCH 113/237] fix linter issues --- .../decomposition/test_weyl_decomposition.cpp | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/mlir/unittests/decomposition/test_weyl_decomposition.cpp b/mlir/unittests/decomposition/test_weyl_decomposition.cpp index 4ec8969565..61865f72f2 100644 --- a/mlir/unittests/decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/decomposition/test_weyl_decomposition.cpp @@ -8,9 +8,18 @@ * Licensed under the MIT License */ +#include "ir/operations/OpType.hpp" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h" +#include +#include +#include +#include +#include #include +#include using namespace mqt::ir::opt; using namespace mqt::ir::opt::decomposition; @@ -23,13 +32,21 @@ namespace { std::srand(123456UL); return true; }(); - matrix4x4 randomMatrix = matrix4x4::Random(); + const matrix4x4 randomMatrix = matrix4x4::Random(); Eigen::HouseholderQR qr{}; qr.compute(randomMatrix); - matrix4x4 unitaryMatrix = qr.householderQ(); + const matrix4x4 unitaryMatrix = qr.householderQ(); assert(helpers::isUnitaryMatrix(unitaryMatrix)); return unitaryMatrix; } + +[[nodiscard]] matrix4x4 canonicalGate(fp a, fp b, fp c) { + TwoQubitWeylDecomposition tmp{}; + tmp.a = a; + tmp.b = b; + tmp.c = c; + return tmp.getCanonicalMatrix(); +} } // namespace class WeylDecompositionTest : public testing::TestWithParam { @@ -110,10 +127,10 @@ INSTANTIATE_TEST_CASE_P( TwoQubitMatrices, WeylDecompositionTest, ::testing::Values( rzzMatrix(2.0), ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0), - TwoQubitWeylDecomposition{1.5, -0.2, 0.0}.getCanonicalMatrix() * + canonicalGate(1.5, -0.2, 0.0) * helpers::kroneckerProduct(rxMatrix(1.0), IDENTITY_GATE), helpers::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * - TwoQubitWeylDecomposition{1.1, 0.2, 3.0}.getCanonicalMatrix() * + canonicalGate(1.1, 0.2, 3.0) * helpers::kroneckerProduct(rxMatrix(1.0), IDENTITY_GATE), helpers::kroneckerProduct(H_GATE, IPZ) * getTwoQubitMatrix({.type = qc::X, .qubitId = {0, 1}}) * From 7f0d1f665f6b5c18e097d479977ba08e2fcde37c Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 26 Nov 2025 21:27:09 +0100 Subject: [PATCH 114/237] add tests for basis decomposer --- .../decomposition/test_basis_decomposer.cpp | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 mlir/unittests/decomposition/test_basis_decomposer.cpp diff --git a/mlir/unittests/decomposition/test_basis_decomposer.cpp b/mlir/unittests/decomposition/test_basis_decomposer.cpp new file mode 100644 index 0000000000..c7354f5839 --- /dev/null +++ b/mlir/unittests/decomposition/test_basis_decomposer.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "ir/operations/OpType.hpp" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace mqt::ir::opt; +using namespace mqt::ir::opt::decomposition; + +namespace { +[[nodiscard]] matrix4x4 randomUnitaryMatrix() { + [[maybe_unused]] static auto initializeRandom = []() { + // Eigen uses std::rand() internally, use fixed seed for deterministic + // testing behavior + std::srand(123456UL); + return true; + }(); + const matrix4x4 randomMatrix = matrix4x4::Random(); + Eigen::HouseholderQR qr{}; + qr.compute(randomMatrix); + const matrix4x4 unitaryMatrix = qr.householderQ(); + assert(helpers::isUnitaryMatrix(unitaryMatrix)); + return unitaryMatrix; +} + +[[nodiscard]] matrix4x4 canonicalGate(fp a, fp b, fp c) { + TwoQubitWeylDecomposition tmp{}; + tmp.a = a; + tmp.b = b; + tmp.c = c; + return tmp.getCanonicalMatrix(); +} +} // namespace + +class BasisDecomposerTest + : public testing::TestWithParam< + std::tuple, matrix4x4>> { +public: + void SetUp() override { + basisGate = std::get<0>(GetParam()); + eulerBases = std::get<1>(GetParam()); + target = std::get<2>(GetParam()); + targetDecomposition = TwoQubitWeylDecomposition::create(target, 1.0); + } + + [[nodiscard]] static matrix4x4 restore(const TwoQubitGateSequence& sequence) { + matrix4x4 matrix = matrix4x4::Identity(); + for (auto&& gate : sequence.gates) { + matrix = getTwoQubitMatrix(gate) * matrix; + } + + matrix *= std::exp(IM * sequence.globalPhase); + return matrix; + } + +protected: + matrix4x4 target; + Gate basisGate; + llvm::SmallVector eulerBases; + TwoQubitWeylDecomposition targetDecomposition; +}; + +TEST_P(BasisDecomposerTest, TestExact) { + const auto& originalMatrix = target; + auto decomposer = TwoQubitBasisDecomposer::create(basisGate, 1.0); + auto decomposedSequence = decomposer.twoQubitDecompose( + targetDecomposition, eulerBases, 1.0, false, std::nullopt); + + ASSERT_TRUE(decomposedSequence.has_value()); + + auto restoredMatrix = restore(*decomposedSequence); + + EXPECT_TRUE(restoredMatrix.isApprox(originalMatrix)) + << "RESULT:\n" + << restoredMatrix << '\n'; +} + +TEST_P(BasisDecomposerTest, TestApproximation) { + const auto& originalMatrix = target; + auto decomposer = TwoQubitBasisDecomposer::create(basisGate, 1.0 - 1e-12); + auto decomposedSequence = decomposer.twoQubitDecompose( + targetDecomposition, eulerBases, 1.0 - 1e-12, true, std::nullopt); + + ASSERT_TRUE(decomposedSequence.has_value()); + + auto restoredMatrix = restore(*decomposedSequence); + + EXPECT_TRUE(restoredMatrix.isApprox(originalMatrix)) + << "RESULT:\n" + << restoredMatrix << '\n'; +} + +TEST(BasisDecomposerTest, Random) { + auto stopTime = std::chrono::steady_clock::now() + std::chrono::seconds{10}; + auto iterations = 0; + while (std::chrono::steady_clock::now() < stopTime) { + auto originalMatrix = randomUnitaryMatrix(); + + auto targetDecomposition = + TwoQubitWeylDecomposition::create(originalMatrix, 1.0); + + Gate basisGate{.type = qc::X, .qubitId = {0, 1}}; + llvm::SmallVector eulerBases = {EulerBasis::XYX, + EulerBasis::ZXZ}; + auto decomposer = TwoQubitBasisDecomposer::create(basisGate, 1.0); + auto decomposedSequence = decomposer.twoQubitDecompose( + targetDecomposition, eulerBases, 1.0, true, std::nullopt); + + ASSERT_TRUE(decomposedSequence.has_value()); + + auto restoredMatrix = BasisDecomposerTest::restore(*decomposedSequence); + + EXPECT_TRUE(restoredMatrix.isApprox(originalMatrix)) + << "ORIGINAL:\n" + << originalMatrix << '\n' + << "RESULT:\n" + << restoredMatrix << '\n'; + ++iterations; + } + + RecordProperty("iterations", iterations); + std::cerr << "Iterations: " << iterations << '\n'; +} + +INSTANTIATE_TEST_CASE_P( + SingleQubitMatrices, BasisDecomposerTest, + testing::Combine( + // basis gates + testing::Values(Gate{.type = qc::X, .qubitId = {0, 1}}, + Gate{.type = qc::X, .qubitId = {1, 0}}), + // sets of euler bases + testing::Values(llvm::SmallVector{EulerBasis::ZYZ}, + llvm::SmallVector{ + EulerBasis::ZYZ, EulerBasis::ZXZ, EulerBasis::XYX, + EulerBasis::XZX}, + llvm::SmallVector{EulerBasis::XZX}), + // targets to be decomposed + testing::Values(helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE), + helpers::kroneckerProduct(rzMatrix(1.0), ryMatrix(3.1)), + helpers::kroneckerProduct(IDENTITY_GATE, + rxMatrix(0.1))))); + +INSTANTIATE_TEST_CASE_P( + TwoQubitMatrices, BasisDecomposerTest, + testing::Combine( + // basis gates + testing::Values(Gate{.type = qc::X, .qubitId = {0, 1}}, + Gate{.type = qc::X, .qubitId = {1, 0}}), + // sets of euler bases + testing::Values(llvm::SmallVector{EulerBasis::ZYZ}, + llvm::SmallVector{ + EulerBasis::ZYZ, EulerBasis::ZXZ, EulerBasis::XYX, + EulerBasis::XZX}, + llvm::SmallVector{EulerBasis::XZX}), + // targets to be decomposed + ::testing::Values( + rzzMatrix(2.0), ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0), + canonicalGate(1.5, -0.2, 0.0) * + helpers::kroneckerProduct(rxMatrix(1.0), IDENTITY_GATE), + helpers::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * + canonicalGate(1.1, 0.2, 3.0) * + helpers::kroneckerProduct(rxMatrix(1.0), IDENTITY_GATE), + helpers::kroneckerProduct(H_GATE, IPZ) * + getTwoQubitMatrix({.type = qc::X, .qubitId = {0, 1}}) * + helpers::kroneckerProduct(IPX, IPY)))); From e904dc197b0fb1d84975ce7909d74a4e78d052fc Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 27 Nov 2025 15:03:28 +0100 Subject: [PATCH 115/237] fix linter issues --- .../Decomposition/BasisDecomposer.h | 3 +- .../Decomposition/EulerDecomposition.h | 3 +- .../Transforms/Decomposition/GateSequence.h | 4 +-- .../Transforms/GateDecompositionPattern.cpp | 3 +- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 6 ++-- .../decomposition/test_basis_decomposer.cpp | 30 ++++++++++++------- .../decomposition/test_weyl_decomposition.cpp | 5 ++-- 7 files changed, 31 insertions(+), 23 deletions(-) diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h index 6a41aec896..8e755647f6 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h @@ -18,7 +18,7 @@ #include "WeylDecomposition.h" #include "ir/Definitions.hpp" -#include // NOLINT(misc-include-cleaner) +#include #include #include #include @@ -36,7 +36,6 @@ #include #include #include -#include // TODO: unstable, NOLINT(misc-include-cleaner) #include namespace mqt::ir::opt::decomposition { diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerDecomposition.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerDecomposition.h index bd99a2d301..df9aa21e0a 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerDecomposition.h +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerDecomposition.h @@ -16,7 +16,7 @@ #include "ir/Definitions.hpp" #include "ir/operations/OpType.hpp" -#include // NOLINT(misc-include-cleaner) +#include #include #include #include @@ -31,7 +31,6 @@ #include #include #include -#include // TODO: unstable, NOLINT(misc-include-cleaner) namespace mqt::ir::opt::decomposition { diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h index 98b1676a96..8116e366d7 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h @@ -10,13 +10,14 @@ #pragma once +#include "EulerBasis.h" #include "Gate.h" #include "Helpers.h" #include "UnitaryMatrices.h" #include "ir/operations/OpType.hpp" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h" -#include // NOLINT(misc-include-cleaner) +#include #include #include #include @@ -30,7 +31,6 @@ #include #include #include -#include // TODO: unstable, NOLINT(misc-include-cleaner) namespace mqt::ir::opt::decomposition { /** diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 32aa3c5a7c..dcd71ce0fc 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -19,7 +19,7 @@ #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h" #include "mlir/Dialect/MQTOpt/Transforms/Passes.h" -#include // NOLINT(misc-include-cleaner) +#include #include #include #include @@ -36,7 +36,6 @@ #include #include #include -#include // TODO: unstable, NOLINT(misc-include-cleaner) #include namespace mqt::ir::opt { diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h index bb0fa3357e..55cd761704 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h @@ -14,8 +14,8 @@ #include "ir/operations/OpType.hpp" #include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include // NOLINT(misc-include-cleaner) -#include // NOLINT(misc-include-cleaner) +#include +#include #include #include #include @@ -28,7 +28,7 @@ #include #include #include -#include // TODO: unstable, NOLINT(misc-include-cleaner) +#include // TODO: unstable namespace mqt::ir::opt { using fp = qc::fp; diff --git a/mlir/unittests/decomposition/test_basis_decomposer.cpp b/mlir/unittests/decomposition/test_basis_decomposer.cpp index c7354f5839..a221d70b36 100644 --- a/mlir/unittests/decomposition/test_basis_decomposer.cpp +++ b/mlir/unittests/decomposition/test_basis_decomposer.cpp @@ -10,8 +10,11 @@ #include "ir/operations/OpType.hpp" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Gate.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h" #include #include @@ -20,6 +23,9 @@ #include #include #include +#include +#include +#include using namespace mqt::ir::opt; using namespace mqt::ir::opt::decomposition; @@ -33,7 +39,7 @@ namespace { return true; }(); const matrix4x4 randomMatrix = matrix4x4::Random(); - Eigen::HouseholderQR qr{}; + Eigen::HouseholderQR qr{}; // NOLINT(misc-include-cleaner) qr.compute(randomMatrix); const matrix4x4 unitaryMatrix = qr.householderQ(); assert(helpers::isUnitaryMatrix(unitaryMatrix)); @@ -110,15 +116,16 @@ TEST_P(BasisDecomposerTest, TestApproximation) { TEST(BasisDecomposerTest, Random) { auto stopTime = std::chrono::steady_clock::now() + std::chrono::seconds{10}; auto iterations = 0; + + const Gate basisGate{.type = qc::X, .parameter = {}, .qubitId = {0, 1}}; + const llvm::SmallVector eulerBases = {EulerBasis::XYX, + EulerBasis::ZXZ}; + while (std::chrono::steady_clock::now() < stopTime) { auto originalMatrix = randomUnitaryMatrix(); auto targetDecomposition = TwoQubitWeylDecomposition::create(originalMatrix, 1.0); - - Gate basisGate{.type = qc::X, .qubitId = {0, 1}}; - llvm::SmallVector eulerBases = {EulerBasis::XYX, - EulerBasis::ZXZ}; auto decomposer = TwoQubitBasisDecomposer::create(basisGate, 1.0); auto decomposedSequence = decomposer.twoQubitDecompose( targetDecomposition, eulerBases, 1.0, true, std::nullopt); @@ -143,8 +150,9 @@ INSTANTIATE_TEST_CASE_P( SingleQubitMatrices, BasisDecomposerTest, testing::Combine( // basis gates - testing::Values(Gate{.type = qc::X, .qubitId = {0, 1}}, - Gate{.type = qc::X, .qubitId = {1, 0}}), + testing::Values(Gate{.type = qc::X, .parameter = {}, .qubitId = {0, 1}}, + Gate{ + .type = qc::X, .parameter = {}, .qubitId = {1, 0}}), // sets of euler bases testing::Values(llvm::SmallVector{EulerBasis::ZYZ}, llvm::SmallVector{ @@ -161,8 +169,9 @@ INSTANTIATE_TEST_CASE_P( TwoQubitMatrices, BasisDecomposerTest, testing::Combine( // basis gates - testing::Values(Gate{.type = qc::X, .qubitId = {0, 1}}, - Gate{.type = qc::X, .qubitId = {1, 0}}), + testing::Values(Gate{.type = qc::X, .parameter = {}, .qubitId = {0, 1}}, + Gate{ + .type = qc::X, .parameter = {}, .qubitId = {1, 0}}), // sets of euler bases testing::Values(llvm::SmallVector{EulerBasis::ZYZ}, llvm::SmallVector{ @@ -178,5 +187,6 @@ INSTANTIATE_TEST_CASE_P( canonicalGate(1.1, 0.2, 3.0) * helpers::kroneckerProduct(rxMatrix(1.0), IDENTITY_GATE), helpers::kroneckerProduct(H_GATE, IPZ) * - getTwoQubitMatrix({.type = qc::X, .qubitId = {0, 1}}) * + getTwoQubitMatrix( + {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * helpers::kroneckerProduct(IPX, IPY)))); diff --git a/mlir/unittests/decomposition/test_weyl_decomposition.cpp b/mlir/unittests/decomposition/test_weyl_decomposition.cpp index 61865f72f2..60e2a67825 100644 --- a/mlir/unittests/decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/decomposition/test_weyl_decomposition.cpp @@ -33,7 +33,7 @@ namespace { return true; }(); const matrix4x4 randomMatrix = matrix4x4::Random(); - Eigen::HouseholderQR qr{}; + Eigen::HouseholderQR qr{}; // NOLINT(misc-include-cleaner) qr.compute(randomMatrix); const matrix4x4 unitaryMatrix = qr.householderQ(); assert(helpers::isUnitaryMatrix(unitaryMatrix)); @@ -133,5 +133,6 @@ INSTANTIATE_TEST_CASE_P( canonicalGate(1.1, 0.2, 3.0) * helpers::kroneckerProduct(rxMatrix(1.0), IDENTITY_GATE), helpers::kroneckerProduct(H_GATE, IPZ) * - getTwoQubitMatrix({.type = qc::X, .qubitId = {0, 1}}) * + getTwoQubitMatrix( + {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * helpers::kroneckerProduct(IPX, IPY))); From c586338771eac1c235de5faba8442ea2da7258ca Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 27 Nov 2025 15:03:45 +0100 Subject: [PATCH 116/237] clang-tidy: Add IgnoreHeader for include cleaner for Eigen --- .clang-tidy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.clang-tidy b/.clang-tidy index 8683b18a06..65f2796cd5 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -74,3 +74,5 @@ CheckOptions: value: CamelCase - key: readability-identifier-naming.VariableCase value: camelBack + - key: misc-include-cleaner.IgnoreHeaders + value: "Eigen/.*;unsupported/Eigen/.*" From f2efbcb2b31e6c385b5d79fa0539cb8735c22441 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 27 Nov 2025 15:08:30 +0100 Subject: [PATCH 117/237] remove exact values from MLIR test --- .../MQTOpt/Transforms/gate-decomposition.mlir | 122 +++--------------- 1 file changed, 20 insertions(+), 102 deletions(-) diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index 79510e6045..98ab5e6aed 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -280,45 +280,16 @@ module { module { // CHECK-LABEL: func.func @testTwoBasisGateDecomposition func.func @testTwoBasisGateDecomposition() { - // CHECK: %[[C0:.*]] = arith.constant -1.5707963267 - // CHECK: %[[C1:.*]] = arith.constant -3.141592653 - // CHECK: %[[C2:.*]] = arith.constant 1.2502369157 - // CHECK: %[[C3:.*]] = arith.constant 1.8299117708 - // CHECK: %[[C4:.*]] = arith.constant 2.8733113535 - // CHECK: %[[C5:.*]] = arith.constant 3.0097427712 - // CHECK: %[[C6:.*]] = arith.constant 0.2614370492 - // CHECK: %[[C7:.*]] = arith.constant -0.131849882 - // CHECK: %[[C8:.*]] = arith.constant 2.500000e+00 - // CHECK: %[[C9:.*]] = arith.constant -1.570796326 - // CHECK: %[[C10:.*]] = arith.constant 1.570796326 - // CHECK: %[[C11:.*]] = arith.constant 0.785398163 - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK-NOT: mqtopt.gphase(%[[ANY:.*]]) - // CHECK: %[[Q0_1:.*]] = mqtopt.ry(%[[C11]]) %[[Q0_0]] - // CHECK: %[[Q0_2:.*]] = mqtopt.rz(%[[C10]]) %[[Q0_1]] - // CHECK: %[[Q1_1:.*]] = mqtopt.rx(%[[C9]]) %[[Q1_0]] - // CHECK: %[[Q1_2:.*]] = mqtopt.ry(%[[C8]]) %[[Q1_1]] - // CHECK: %[[Q0_3:.*]], %[[Q1_3:.*]] = mqtopt.x() %[[Q0_2]] ctrl %[[Q1_2]] - // CHECK: %[[Q0_4:.*]] = mqtopt.rz(%[[C7]]) %[[Q0_3]] - // CHECK: %[[Q0_5:.*]] = mqtopt.ry(%[[C6]]) %[[Q0_4]] - // CHECK: %[[Q0_6:.*]] = mqtopt.rz(%[[C5]]) %[[Q0_5]] - // CHECK: %[[Q1_4:.*]] = mqtopt.rz(%[[C10]]) %[[Q1_3]] - // CHECK: %[[Q1_5:.*]] = mqtopt.ry(%[[C10]]) %[[Q1_4]] - // CHECK: %[[Q0_7:.*]], %[[Q1_6:.*]] = mqtopt.x() %[[Q0_6]] ctrl %[[Q1_5]] - // CHECK: %[[Q0_8:.*]] = mqtopt.rz(%[[C4]]) %[[Q0_7]] - // CHECK: %[[Q0_9:.*]] = mqtopt.ry(%[[C3]]) %[[Q0_8]] - // CHECK: %[[Q0_10:.*]] = mqtopt.rz(%[[C2]]) %[[Q0_9]] - // CHECK: %[[Q1_7:.*]] = mqtopt.rz(%[[C1]]) %[[Q1_6]] - // CHECK: %[[Q1_8:.*]] = mqtopt.rx(%[[C0]]) %[[Q1_7]] + // CHECK-NOT: mqtopt.gphase + // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] + // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] + // CHECK-NOT: mqtopt.x() - // ensure no other operations are inserted - // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) - - // CHECK: mqtopt.deallocQubit %[[Q0_10]] - // CHECK: mqtopt.deallocQubit %[[Q1_8]] + // CHECK: mqtopt.deallocQubit %[[ANY:.*]] + // CHECK: mqtopt.deallocQubit %[[ANY:.*]] %cst0 = arith.constant 2.5 : f64 %cst1 = arith.constant 1.2 : f64 @@ -352,56 +323,17 @@ module { module { // CHECK-LABEL: func.func @testThreeBasisGateDecomposition func.func @testThreeBasisGateDecomposition() { - // CHECK: %[[C0:.*]] = arith.constant 1.00543528660 - // CHECK: %[[C1:.*]] = arith.constant -2.3561944901 - // CHECK: %[[C2:.*]] = arith.constant 0.85849590894 - // CHECK: %[[C3:.*]] = arith.constant 0.43624604946 - // CHECK: %[[C4:.*]] = arith.constant 2.43419171936 - // CHECK: %[[C5:.*]] = arith.constant -0.6154797086 - // CHECK: %[[C6:.*]] = arith.constant 1.04719755119 - // CHECK: %[[C7:.*]] = arith.constant 2.52611294491 - // CHECK: %[[C8:.*]] = arith.constant -0.2593805121 - // CHECK: %[[C9:.*]] = arith.constant 1.57079632679 - // CHECK: %[[C10:.*]] = arith.constant -2.52611294491 - // CHECK: %[[C11:.*]] = arith.constant 2.094395102393 - // CHECK: %[[C12:.*]] = arith.constant -0.61547970867 - // CHECK: %[[C13:.*]] = arith.constant 0.694804217319 - // CHECK: %[[C14:.*]] = arith.constant 0.220207934068 - // CHECK: %[[C15:.*]] = arith.constant 1.125358392049 - // CHECK: %[[C16:.*]] = arith.constant -0.03425899788 - // CHECK: %[[C17:.*]] = arith.constant -1.57079632679 - // CHECK: %[[C18:.*]] = arith.constant -2.39270110306 - // CHECK: %[[C19:.*]] = arith.constant 5.497787143782 - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: mqtopt.gphase(%[[C19]]) - // CHECK: %[[Q0_1:.*]] = mqtopt.ry(%[[C18]]) %[[Q0_0]] - // CHECK: %[[Q0_2:.*]] = mqtopt.rx(%[[C17]]) %[[Q0_1]] - // CHECK: %[[Q1_1:.*]] = mqtopt.rz(%[[C16]]) %[[Q1_0]] - // CHECK: %[[Q1_2:.*]] = mqtopt.ry(%[[C15]]) %[[Q1_1]] - // CHECK: %[[Q1_3:.*]] = mqtopt.rz(%[[C14]]) %[[Q1_2]] - // CHECK: %[[Q1_4:.*]], %[[Q0_3:.*]] = mqtopt.x() %[[Q1_3]] ctrl %[[Q0_2]] - // CHECK: %[[Q0_4:.*]] = mqtopt.rx(%[[C13]]) %[[Q0_3]] - // CHECK: %[[Q1_5:.*]] = mqtopt.rz(%[[C12]]) %[[Q1_4]] - // CHECK: %[[Q1_6:.*]] = mqtopt.ry(%[[C11]]) %[[Q1_5]] - // CHECK: %[[Q1_7:.*]] = mqtopt.rz(%[[C10]]) %[[Q1_6]] - // CHECK: %[[Q1_8:.*]], %[[Q0_5:.*]] = mqtopt.x() %[[Q1_7]] ctrl %[[Q0_4]] - // CHECK: %[[Q0_6:.*]] = mqtopt.rz(%[[C9]]) %[[Q0_5]] - // CHECK: %[[Q0_7:.*]] = mqtopt.ry(%[[C8]]) %[[Q0_6]] - // CHECK: %[[Q1_9:.*]] = mqtopt.rz(%[[C7]]) %[[Q1_8]] - // CHECK: %[[Q1_10:.*]] = mqtopt.ry(%[[C6]]) %[[Q1_9]] - // CHECK: %[[Q1_11:.*]] = mqtopt.rz(%[[C5]]) %[[Q1_10]] - // CHECK: %[[Q1_12:.*]], %[[Q0_8:.*]] = mqtopt.x() %[[Q1_11]] ctrl %[[Q0_7]] - // CHECK: %[[Q0_9:.*]] = mqtopt.rz(%[[C4]]) %[[Q0_8]] - // CHECK: %[[Q0_10:.*]] = mqtopt.ry(%[[C3]]) %[[Q0_9]] - // CHECK: %[[Q0_11:.*]] = mqtopt.rz(%[[C2]]) %[[Q0_10]] - // CHECK: %[[Q1_13:.*]] = mqtopt.rx(%[[C1]]) %[[Q1_12]] - // CHECK: %[[Q1_14:.*]] = mqtopt.rz(%[[C0]]) %[[Q1_13]] - - // CHECK: mqtopt.deallocQubit %[[Q0_11]] - // CHECK: mqtopt.deallocQubit %[[Q1_14]] + // CHECK: mqtopt.gphase(%[[ANY:.*]]) + // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] + // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] + // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] + // CHECK-NOT: mqtopt.x() + + // CHECK: mqtopt.deallocQubit %[[ANY:.*]] + // CHECK: mqtopt.deallocQubit %[[ANY:.*]] %cst0 = arith.constant 2.5 : f64 %cst1 = arith.constant 1.2 : f64 @@ -487,30 +419,16 @@ module { module { // CHECK-LABEL: func.func @testSeriesSingleQubitBacktracking func.func @testSeriesSingleQubitBacktracking() { - // CHECK: %[[C0:.*]] = arith.constant -2.4269908 - // CHECK: %[[C1:.*]] = arith.constant 3.14159265 - // CHECK: %[[C2:.*]] = arith.constant 0.85619449 - // CHECK: %[[C3:.*]] = arith.constant -2.3561944 - // CHECK: %[[C4:.*]] = arith.constant -1.5707963 - // CHECK: %[[C5:.*]] = arith.constant -1.5707963 - // CHECK: %[[C6:.*]] = arith.constant 2.35619449 - // CHECK: %[[C7:.*]] = arith.constant 3.1415926 - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: mqtopt.gphase(%[[C7]]) - // CHECK: %[[Q0_1:.*]] = mqtopt.rz(%[[C6]]) %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C5]]) %[[Q0_1]] : !mqtopt.Qubit - // CHECK: %[[Q1_1:.*]] = mqtopt.ry(%[[C4]]) %[[Q1_0]] : !mqtopt.Qubit - // CHECK: %[[Q1_2:.*]] = mqtopt.rz(%[[C3]]) %[[Q1_1]] : !mqtopt.Qubit - // CHECK: %[[Q0_3:.*]], %[[Q1_3:.*]] = mqtopt.x() %[[Q0_2]] ctrl %[[Q1_2]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_4:.*]] = mqtopt.rx(%[[C2]]) %[[Q0_3]] : !mqtopt.Qubit - // CHECK: %[[Q0_5:.*]] = mqtopt.ry(%[[C1]]) %[[Q0_4]] : !mqtopt.Qubit - // CHECK: %[[Q1_4:.*]] = mqtopt.rz(%[[C0]]) %[[Q1_3]] : !mqtopt.Qubit + // CHECK: mqtopt.gphase(%[[ANY:.*]]) + // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] + // CHECK-NOT: mqtopt.x() + // CHECK-NOT: mqtopt.h() - // CHECK: mqtopt.deallocQubit %[[Q0_5]] - // CHECK: mqtopt.deallocQubit %[[Q1_4]] + // CHECK: mqtopt.deallocQubit %[[ANY:.*]] + // CHECK: mqtopt.deallocQubit %[[ANY:.*]] %q0_0 = mqtopt.allocQubit %q1_0 = mqtopt.allocQubit From 78daaba0ccc9ac4675b388ec860a11766e715b31 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 27 Nov 2025 15:58:15 +0100 Subject: [PATCH 118/237] fix mlir test/linter issues --- mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir | 3 --- mlir/unittests/decomposition/test_basis_decomposer.cpp | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index 98ab5e6aed..6decb1c317 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -283,7 +283,6 @@ module { // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK-NOT: mqtopt.gphase // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] // CHECK-NOT: mqtopt.x() @@ -326,7 +325,6 @@ module { // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: mqtopt.gphase(%[[ANY:.*]]) // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] @@ -422,7 +420,6 @@ module { // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: mqtopt.gphase(%[[ANY:.*]]) // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] // CHECK-NOT: mqtopt.x() // CHECK-NOT: mqtopt.h() diff --git a/mlir/unittests/decomposition/test_basis_decomposer.cpp b/mlir/unittests/decomposition/test_basis_decomposer.cpp index a221d70b36..7eaeb2a77c 100644 --- a/mlir/unittests/decomposition/test_basis_decomposer.cpp +++ b/mlir/unittests/decomposition/test_basis_decomposer.cpp @@ -10,6 +10,7 @@ #include "ir/operations/OpType.hpp" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerBasis.h" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Gate.h" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h" From 741660e70b014d294faa2fa37a8fc12e0881d4ff Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 27 Nov 2025 16:00:04 +0100 Subject: [PATCH 119/237] remove std::numbers because not supported by all compilers in CI llvm::numbers could be used instead --- .../Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h index 4393e67dd4..6d9be79b15 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h @@ -68,7 +68,8 @@ inline matrix4x4 rzzMatrix(const fp theta) { inline matrix2x2 pMatrix(const fp lambda) { return matrix2x2{{1, 0}, {0, {std::cos(lambda), std::sin(lambda)}}}; } -inline constexpr auto SQRT2 = std::numbers::sqrt2_v; +inline constexpr auto SQRT2 = + static_cast(1.414213562373095048801688724209698079L); inline constexpr auto FRAC1_SQRT2 = static_cast( 0.707106781186547524400844362104849039284835937688474036588L); const matrix2x2 IDENTITY_GATE = matrix2x2::Identity(); From 9a6c691e464776ceb7de93c66edaa51ae84eeac9 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 27 Nov 2025 16:01:40 +0100 Subject: [PATCH 120/237] remove unused Helpers.h from src dir --- mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h | 202 ------------------- 1 file changed, 202 deletions(-) delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h b/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h deleted file mode 100644 index 55cd761704..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Helpers.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM - * Copyright (c) 2025 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include "ir/Definitions.hpp" -#include "ir/operations/OpType.hpp" -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // TODO: unstable - -namespace mqt::ir::opt { -using fp = qc::fp; -using qfp = std::complex; -// NOLINTBEGIN(misc-include-cleaner) -using matrix2x2 = Eigen::Matrix2; -using matrix4x4 = Eigen::Matrix4; -using rmatrix4x4 = Eigen::Matrix4; -using diagonal4x4 = Eigen::Vector; -using rdiagonal4x4 = Eigen::Vector; -// NOLINTEND(misc-include-cleaner) - -constexpr qfp C_ZERO{0., 0.}; -constexpr qfp C_ONE{1., 0.}; -constexpr qfp C_M_ONE{-1., 0.}; -constexpr qfp IM{0., 1.}; -constexpr qfp M_IM{0., -1.}; - -} // namespace mqt::ir::opt - -namespace mqt::ir::opt::helpers { - -std::optional mlirValueToFp(mlir::Value value); - -template -std::optional performMlirFloatBinaryOp(mlir::Value value, Func&& func) { - if (auto op = value.getDefiningOp()) { - auto lhs = mlirValueToFp(op.getLhs()); - auto rhs = mlirValueToFp(op.getRhs()); - if (lhs && rhs) { - return std::invoke(std::forward(func), *lhs, *rhs); - } - } - return std::nullopt; -} - -template -std::optional performMlirFloatUnaryOp(mlir::Value value, Func&& func) { - if (auto op = value.getDefiningOp()) { - if (auto operand = mlirValueToFp(op.getOperand())) { - return std::invoke(std::forward(func), *operand); - } - } - return std::nullopt; -} - -inline std::optional mlirValueToFp(mlir::Value value) { - if (auto op = value.getDefiningOp()) { - if (auto attr = llvm::dyn_cast(op.getValue())) { - return attr.getValueAsDouble(); - } - return std::nullopt; - } - if (auto result = performMlirFloatUnaryOp( - value, [](fp a) { return -a; })) { - return result; - } - if (auto result = performMlirFloatUnaryOp( - value, [](fp a) { return a; })) { - return result; - } - if (auto result = performMlirFloatUnaryOp( - value, [](fp a) { return a; })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return std::max(a, b); })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return std::max(a, b); })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return std::min(a, b); })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return std::min(a, b); })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return std::fmod(a, b); })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return a + b; })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return a * b; })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return a / b; })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return a - b; })) { - return result; - } - return std::nullopt; -} - -[[nodiscard]] inline llvm::SmallVector -getParameters(UnitaryInterface op) { - llvm::SmallVector parameters; - for (auto&& param : op.getParams()) { - if (auto value = helpers::mlirValueToFp(param)) { - parameters.push_back(*value); - } - } - return parameters; -} - -[[nodiscard]] inline qc::OpType getQcType(UnitaryInterface op) { - try { - const std::string type = op->getName().stripDialect().str(); - return qc::opTypeFromString(type); - } catch (const std::invalid_argument& /*exception*/) { - return qc::OpType::None; - } -} - -[[nodiscard]] inline bool isSingleQubitOperation(UnitaryInterface op) { - auto&& inQubits = op.getInQubits(); - auto&& outQubits = op.getOutQubits(); - const bool isSingleQubitOp = - inQubits.size() == 1 && outQubits.size() == 1 && !op.isControlled(); - return isSingleQubitOp; -} - -[[nodiscard]] inline bool isTwoQubitOperation(UnitaryInterface op) { - auto&& inQubits = op.getInQubits(); - auto&& inPosCtrlQubits = op.getPosCtrlInQubits(); - auto&& inNegCtrlQubits = op.getNegCtrlInQubits(); - auto inQubitSize = - inQubits.size() + inPosCtrlQubits.size() + inNegCtrlQubits.size(); - auto&& outQubits = op.getOutQubits(); - auto&& outPosCtrlQubits = op.getPosCtrlOutQubits(); - auto&& outNegCtrlQubits = op.getNegCtrlOutQubits(); - auto outQubitSize = - outQubits.size() + outPosCtrlQubits.size() + outNegCtrlQubits.size(); - const bool isTwoQubitOp = inQubitSize == 2 && outQubitSize == 2; - return isTwoQubitOp; -} - -// NOLINTBEGIN(misc-include-cleaner) -template -inline Eigen::Matrix4 kroneckerProduct(const Eigen::Matrix2& lhs, - const Eigen::Matrix2& rhs) { - return Eigen::kroneckerProduct(lhs, rhs); -} - -template -inline auto selfAdjointEvd(Eigen::Matrix a) { - Eigen::SelfAdjointEigenSolver s; - s.compute(a); // TODO: computeDirect is faster - auto vecs = s.eigenvectors().eval(); - auto vals = s.eigenvalues(); - return std::make_pair(vecs, vals); -} - -template -[[nodiscard]] static bool -isUnitaryMatrix(const Eigen::Matrix& matrix) { - return (matrix.transpose().conjugate() * matrix).isIdentity(); -} -// NOLINTEND(misc-include-cleaner) - -} // namespace mqt::ir::opt::helpers From ad8da2332888847d73ba53b0c2be79c00d83f940 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 27 Nov 2025 16:02:35 +0100 Subject: [PATCH 121/237] remove auto* because of windows build --- .../MQTOpt/Transforms/GateDecompositionPattern.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index dcd71ce0fc..634adc0176 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -233,7 +233,7 @@ struct GateDecompositionPattern final */ bool appendSingleQubitGate(UnitaryInterface nextGate) { auto operand = nextGate.getAllInQubits()[0]; - auto* it = llvm::find(outQubits, operand); + auto it = llvm::find(outQubits, operand); if (it == outQubits.end()) { throw std::logic_error{"Operand of single-qubit op and user of " "qubit is not in current outQubits"}; @@ -253,12 +253,12 @@ struct GateDecompositionPattern final auto opInQubits = nextGate.getAllInQubits(); auto&& firstOperand = opInQubits[0]; auto&& secondOperand = opInQubits[1]; - auto* firstQubitIt = llvm::find(outQubits, firstOperand); - auto* secondQubitIt = llvm::find(outQubits, secondOperand); + auto firstQubitIt = llvm::find(outQubits, firstOperand); + auto secondQubitIt = llvm::find(outQubits, secondOperand); if (firstQubitIt == outQubits.end() || secondQubitIt == outQubits.end()) { // another qubit is involved, series is finished (except there only // has been one qubit so far) - auto* it = llvm::find(outQubits, mlir::Value{}); + auto it = llvm::find(outQubits, mlir::Value{}); if (it == outQubits.end()) { return false; } From 1e89778c2e673575911f35410f4056c39c3036f2 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 28 Nov 2025 09:52:20 +0100 Subject: [PATCH 122/237] try to fix windows ARM ci --- .../Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h index e8632533c3..5ccb6b8782 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h @@ -472,7 +472,7 @@ struct TwoQubitWeylDecomposition { * global phase adjustment */ static std::tuple - decomposeTwoQubitProductGate(matrix4x4 specialUnitary) { + decomposeTwoQubitProductGate(const matrix4x4& specialUnitary) { // for alternative approaches, see // pennylane's math.decomposition.su2su2_to_tensor_products // or quantumflow.kronecker_decomposition From 854d9a089922133f047cd58fa5148a38780c0f91 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 28 Nov 2025 11:41:02 +0100 Subject: [PATCH 123/237] pass matrices by reference --- .../Decomposition/BasisDecomposer.h | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h index 8e755647f6..18b6f1b65d 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h @@ -317,26 +317,28 @@ class TwoQubitBasisDecomposer { } protected: + // NOLINTBEGIN(modernize-pass-by-value) /** * Constructs decomposer instance. */ TwoQubitBasisDecomposer( Gate basisGate, fp basisFidelity, - decomposition::TwoQubitWeylDecomposition basisDecomposer, - bool isSuperControlled, matrix2x2 u0l, matrix2x2 u0r, matrix2x2 u1l, - matrix2x2 u1ra, matrix2x2 u1rb, matrix2x2 u2la, matrix2x2 u2lb, - matrix2x2 u2ra, matrix2x2 u2rb, matrix2x2 u3l, matrix2x2 u3r, - matrix2x2 q0l, matrix2x2 q0r, matrix2x2 q1la, matrix2x2 q1lb, - matrix2x2 q1ra, matrix2x2 q1rb, matrix2x2 q2l, matrix2x2 q2r) + const decomposition::TwoQubitWeylDecomposition& basisDecomposer, + bool isSuperControlled, const matrix2x2& u0l, const matrix2x2& u0r, const matrix2x2& u1l, + const matrix2x2& u1ra, const matrix2x2& u1rb, const matrix2x2& u2la, const matrix2x2& u2lb, + const matrix2x2& u2ra, const matrix2x2& u2rb, const matrix2x2& u3l, const matrix2x2& u3r, + const matrix2x2& q0l, const matrix2x2& q0r, const matrix2x2& q1la, const matrix2x2& q1lb, + const matrix2x2& q1ra, const matrix2x2& q1rb, const matrix2x2& q2l, const matrix2x2& q2r) : basisGate{std::move(basisGate)}, basisFidelity{basisFidelity}, - basisDecomposer{std::move(basisDecomposer)}, - isSuperControlled{isSuperControlled}, u0l{std::move(u0l)}, - u0r{std::move(u0r)}, u1l{std::move(u1l)}, u1ra{std::move(u1ra)}, - u1rb{std::move(u1rb)}, u2la{std::move(u2la)}, u2lb{std::move(u2lb)}, - u2ra{std::move(u2ra)}, u2rb{std::move(u2rb)}, u3l{std::move(u3l)}, - u3r{std::move(u3r)}, q0l{std::move(q0l)}, q0r{std::move(q0r)}, - q1la{std::move(q1la)}, q1lb{std::move(q1lb)}, q1ra{std::move(q1ra)}, - q1rb{std::move(q1rb)}, q2l{std::move(q2l)}, q2r{std::move(q2r)} {} + basisDecomposer{basisDecomposer}, + isSuperControlled{isSuperControlled}, u0l{u0l}, + u0r{u0r}, u1l{u1l}, u1ra{u1ra}, + u1rb{u1rb}, u2la{u2la}, u2lb{u2lb}, + u2ra{u2ra}, u2rb{u2rb}, u3l{u3l}, + u3r{u3r}, q0l{q0l}, q0r{q0r}, + q1la{q1la}, q1lb{q1lb}, q1ra{q1ra}, + q1rb{q1rb}, q2l{q2l}, q2r{q2r} {} + // NOLINTEND(modernize-pass-by-value) /** * Calculate decompositions when no basis gate is required. From 25b377d89892dafc4d2d8b9394e154c2f72de4ce Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 28 Nov 2025 13:51:42 +0100 Subject: [PATCH 124/237] allow non-optimal vectorization --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index e8096ec2e8..1aa04e29f6 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,10 @@ FetchContent_Declare( GIT_TAG 3.4.1 GIT_SHALLOW TRUE) FetchContent_MakeAvailable(Eigen) +if(WIN32 AND ${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL ARM64) + message(STATUS "Enabling non-optimal vectorization in Eigen to avoid alignment issues") + add_definitions("-DEIGEN_MAX_STATIC_ALIGN_BYTES=0") +endif() option(BUILD_MQT_CORE_BINDINGS "Build the MQT Core Python bindings" OFF) if(BUILD_MQT_CORE_BINDINGS) From cd8ae96667aef5873028f9d7d8b332bfaa53acc1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:52:41 +0000 Subject: [PATCH 125/237] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Decomposition/BasisDecomposer.h | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h index 18b6f1b65d..11149787ed 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h @@ -324,20 +324,19 @@ class TwoQubitBasisDecomposer { TwoQubitBasisDecomposer( Gate basisGate, fp basisFidelity, const decomposition::TwoQubitWeylDecomposition& basisDecomposer, - bool isSuperControlled, const matrix2x2& u0l, const matrix2x2& u0r, const matrix2x2& u1l, - const matrix2x2& u1ra, const matrix2x2& u1rb, const matrix2x2& u2la, const matrix2x2& u2lb, - const matrix2x2& u2ra, const matrix2x2& u2rb, const matrix2x2& u3l, const matrix2x2& u3r, - const matrix2x2& q0l, const matrix2x2& q0r, const matrix2x2& q1la, const matrix2x2& q1lb, - const matrix2x2& q1ra, const matrix2x2& q1rb, const matrix2x2& q2l, const matrix2x2& q2r) + bool isSuperControlled, const matrix2x2& u0l, const matrix2x2& u0r, + const matrix2x2& u1l, const matrix2x2& u1ra, const matrix2x2& u1rb, + const matrix2x2& u2la, const matrix2x2& u2lb, const matrix2x2& u2ra, + const matrix2x2& u2rb, const matrix2x2& u3l, const matrix2x2& u3r, + const matrix2x2& q0l, const matrix2x2& q0r, const matrix2x2& q1la, + const matrix2x2& q1lb, const matrix2x2& q1ra, const matrix2x2& q1rb, + const matrix2x2& q2l, const matrix2x2& q2r) : basisGate{std::move(basisGate)}, basisFidelity{basisFidelity}, - basisDecomposer{basisDecomposer}, - isSuperControlled{isSuperControlled}, u0l{u0l}, - u0r{u0r}, u1l{u1l}, u1ra{u1ra}, - u1rb{u1rb}, u2la{u2la}, u2lb{u2lb}, - u2ra{u2ra}, u2rb{u2rb}, u3l{u3l}, - u3r{u3r}, q0l{q0l}, q0r{q0r}, - q1la{q1la}, q1lb{q1lb}, q1ra{q1ra}, - q1rb{q1rb}, q2l{q2l}, q2r{q2r} {} + basisDecomposer{basisDecomposer}, isSuperControlled{isSuperControlled}, + u0l{u0l}, u0r{u0r}, u1l{u1l}, u1ra{u1ra}, u1rb{u1rb}, u2la{u2la}, + u2lb{u2lb}, u2ra{u2ra}, u2rb{u2rb}, u3l{u3l}, u3r{u3r}, q0l{q0l}, + q0r{q0r}, q1la{q1la}, q1lb{q1lb}, q1ra{q1ra}, q1rb{q1rb}, q2l{q2l}, + q2r{q2r} {} // NOLINTEND(modernize-pass-by-value) /** From 2f634f552de9335c0198d88689ae528d1e660c25 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 28 Nov 2025 15:35:07 +0100 Subject: [PATCH 126/237] try to fix --- CMakeLists.txt | 2 +- .../MQTOpt/Transforms/GateDecompositionPattern.cpp | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1aa04e29f6..0f6473a6de 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ FetchContent_Declare( FetchContent_MakeAvailable(Eigen) if(WIN32 AND ${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL ARM64) message(STATUS "Enabling non-optimal vectorization in Eigen to avoid alignment issues") - add_definitions("-DEIGEN_MAX_STATIC_ALIGN_BYTES=0") + add_compile_definitions(EIGEN_DONT_ALIGN_STATICALLY) endif() option(BUILD_MQT_CORE_BINDINGS "Build the MQT Core Python bindings" OFF) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 634adc0176..d7023b7f1e 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -233,6 +233,7 @@ struct GateDecompositionPattern final */ bool appendSingleQubitGate(UnitaryInterface nextGate) { auto operand = nextGate.getAllInQubits()[0]; + // NOLINTNEXTLINE(readability-qualified-auto) auto it = llvm::find(outQubits, operand); if (it == outQubits.end()) { throw std::logic_error{"Operand of single-qubit op and user of " @@ -253,12 +254,15 @@ struct GateDecompositionPattern final auto opInQubits = nextGate.getAllInQubits(); auto&& firstOperand = opInQubits[0]; auto&& secondOperand = opInQubits[1]; - auto firstQubitIt = llvm::find(outQubits, firstOperand); - auto secondQubitIt = llvm::find(outQubits, secondOperand); + auto firstQubitIt = // NOLINT(readability-qualified-auto) + llvm::find(outQubits, firstOperand); + auto secondQubitIt = // NOLINT(readability-qualified-auto) + llvm::find(outQubits, secondOperand); if (firstQubitIt == outQubits.end() || secondQubitIt == outQubits.end()) { // another qubit is involved, series is finished (except there only // has been one qubit so far) - auto it = llvm::find(outQubits, mlir::Value{}); + auto it = // NOLINT(readability-qualified-auto) + llvm::find(outQubits, mlir::Value{}); if (it == outQubits.end()) { return false; } From f9b033d45823fb5aa5ade290b133fe17e13f523e Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 28 Nov 2025 21:26:13 +0100 Subject: [PATCH 127/237] integrate single-qubit decomposition --- .../Transforms/Decomposition/GateSequence.h | 2 +- .../Transforms/GateDecompositionPattern.cpp | 62 ++++++++++++++----- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h index 8116e366d7..7254c47ab0 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h @@ -59,7 +59,7 @@ struct QubitGateSequence { [[nodiscard]] std::size_t complexity() const { // TODO: add more sophisticated metric to determine complexity of // series/sequence - // TODO: caching mechanism? + // TODO: caching mechanism std::size_t c{}; for (auto&& gate : gates) { c += helpers::getComplexity(gate.type, gate.qubitId.size()); diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index d7023b7f1e..616405d9e3 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -76,28 +76,39 @@ struct GateDecompositionPattern final // too short return mlir::failure(); } + + std::optional bestSequence; + if (series.isSingleQubitSeries()) { // only a single-qubit series; // single-qubit euler decomposition is more efficient - return mlir::failure(); - } - - const matrix4x4 unitaryMatrix = series.getUnitaryMatrix(); - const auto targetDecomposition = - decomposition::TwoQubitWeylDecomposition::create(unitaryMatrix, - DEFAULT_FIDELITY); - - std::optional bestSequence; - for (const auto& decomposer : basisDecomposers) { - auto sequence = decomposer.twoQubitDecompose( - targetDecomposition, decomposerEulerBases, DEFAULT_FIDELITY, false, - std::nullopt); - if (sequence) { + const matrix2x2 unitaryMatrix = series.getSingleQubitUnitaryMatrix(); + for (auto&& eulerBasis : decomposerEulerBases) { + auto sequence = decomposition::EulerDecomposition::generateCircuit( + eulerBasis, unitaryMatrix, true, std::nullopt); if (!bestSequence || - sequence->complexity() < bestSequence->complexity()) { + sequence.complexity() < bestSequence->complexity()) { bestSequence = sequence; } } + } else { + // two-qubit series; perform two-qubit basis decomposition + const matrix4x4 unitaryMatrix = series.getUnitaryMatrix(); + const auto targetDecomposition = + decomposition::TwoQubitWeylDecomposition::create(unitaryMatrix, + DEFAULT_FIDELITY); + + for (const auto& decomposer : basisDecomposers) { + auto sequence = decomposer.twoQubitDecompose( + targetDecomposition, decomposerEulerBases, DEFAULT_FIDELITY, false, + std::nullopt); + if (sequence) { + if (!bestSequence || + sequence->complexity() < bestSequence->complexity()) { + bestSequence = sequence; + } + } + } } if (!bestSequence) { return mlir::failure(); @@ -189,6 +200,21 @@ struct GateDecompositionPattern final return result; } + [[nodiscard]] matrix2x2 getSingleQubitUnitaryMatrix() const { + auto unitaryMatrix = decomposition::IDENTITY_GATE; + for (auto&& gate : gates) { + auto gateMatrix = decomposition::getSingleQubitMatrix( + {.type = helpers::getQcType(gate.op), + .parameter = helpers::getParameters(gate.op), + .qubitId = gate.qubitIds}); + unitaryMatrix = gateMatrix * unitaryMatrix; + } + unitaryMatrix *= std::exp(IM * globalPhase); + + assert(helpers::isUnitaryMatrix(unitaryMatrix)); + return unitaryMatrix; + } + [[nodiscard]] matrix4x4 getUnitaryMatrix() const { matrix4x4 unitaryMatrix = helpers::kroneckerProduct( decomposition::IDENTITY_GATE, decomposition::IDENTITY_GATE); @@ -441,7 +467,11 @@ struct GateDecompositionPattern final assert((unitaryMatrix * std::exp(IM * sequence.globalPhase)) .isApprox(series.getUnitaryMatrix(), SANITY_CHECK_PRECISION)); - rewriter.replaceAllUsesWith(series.outQubits, inQubits); + if (series.isSingleQubitSeries()) { + rewriter.replaceAllUsesWith(series.outQubits[0], inQubits[0]); + } else { + rewriter.replaceAllUsesWith(series.outQubits, inQubits); + } for (auto&& gate : llvm::reverse(series.gates)) { rewriter.eraseOp(gate.op); } From aaab4b8ea5fe5e0a4970116fc9b9127b604c2915 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 28 Nov 2025 21:26:25 +0100 Subject: [PATCH 128/237] add tests for single-qubit decomposition --- .../MQTOpt/Transforms/gate-decomposition.mlir | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir index 6decb1c317..87f8665488 100644 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir @@ -449,3 +449,92 @@ module { return } } + +// ----- +// This test checks if a single-qubit series that is equal to the identity is fully removed. + +module { + // CHECK-LABEL: func.func @testNegationSeries + func.func @testNegationSeries() { + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit + + // CHECK-NOT: %[[ANY:.*]] = mqtopt.x + + // CHECK: mqtopt.deallocQubit %[[Q0_0]] + + %q0_0 = mqtopt.allocQubit + + %q0_1 = mqtopt.x() %q0_0 : !mqtopt.Qubit + %q0_2 = mqtopt.x() %q0_1 : !mqtopt.Qubit + %q0_3 = mqtopt.x() %q0_2 : !mqtopt.Qubit + %q0_4 = mqtopt.x() %q0_3 : !mqtopt.Qubit + %q0_5 = mqtopt.x() %q0_4 : !mqtopt.Qubit + %q0_6 = mqtopt.x() %q0_5 : !mqtopt.Qubit + + mqtopt.deallocQubit %q0_6 + + return + } +} + +// ----- +// This test checks if a single-qubit series with consecutive rotations will be simplified. + +module { + // CHECK-LABEL: func.func @testMergeRotationSeries + func.func @testMergeRotationSeries() { + // CHECK: %[[C_0:.*]] = arith.constant 2.000000e+00 + + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit + + // CHECK: %[[Q0_1:.*]] = mqtopt.rx(%[[C_0]]) %[[Q0_0]] + + // CHECK-NOT: mqtopt.[[ANY:.*]]([[ANY:.*]]) + + // CHECK: mqtopt.deallocQubit %[[Q0_1]] + + %q0_0 = mqtopt.allocQubit + + %c_0 = arith.constant 0.500000e+00 : f64 + %q0_1 = mqtopt.rx(%c_0) %q0_0 : !mqtopt.Qubit + %q0_2 = mqtopt.rx(%c_0) %q0_1 : !mqtopt.Qubit + %q0_3 = mqtopt.rx(%c_0) %q0_2 : !mqtopt.Qubit + %q0_4 = mqtopt.rx(%c_0) %q0_3 : !mqtopt.Qubit + + mqtopt.deallocQubit %q0_4 + + return + } +} + +// ----- +// This test checks if a single-qubit series that cannot be simplified remains as-is. + +module { + // CHECK-LABEL: func.func @testNoMergeSmallSeries + func.func @testNoMergeSmallSeries() { + // CHECK: %[[C_0:.*]] = arith.constant 1.000000e+00 + + // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit + + // CHECK: %[[Q0_1:.*]] = mqtopt.rx(%[[C_0]]) %[[Q0_0:.*]] + // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C_0]]) %[[Q0_1:.*]] + // CHECK: %[[Q0_3:.*]] = mqtopt.rz(%[[C_0]]) %[[Q0_2:.*]] + + // CHECK-NOT: %[[ANY:.*]] = mqtopt.rz + // CHECK-NOT: %[[ANY:.*]] = mqtopt.ry + + // CHECK: mqtopt.deallocQubit %[[Q0_3]] + + %q0_0 = mqtopt.allocQubit + + %c_0 = arith.constant 1.000000e+00 : f64 + %q0_1 = mqtopt.rx(%c_0) %q0_0 : !mqtopt.Qubit + %q0_2 = mqtopt.ry(%c_0) %q0_1 : !mqtopt.Qubit + %q0_3 = mqtopt.rz(%c_0) %q0_2 : !mqtopt.Qubit + + mqtopt.deallocQubit %q0_3 + + return + } +} From fa78cbb9ef00cd3925de7dc1e745b16709ca4dba Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 30 Nov 2025 11:53:18 +0100 Subject: [PATCH 129/237] add unittests for euler decomposition --- .../test_euler_decomposition.cpp | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 mlir/unittests/decomposition/test_euler_decomposition.cpp diff --git a/mlir/unittests/decomposition/test_euler_decomposition.cpp b/mlir/unittests/decomposition/test_euler_decomposition.cpp new file mode 100644 index 0000000000..4aa54a6679 --- /dev/null +++ b/mlir/unittests/decomposition/test_euler_decomposition.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerDecomposition.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace mqt::ir::opt; +using namespace mqt::ir::opt::decomposition; + +namespace { +[[nodiscard]] matrix2x2 randomUnitaryMatrix() { + [[maybe_unused]] static auto initializeRandom = []() { + // Eigen uses std::rand() internally, use fixed seed for deterministic + // testing behavior + std::srand(123456UL); + return true; + }(); + const matrix2x2 randomMatrix = matrix2x2::Random(); + Eigen::HouseholderQR qr{}; // NOLINT(misc-include-cleaner) + qr.compute(randomMatrix); + const matrix2x2 unitaryMatrix = qr.householderQ(); + assert(helpers::isUnitaryMatrix(unitaryMatrix)); + return unitaryMatrix; +} +} // namespace + +class EulerDecompositionTest + : public testing::TestWithParam> { +public: + [[nodiscard]] static matrix2x2 restore(const TwoQubitGateSequence& sequence) { + matrix2x2 matrix = matrix2x2::Identity(); + for (auto&& gate : sequence.gates) { + matrix = getSingleQubitMatrix(gate) * matrix; + } + + matrix *= std::exp(IM * sequence.globalPhase); + return matrix; + } + + void SetUp() override { + eulerBasis = std::get<0>(GetParam()); + originalMatrix = std::get<1>(GetParam()); + } + +protected: + matrix2x2 originalMatrix; + EulerBasis eulerBasis{}; +}; + +TEST_P(EulerDecompositionTest, TestExact) { + auto decomposition = EulerDecomposition::generateCircuit( + eulerBasis, originalMatrix, false, 0.0); + auto restoredMatrix = restore(decomposition); + + EXPECT_TRUE(restoredMatrix.isApprox(originalMatrix)) + << "RESULT:\n" + << restoredMatrix << '\n'; +} + +TEST(EulerDecompositionTest, Random) { + auto stopTime = std::chrono::steady_clock::now() + std::chrono::seconds{10}; + auto iterations = 0; + auto eulerBases = std::array{EulerBasis::XYX, EulerBasis::XZX, + EulerBasis::ZYZ, EulerBasis::ZXZ}; + std::size_t currentEulerBase = 0; + while (std::chrono::steady_clock::now() < stopTime) { + auto originalMatrix = randomUnitaryMatrix(); + auto eulerBasis = eulerBases[currentEulerBase++ % eulerBases.size()]; + auto decomposition = EulerDecomposition::generateCircuit( + eulerBasis, originalMatrix, true, std::nullopt); + auto restoredMatrix = EulerDecompositionTest::restore(decomposition); + + EXPECT_TRUE(restoredMatrix.isApprox(originalMatrix)) + << "ORIGINAL:\n" + << originalMatrix << '\n' + << "RESULT:\n" + << restoredMatrix << '\n'; + ++iterations; + } + + RecordProperty("iterations", iterations); + std::cerr << "Iterations: " << iterations << '\n'; +} + +INSTANTIATE_TEST_CASE_P( + SingleQubitMatrices, EulerDecompositionTest, + testing::Combine(testing::Values(EulerBasis::XYX, EulerBasis::XZX, + EulerBasis::ZYZ, EulerBasis::ZXZ), + testing::Values(IDENTITY_GATE, ryMatrix(2.0), + rxMatrix(0.5), rzMatrix(3.14), H_GATE))); From d103e7abf4f668377beb70ea64d7015526ec3d07 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 30 Nov 2025 11:54:20 +0100 Subject: [PATCH 130/237] fix linter issues --- mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 616405d9e3..59e85286fc 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -12,6 +12,7 @@ #include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerBasis.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerDecomposition.h" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Gate.h" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h" From 7fc31aeebe5357720bb56958446af7a9a695d557 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 1 Dec 2025 10:13:53 +0100 Subject: [PATCH 131/237] fix include cleaner issues --- mlir/unittests/decomposition/test_euler_decomposition.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mlir/unittests/decomposition/test_euler_decomposition.cpp b/mlir/unittests/decomposition/test_euler_decomposition.cpp index 4aa54a6679..a2eea0013f 100644 --- a/mlir/unittests/decomposition/test_euler_decomposition.cpp +++ b/mlir/unittests/decomposition/test_euler_decomposition.cpp @@ -8,17 +8,22 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerBasis.h" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerDecomposition.h" +#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h" #include "mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h" #include +#include #include #include #include #include #include #include +#include +#include using namespace mqt::ir::opt; using namespace mqt::ir::opt::decomposition; From 43e9d315889593572de328c418413f94b2402f19 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 1 Dec 2025 13:01:30 +0100 Subject: [PATCH 132/237] add test parametrizations for other specializations --- .../decomposition/test_weyl_decomposition.cpp | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/mlir/unittests/decomposition/test_weyl_decomposition.cpp b/mlir/unittests/decomposition/test_weyl_decomposition.cpp index 60e2a67825..5ef7f71ef6 100644 --- a/mlir/unittests/decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/decomposition/test_weyl_decomposition.cpp @@ -73,6 +73,28 @@ class WeylDecompositionTest : public testing::TestWithParam { k2(const TwoQubitWeylDecomposition& decomposition) { return helpers::kroneckerProduct(decomposition.k2l, decomposition.k2r); } + + [[nodiscard]] static matrix4x4 ud(fp a, fp b, fp c, bool flipped = false) { + if (flipped) { + c = -c; + } + return matrix4x4{{std::exp(IM * c) * std::cos(a - b), C_ZERO, C_ZERO, + IM * std::exp(IM * c) * std::sin(a - b)}, + {C_ZERO, std::exp(M_IM * c) * std::cos(a + b), + IM * std::exp(M_IM * c) * std::sin(a + b), C_ZERO}, + {C_ZERO, IM * std::exp(M_IM * c) * std::sin(a + b), + std::exp(M_IM * c) * std::cos(a + b), C_ZERO}, + {IM * std::exp(IM * c) * std::sin(a - b), C_ZERO, C_ZERO, + std::exp(IM * c) * std::cos(a - b)}}; + } + + [[nodiscard]] static matrix4x4 fSimMatrix(fp theta, fp phi) { + auto isin = -IM * std::sin(theta); + auto cos = std::cos(theta); + auto x = std::exp(-IM * phi); + return matrix4x4{ + {1, 0, 0, 0}, {0, cos, isin, 0}, {0, isin, cos, 0}, {0, 0, 0, x}}; + } }; TEST_P(WeylDecompositionTest, TestExact) { @@ -136,3 +158,28 @@ INSTANTIATE_TEST_CASE_P( getTwoQubitMatrix( {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * helpers::kroneckerProduct(IPX, IPY))); + +INSTANTIATE_TEST_CASE_P( + SpecializedMatrices, WeylDecompositionTest, + ::testing::Values( + // id + controlled + general already covered by other parametrizations + // swap equiv + getTwoQubitMatrix({.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * + getTwoQubitMatrix( + {.type = qc::X, .parameter = {}, .qubitId = {1, 0}}) * + getTwoQubitMatrix( + {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}), + // partial swap equiv + WeylDecompositionTest::ud(0.5, 0.5, 0.5, false), + // partial swap equiv (flipped) + WeylDecompositionTest::ud(0.5, 0.5, 0.5, true), + // mirror controlled equiv + getTwoQubitMatrix({.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * + getTwoQubitMatrix( + {.type = qc::X, .parameter = {}, .qubitId = {1, 0}}), + // sim aab equiv + WeylDecompositionTest::fSimMatrix(2.6, 5.5), + // sim abb equiv + WeylDecompositionTest::ud(0.5, 0.1, 0.1), + // sim -ab-b equiv + WeylDecompositionTest::fSimMatrix(-3.2, -4.5))); From 95424eefc39a8ba44df042c80ab1f5abb18e484e Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 1 Dec 2025 15:23:19 +0100 Subject: [PATCH 133/237] add more matrix definitions --- .../Decomposition/UnitaryMatrices.h | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h index 6d9be79b15..1f10370f87 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h @@ -16,6 +16,31 @@ namespace mqt::ir::opt::decomposition { +inline constexpr auto SQRT2 = + static_cast(1.414213562373095048801688724209698079L); +inline constexpr auto FRAC1_SQRT2 = static_cast( + 0.707106781186547524400844362104849039284835937688474036588L); + +[[nodiscard]] inline matrix2x2 uMatrix(const fp lambda, const fp phi, + const fp theta) { + return matrix2x2{{{{std::cos(theta / 2.), 0.}, + {-std::cos(lambda) * std::sin(theta / 2.), + -std::sin(lambda) * std::sin(theta / 2.)}}, + {{std::cos(phi) * std::sin(theta / 2.), + std::sin(phi) * std::sin(theta / 2.)}, + {std::cos(lambda + phi) * std::cos(theta / 2.), + std::sin(lambda + phi) * std::cos(theta / 2.)}}}}; +} + +[[nodiscard]] inline matrix2x2 u2Matrix(const fp lambda, const fp phi) { + return matrix2x2{ + {FRAC1_SQRT2, + {-std::cos(lambda) * FRAC1_SQRT2, -std::sin(lambda) * FRAC1_SQRT2}}, + {{std::cos(phi) * FRAC1_SQRT2, std::sin(phi) * FRAC1_SQRT2}, + {std::cos(lambda + phi) * FRAC1_SQRT2, + std::sin(lambda + phi) * FRAC1_SQRT2}}}; +} + inline matrix2x2 rxMatrix(fp theta) { auto halfTheta = theta / 2.; auto cos = qfp(std::cos(halfTheta), 0.); @@ -68,10 +93,6 @@ inline matrix4x4 rzzMatrix(const fp theta) { inline matrix2x2 pMatrix(const fp lambda) { return matrix2x2{{1, 0}, {0, {std::cos(lambda), std::sin(lambda)}}}; } -inline constexpr auto SQRT2 = - static_cast(1.414213562373095048801688724209698079L); -inline constexpr auto FRAC1_SQRT2 = static_cast( - 0.707106781186547524400844362104849039284835937688474036588L); const matrix2x2 IDENTITY_GATE = matrix2x2::Identity(); const matrix2x2 H_GATE{{1.0 / SQRT2, 1.0 / SQRT2}, {1.0 / SQRT2, -1.0 / SQRT2}}; const matrix2x2 IPZ{{IM, C_ZERO}, {C_ZERO, M_IM}}; @@ -101,6 +122,12 @@ inline matrix2x2 getSingleQubitMatrix(const Gate& gate) { if (gate.type == qc::P) { return pMatrix(gate.parameter[0]); } + if (gate.type == qc::U) { + return uMatrix(gate.parameter[0], gate.parameter[1], gate.parameter[2]); + } + if (gate.type == qc::U2) { + return u2Matrix(gate.parameter[0], gate.parameter[1]); + } if (gate.type == qc::H) { return matrix2x2{{FRAC1_SQRT2, FRAC1_SQRT2}, {FRAC1_SQRT2, -FRAC1_SQRT2}}; } @@ -151,7 +178,9 @@ inline matrix4x4 getTwoQubitMatrix(const Gate& gate) { if (gate.type == qc::I) { return kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); } - throw std::invalid_argument{"unsupported gate type for two qubit matrix "}; + throw std::invalid_argument{ + "unsupported gate type for two qubit matrix (" + + qc::toString(gate.type) + ")"}; } throw std::logic_error{"Invalid number of qubit IDs in compute_unitary"}; } From a5c54e6e48b2aa201780f7a4a93720d8e48161eb Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 1 Dec 2025 15:23:45 +0100 Subject: [PATCH 134/237] handle barrier gates in series collection --- .../Transforms/GateDecompositionPattern.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index 59e85286fc..e4cf6a3d0e 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -186,7 +186,6 @@ struct GateDecompositionPattern final while (auto user = getUser(result.outQubits[i], &helpers::isSingleQubitOperation)) { foundGate = result.appendSingleQubitGate(*user); - assert(foundGate); // appending a single-qubit gate should not fail } } @@ -241,7 +240,9 @@ struct GateDecompositionPattern final explicit TwoQubitSeries(UnitaryInterface initialOperation) { auto&& in = initialOperation.getAllInQubits(); auto&& out = initialOperation->getResults(); - if (helpers::isSingleQubitOperation(initialOperation)) { + if (isBarrier(initialOperation)) { + // ignore barrier op as initial operation + } else if (helpers::isSingleQubitOperation(initialOperation)) { inQubits = {in[0], mlir::Value{}}; outQubits = {out[0], mlir::Value{}}; gates.push_back({.op = initialOperation, .qubitIds = {0}}); @@ -259,6 +260,9 @@ struct GateDecompositionPattern final * (will always return true) */ bool appendSingleQubitGate(UnitaryInterface nextGate) { + if (isBarrier(nextGate)) { + return false; + } auto operand = nextGate.getAllInQubits()[0]; // NOLINTNEXTLINE(readability-qualified-auto) auto it = llvm::find(outQubits, operand); @@ -314,6 +318,10 @@ struct GateDecompositionPattern final // possible to collect other single-qubit operations backtrackSingleQubitSeries(newInQubitId); } + if (isBarrier(nextGate)) { + // a barrier operation should not be crossed for a decomposition + return false; + } const QubitId firstQubitId = std::distance(outQubits.begin(), firstQubitIt); const QubitId secondQubitId = @@ -343,13 +351,18 @@ struct GateDecompositionPattern final }; while (auto* op = inQubits[qubitId].getDefiningOp()) { auto unitaryOp = mlir::dyn_cast(op); - if (unitaryOp && helpers::isSingleQubitOperation(unitaryOp)) { + if (unitaryOp && helpers::isSingleQubitOperation(unitaryOp) && + !isBarrier(unitaryOp)) { prependSingleQubitGate(unitaryOp); } else { break; } } } + + [[nodiscard]] static bool isBarrier(UnitaryInterface op) { + return llvm::isa_and_nonnull(*op); + } }; /** From aa3212d40b7f955a8c63f320cd04fbc18ba2e706 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 14:37:19 +0000 Subject: [PATCH 135/237] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MQTOpt/Transforms/Decomposition/UnitaryMatrices.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h index 1f10370f87..5d2a1100d1 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h @@ -178,9 +178,8 @@ inline matrix4x4 getTwoQubitMatrix(const Gate& gate) { if (gate.type == qc::I) { return kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); } - throw std::invalid_argument{ - "unsupported gate type for two qubit matrix (" + - qc::toString(gate.type) + ")"}; + throw std::invalid_argument{"unsupported gate type for two qubit matrix (" + + qc::toString(gate.type) + ")"}; } throw std::logic_error{"Invalid number of qubit IDs in compute_unitary"}; } From 474a5d9555cd02cbd65b9ac456521a0880935906 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 1 Dec 2025 16:14:42 +0100 Subject: [PATCH 136/237] fix include cleaner issues --- mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index e4cf6a3d0e..a0d71c47f4 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include From 21e9b7b8cbcff1be1a68abf12dd327c077e39723 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 1 Dec 2025 16:19:58 +0100 Subject: [PATCH 137/237] remove unused globalPhase from TwoQubitSeries --- .../lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index a0d71c47f4..c7df9b1967 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -155,7 +155,6 @@ struct GateDecompositionPattern final * mlir::Value{} if the series consists of only single-qubit gates. */ std::array outQubits; - fp globalPhase{}; struct MlirGate { UnitaryInterface op; @@ -210,7 +209,6 @@ struct GateDecompositionPattern final .qubitId = gate.qubitIds}); unitaryMatrix = gateMatrix * unitaryMatrix; } - unitaryMatrix *= std::exp(IM * globalPhase); assert(helpers::isUnitaryMatrix(unitaryMatrix)); return unitaryMatrix; @@ -226,7 +224,6 @@ struct GateDecompositionPattern final .qubitId = gate.qubitIds}); unitaryMatrix = gateMatrix * unitaryMatrix; } - unitaryMatrix *= std::exp(IM * globalPhase); assert(helpers::isUnitaryMatrix(unitaryMatrix)); return unitaryMatrix; From 6815b2601440365869b1617b6aca0da749a1487e Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 1 Dec 2025 16:20:18 +0100 Subject: [PATCH 138/237] fix barrier handling at start of series --- .../Transforms/GateDecompositionPattern.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp index c7df9b1967..cbbfee39ad 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp @@ -148,13 +148,13 @@ struct GateDecompositionPattern final * * All */ - std::array inQubits; + std::array inQubits{}; /** * Qubits that are the input for the series. * First qubit will always be set, second qubit may be equal to * mlir::Value{} if the series consists of only single-qubit gates. */ - std::array outQubits; + std::array outQubits{}; struct MlirGate { UnitaryInterface op; @@ -163,6 +163,9 @@ struct GateDecompositionPattern final llvm::SmallVector gates; [[nodiscard]] static TwoQubitSeries getTwoQubitSeries(UnitaryInterface op) { + if (isBarrier(op)) { + return {}; + } TwoQubitSeries result(op); auto getUser = [](mlir::Value qubit, @@ -235,12 +238,18 @@ struct GateDecompositionPattern final } private: + /** + * Initialize empty TwoQubitSeries instance. + * New operations can *NOT* be added when calling this constructor overload. + */ + TwoQubitSeries() = default; + /** + * Initialize TwoQubitSeries instance with given first operation. + */ explicit TwoQubitSeries(UnitaryInterface initialOperation) { auto&& in = initialOperation.getAllInQubits(); auto&& out = initialOperation->getResults(); - if (isBarrier(initialOperation)) { - // ignore barrier op as initial operation - } else if (helpers::isSingleQubitOperation(initialOperation)) { + if (helpers::isSingleQubitOperation(initialOperation)) { inQubits = {in[0], mlir::Value{}}; outQubits = {out[0], mlir::Value{}}; gates.push_back({.op = initialOperation, .qubitIds = {0}}); From 202fa7fd181dc4ac9da2c6511f42d863a38b3489 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 1 Dec 2025 18:37:06 +0100 Subject: [PATCH 139/237] simplify weyl specialization test parameters --- .../Decomposition/WeylDecomposition.h | 6 ++-- .../decomposition/test_weyl_decomposition.cpp | 34 ++++--------------- 2 files changed, 9 insertions(+), 31 deletions(-) diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h index 5ccb6b8782..997753145c 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h @@ -65,9 +65,9 @@ struct TwoQubitWeylDecomposition { // These next 3 gates use the definition of fSim from eq (1) in: // https://arxiv.org/pdf/2001.08343.pdf - FSimaabEquiv, - FSimabbEquiv, - FSimabmbEquiv, + FSimaabEquiv, // parameters a=b & a!=c + FSimabbEquiv, // parameters a!=b & b=c + FSimabmbEquiv, // parameters a!=b!=c & -b=c }; // a, b, c are the parameters of the canonical gate (CAN) diff --git a/mlir/unittests/decomposition/test_weyl_decomposition.cpp b/mlir/unittests/decomposition/test_weyl_decomposition.cpp index 5ef7f71ef6..77e68fcb8b 100644 --- a/mlir/unittests/decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/decomposition/test_weyl_decomposition.cpp @@ -73,28 +73,6 @@ class WeylDecompositionTest : public testing::TestWithParam { k2(const TwoQubitWeylDecomposition& decomposition) { return helpers::kroneckerProduct(decomposition.k2l, decomposition.k2r); } - - [[nodiscard]] static matrix4x4 ud(fp a, fp b, fp c, bool flipped = false) { - if (flipped) { - c = -c; - } - return matrix4x4{{std::exp(IM * c) * std::cos(a - b), C_ZERO, C_ZERO, - IM * std::exp(IM * c) * std::sin(a - b)}, - {C_ZERO, std::exp(M_IM * c) * std::cos(a + b), - IM * std::exp(M_IM * c) * std::sin(a + b), C_ZERO}, - {C_ZERO, IM * std::exp(M_IM * c) * std::sin(a + b), - std::exp(M_IM * c) * std::cos(a + b), C_ZERO}, - {IM * std::exp(IM * c) * std::sin(a - b), C_ZERO, C_ZERO, - std::exp(IM * c) * std::cos(a - b)}}; - } - - [[nodiscard]] static matrix4x4 fSimMatrix(fp theta, fp phi) { - auto isin = -IM * std::sin(theta); - auto cos = std::cos(theta); - auto x = std::exp(-IM * phi); - return matrix4x4{ - {1, 0, 0, 0}, {0, cos, isin, 0}, {0, isin, cos, 0}, {0, 0, 0, x}}; - } }; TEST_P(WeylDecompositionTest, TestExact) { @@ -170,16 +148,16 @@ INSTANTIATE_TEST_CASE_P( getTwoQubitMatrix( {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}), // partial swap equiv - WeylDecompositionTest::ud(0.5, 0.5, 0.5, false), + canonicalGate(0.5, 0.5, 0.5), // partial swap equiv (flipped) - WeylDecompositionTest::ud(0.5, 0.5, 0.5, true), + canonicalGate(0.5, 0.5, -0.5), // mirror controlled equiv getTwoQubitMatrix({.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * getTwoQubitMatrix( {.type = qc::X, .parameter = {}, .qubitId = {1, 0}}), // sim aab equiv - WeylDecompositionTest::fSimMatrix(2.6, 5.5), + canonicalGate(0.5, 0.5, 0.1), // sim abb equiv - WeylDecompositionTest::ud(0.5, 0.1, 0.1), - // sim -ab-b equiv - WeylDecompositionTest::fSimMatrix(-3.2, -4.5))); + canonicalGate(0.5, 0.1, 0.1), + // sim ab-b equiv + canonicalGate(0.5, 0.1, -0.1))); From 49a60a7932d3380e5485095abfa6790c4df19c7c Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 19 Dec 2025 16:29:39 +0100 Subject: [PATCH 140/237] remove dynamic_casts due to RTTI issue --- .../Dialect/MQTRef/Translation/ImportQuantumComputation.cpp | 6 +++--- .../QC/Translation/TranslateQuantumComputationToQC.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mlir/lib/Dialect/MQTRef/Translation/ImportQuantumComputation.cpp b/mlir/lib/Dialect/MQTRef/Translation/ImportQuantumComputation.cpp index 49548caf1f..7eb3150baf 100644 --- a/mlir/lib/Dialect/MQTRef/Translation/ImportQuantumComputation.cpp +++ b/mlir/lib/Dialect/MQTRef/Translation/ImportQuantumComputation.cpp @@ -289,7 +289,7 @@ static void addMeasureOp(mlir::OpBuilder& builder, const llvm::SmallVector& qubits, const BitIndexVec& bitMap) { const auto& measureOp = - dynamic_cast(operation); + static_cast(operation); const auto& targets = measureOp.getTargets(); const auto& classics = measureOp.getClassics(); for (std::size_t i = 0; i < targets.size(); ++i) { @@ -343,7 +343,7 @@ addBlockOps(mlir::OpBuilder& builder, const qc::Operation* operationInBlock, const BitIndexVec& bitMap) { if (operationInBlock->isCompoundOperation()) { for (const auto& operation : - dynamic_cast(*operationInBlock)) { + static_cast(*operationInBlock)) { if (addOperation(builder, *operation, qubits, bitMap).failed()) { return llvm::failure(); } @@ -422,7 +422,7 @@ addIfElseOp(mlir::OpBuilder& builder, const qc::Operation& op, const llvm::SmallVector& qubits, const BitIndexVec& bitMap) { const auto loc = builder.getUnknownLoc(); - const auto& ifElse = dynamic_cast(op); + const auto& ifElse = static_cast(op); const auto* thenOp = ifElse.getThenOp(); // Canonicalization should have removed empty then blocks diff --git a/mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp b/mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp index 0a15224437..4ce25d33de 100644 --- a/mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp +++ b/mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp @@ -194,7 +194,7 @@ static void addMeasureOp(QCProgramBuilder& builder, const SmallVector& qubits, const BitIndexVec& bitMap) { const auto& measureOp = - dynamic_cast(operation); + static_cast(operation); const auto& targets = measureOp.getTargets(); const auto& classics = measureOp.getClassics(); From 8fc3b53d9626e673d3b2dd3af3e479572bf2e897 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 19 Dec 2025 16:29:49 +0100 Subject: [PATCH 141/237] Revert "remove dynamic_casts due to RTTI issue" This reverts commit dc13927d78c687e4ab0d22adcef0724ada281a52. --- .../Dialect/MQTRef/Translation/ImportQuantumComputation.cpp | 6 +++--- .../QC/Translation/TranslateQuantumComputationToQC.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mlir/lib/Dialect/MQTRef/Translation/ImportQuantumComputation.cpp b/mlir/lib/Dialect/MQTRef/Translation/ImportQuantumComputation.cpp index 7eb3150baf..49548caf1f 100644 --- a/mlir/lib/Dialect/MQTRef/Translation/ImportQuantumComputation.cpp +++ b/mlir/lib/Dialect/MQTRef/Translation/ImportQuantumComputation.cpp @@ -289,7 +289,7 @@ static void addMeasureOp(mlir::OpBuilder& builder, const llvm::SmallVector& qubits, const BitIndexVec& bitMap) { const auto& measureOp = - static_cast(operation); + dynamic_cast(operation); const auto& targets = measureOp.getTargets(); const auto& classics = measureOp.getClassics(); for (std::size_t i = 0; i < targets.size(); ++i) { @@ -343,7 +343,7 @@ addBlockOps(mlir::OpBuilder& builder, const qc::Operation* operationInBlock, const BitIndexVec& bitMap) { if (operationInBlock->isCompoundOperation()) { for (const auto& operation : - static_cast(*operationInBlock)) { + dynamic_cast(*operationInBlock)) { if (addOperation(builder, *operation, qubits, bitMap).failed()) { return llvm::failure(); } @@ -422,7 +422,7 @@ addIfElseOp(mlir::OpBuilder& builder, const qc::Operation& op, const llvm::SmallVector& qubits, const BitIndexVec& bitMap) { const auto loc = builder.getUnknownLoc(); - const auto& ifElse = static_cast(op); + const auto& ifElse = dynamic_cast(op); const auto* thenOp = ifElse.getThenOp(); // Canonicalization should have removed empty then blocks diff --git a/mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp b/mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp index 4ce25d33de..0a15224437 100644 --- a/mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp +++ b/mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp @@ -194,7 +194,7 @@ static void addMeasureOp(QCProgramBuilder& builder, const SmallVector& qubits, const BitIndexVec& bitMap) { const auto& measureOp = - static_cast(operation); + dynamic_cast(operation); const auto& targets = measureOp.getTargets(); const auto& classics = measureOp.getClassics(); From 68890af54ae905a83f4ea6553ba47b45f48a0c04 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 20 Dec 2025 12:00:42 +0100 Subject: [PATCH 142/237] move GateDecompositionPass to new QCO dialect --- mlir/include/mlir/CMakeLists.txt | 3 +- .../mlir/Dialect/MQTOpt/Transforms/Passes.h | 1 - .../mlir/Dialect/MQTOpt/Transforms/Passes.td | 9 - mlir/include/mlir/Passes/CMakeLists.txt | 12 + .../Decomposition/BasisDecomposer.h | 4 +- .../Decomposition/EulerBasis.h | 4 +- .../Decomposition/EulerDecomposition.h | 4 +- .../Decomposition/Gate.h | 4 +- .../Decomposition/GateSequence.h | 5 +- .../Decomposition/Helpers.h | 41 +- .../Decomposition/UnitaryMatrices.h | 4 +- .../Decomposition/WeylDecomposition.h | 4 +- mlir/include/mlir/Passes/Passes.h | 40 ++ mlir/include/mlir/Passes/Passes.td | 27 + mlir/lib/CMakeLists.txt | 3 +- .../Dialect/MQTOpt/Transforms/CMakeLists.txt | 2 +- mlir/lib/Passes/CMakeLists.txt | 35 ++ .../GateDecompositionPass.cpp} | 14 +- .../Patterns}/GateDecompositionPattern.cpp | 138 ++--- .../MQTOpt/Transforms/gate-decomposition.mlir | 540 ------------------ mlir/unittests/CMakeLists.txt | 5 +- mlir/unittests/decomposition/CMakeLists.txt | 12 +- .../decomposition/test_basis_decomposer.cpp | 18 +- .../test_euler_decomposition.cpp | 14 +- .../decomposition/test_weyl_decomposition.cpp | 10 +- 25 files changed, 248 insertions(+), 705 deletions(-) create mode 100644 mlir/include/mlir/Passes/CMakeLists.txt rename mlir/include/mlir/{Dialect/MQTOpt/Transforms => Passes}/Decomposition/BasisDecomposer.h (99%) rename mlir/include/mlir/{Dialect/MQTOpt/Transforms => Passes}/Decomposition/EulerBasis.h (89%) rename mlir/include/mlir/{Dialect/MQTOpt/Transforms => Passes}/Decomposition/EulerDecomposition.h (98%) rename mlir/include/mlir/{Dialect/MQTOpt/Transforms => Passes}/Decomposition/Gate.h (87%) rename mlir/include/mlir/{Dialect/MQTOpt/Transforms => Passes}/Decomposition/GateSequence.h (94%) rename mlir/include/mlir/{Dialect/MQTOpt/Transforms => Passes}/Decomposition/Helpers.h (83%) rename mlir/include/mlir/{Dialect/MQTOpt/Transforms => Passes}/Decomposition/UnitaryMatrices.h (98%) rename mlir/include/mlir/{Dialect/MQTOpt/Transforms => Passes}/Decomposition/WeylDecomposition.h (99%) create mode 100644 mlir/include/mlir/Passes/Passes.h create mode 100644 mlir/include/mlir/Passes/Passes.td create mode 100644 mlir/lib/Passes/CMakeLists.txt rename mlir/lib/{Dialect/MQTOpt/Transforms/GateDecomposition.cpp => Passes/GateDecompositionPass.cpp} (80%) rename mlir/lib/{Dialect/MQTOpt/Transforms => Passes/Patterns}/GateDecompositionPattern.cpp (80%) delete mode 100644 mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir diff --git a/mlir/include/mlir/CMakeLists.txt b/mlir/include/mlir/CMakeLists.txt index 9add328fef..35666595c7 100644 --- a/mlir/include/mlir/CMakeLists.txt +++ b/mlir/include/mlir/CMakeLists.txt @@ -6,5 +6,6 @@ # # Licensed under the MIT License -add_subdirectory(Dialect) add_subdirectory(Conversion) +add_subdirectory(Dialect) +add_subdirectory(Passes) diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.h index 96c87bd4bf..5bc05293c5 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.h +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.h @@ -34,7 +34,6 @@ enum class PlacementStrategy : std::uint8_t { Random, Identity }; #include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" // IWYU pragma: export void populateGateEliminationPatterns(mlir::RewritePatternSet& patterns); -void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns); void populateMergeRotationGatesPatterns(mlir::RewritePatternSet& patterns); void populateSwapReconstructionAndElisionPatterns( mlir::RewritePatternSet& patterns); diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td index 89c3c517a8..b16059771f 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td @@ -43,15 +43,6 @@ def GateElimination : Pass<"gate-elimination", "mlir::ModuleOp"> { }]; } -def GateDecomposition : Pass<"gate-decomposition", "mlir::ModuleOp"> { - let dependentDialects = [ "mlir::arith::ArithDialect", "mqt::ir::opt::MQTOptDialect" ]; - let summary = "This pass performs various gate decompositions to translate quantum gates being used."; - let description = [{ - Decomposes series of operations that operate on up to two qubits into a sequence of up to three - two-qubit basis gates and single-qubit operations. - }]; -} - def MergeRotationGates : Pass<"merge-rotation-gates", "mlir::ModuleOp"> { let summary = "This pass searches for consecutive applications of rotation gates that can be merged."; let description = [{ diff --git a/mlir/include/mlir/Passes/CMakeLists.txt b/mlir/include/mlir/Passes/CMakeLists.txt new file mode 100644 index 0000000000..03e903ad72 --- /dev/null +++ b/mlir/include/mlir/Passes/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +set(LLVM_TARGET_DEFINITIONS Passes.td) +mlir_tablegen(Passes.h.inc -gen-pass-decls -name QCO) +add_public_tablegen_target(QcoPassesIncGen) +add_mlir_doc(Passes QcoPasses Passes/ -gen-pass-doc) diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h similarity index 99% rename from mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h rename to mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index 11149787ed..cc4d510a0f 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -38,7 +38,7 @@ #include #include -namespace mqt::ir::opt::decomposition { +namespace mlir::qco::decomposition { /** * Decomposer that must be initialized with a two-qubit basis gate that will @@ -544,4 +544,4 @@ class TwoQubitBasisDecomposer { matrix2x2 q2r; }; -} // namespace mqt::ir::opt::decomposition +} // namespace mlir::qco::decomposition diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerBasis.h b/mlir/include/mlir/Passes/Decomposition/EulerBasis.h similarity index 89% rename from mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerBasis.h rename to mlir/include/mlir/Passes/Decomposition/EulerBasis.h index 5924ba530e..887db1a272 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerBasis.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerBasis.h @@ -12,7 +12,7 @@ #include -namespace mqt::ir::opt::decomposition { +namespace mlir::qco::decomposition { /** * Largest number that will be assumed as zero for the euler decompositions. */ @@ -37,4 +37,4 @@ enum class EulerBasis : std::uint8_t { ZSXX = 10, ZSX = 11, }; -} // namespace mqt::ir::opt::decomposition +} // namespace mlir::qco::decomposition diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerDecomposition.h b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h similarity index 98% rename from mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerDecomposition.h rename to mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h index df9aa21e0a..35220464c7 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h @@ -32,7 +32,7 @@ #include #include -namespace mqt::ir::opt::decomposition { +namespace mlir::qco::decomposition { /** * Decomposition of single-qubit matrices into rotation gates using a KAK @@ -210,4 +210,4 @@ class EulerDecomposition { return sequence; } }; -} // namespace mqt::ir::opt::decomposition +} // namespace mlir::qco::decomposition diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/Gate.h b/mlir/include/mlir/Passes/Decomposition/Gate.h similarity index 87% rename from mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/Gate.h rename to mlir/include/mlir/Passes/Decomposition/Gate.h index 1fdae210c0..f6ca1d3d8d 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/Gate.h +++ b/mlir/include/mlir/Passes/Decomposition/Gate.h @@ -15,7 +15,7 @@ #include -namespace mqt::ir::opt::decomposition { +namespace mlir::qco::decomposition { using QubitId = std::size_t; @@ -29,4 +29,4 @@ struct Gate { llvm::SmallVector qubitId = {0}; }; -} // namespace mqt::ir::opt::decomposition +} // namespace mlir::qco::decomposition diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h b/mlir/include/mlir/Passes/Decomposition/GateSequence.h similarity index 94% rename from mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h rename to mlir/include/mlir/Passes/Decomposition/GateSequence.h index 7254c47ab0..fd66133f4e 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h +++ b/mlir/include/mlir/Passes/Decomposition/GateSequence.h @@ -15,7 +15,6 @@ #include "Helpers.h" #include "UnitaryMatrices.h" #include "ir/operations/OpType.hpp" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h" #include #include @@ -32,7 +31,7 @@ #include #include -namespace mqt::ir::opt::decomposition { +namespace mlir::qco::decomposition { /** * Gate sequence of single-qubit and/or two-qubit gates. */ @@ -96,4 +95,4 @@ using OneQubitGateSequence = QubitGateSequence; */ using TwoQubitGateSequence = QubitGateSequence; -} // namespace mqt::ir::opt::decomposition +} // namespace mlir::qco::decomposition diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h b/mlir/include/mlir/Passes/Decomposition/Helpers.h similarity index 83% rename from mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h rename to mlir/include/mlir/Passes/Decomposition/Helpers.h index c2dfe8dc38..44c4e5a310 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h +++ b/mlir/include/mlir/Passes/Decomposition/Helpers.h @@ -12,7 +12,7 @@ #include "ir/Definitions.hpp" #include "ir/operations/OpType.hpp" -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" +#include "mlir/Dialect/QCO/IR/QCODialect.h" #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) @@ -30,7 +30,7 @@ #include #include // TODO: unstable, NOLINT(misc-include-cleaner) -namespace mqt::ir::opt { +namespace mlir::qco { using fp = qc::fp; using qfp = std::complex; // NOLINTBEGIN(misc-include-cleaner) @@ -47,9 +47,9 @@ constexpr qfp C_M_ONE{-1., 0.}; constexpr qfp IM{0., 1.}; constexpr qfp M_IM{0., -1.}; -} // namespace mqt::ir::opt +} // namespace mlir::qco -namespace mqt::ir::opt::helpers { +namespace mlir::qco::helpers { std::optional mlirValueToFp(mlir::Value value); @@ -136,17 +136,17 @@ template } [[nodiscard]] inline llvm::SmallVector -getParameters(UnitaryInterface op) { +getParameters(UnitaryOpInterface op) { llvm::SmallVector parameters; - for (auto&& param : op.getParams()) { - if (auto value = helpers::mlirValueToFp(param)) { + for (std::size_t i = 0; i < op.getNumParams(); ++i) { + if (auto value = helpers::mlirValueToFp(op.getParameter(i))) { parameters.push_back(*value); } } return parameters; } -[[nodiscard]] inline qc::OpType getQcType(UnitaryInterface op) { +[[nodiscard]] inline qc::OpType getQcType(UnitaryOpInterface op) { try { const std::string type = op->getName().stripDialect().str(); return qc::opTypeFromString(type); @@ -155,27 +155,12 @@ getParameters(UnitaryInterface op) { } } -[[nodiscard]] inline bool isSingleQubitOperation(UnitaryInterface op) { - auto&& inQubits = op.getInQubits(); - auto&& outQubits = op.getOutQubits(); - const bool isSingleQubitOp = - inQubits.size() == 1 && outQubits.size() == 1 && !op.isControlled(); - return isSingleQubitOp; +[[nodiscard]] inline bool isSingleQubitOperation(UnitaryOpInterface op) { + return op.isSingleQubit(); } -[[nodiscard]] inline bool isTwoQubitOperation(UnitaryInterface op) { - auto&& inQubits = op.getInQubits(); - auto&& inPosCtrlQubits = op.getPosCtrlInQubits(); - auto&& inNegCtrlQubits = op.getNegCtrlInQubits(); - auto inQubitSize = - inQubits.size() + inPosCtrlQubits.size() + inNegCtrlQubits.size(); - auto&& outQubits = op.getOutQubits(); - auto&& outPosCtrlQubits = op.getPosCtrlOutQubits(); - auto&& outNegCtrlQubits = op.getNegCtrlOutQubits(); - auto outQubitSize = - outQubits.size() + outPosCtrlQubits.size() + outNegCtrlQubits.size(); - const bool isTwoQubitOp = inQubitSize == 2 && outQubitSize == 2; - return isTwoQubitOp; +[[nodiscard]] inline bool isTwoQubitOperation(UnitaryOpInterface op) { + return op.isTwoQubit(); } // NOLINTBEGIN(misc-include-cleaner) @@ -235,4 +220,4 @@ template return 1; } -} // namespace mqt::ir::opt::helpers +} // namespace mlir::qco::helpers diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h similarity index 98% rename from mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h rename to mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index 5d2a1100d1..322112990e 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -14,7 +14,7 @@ #include "Helpers.h" #include "ir/operations/OpType.hpp" -namespace mqt::ir::opt::decomposition { +namespace mlir::qco::decomposition { inline constexpr auto SQRT2 = static_cast(1.414213562373095048801688724209698079L); @@ -184,4 +184,4 @@ inline matrix4x4 getTwoQubitMatrix(const Gate& gate) { throw std::logic_error{"Invalid number of qubit IDs in compute_unitary"}; } -} // namespace mqt::ir::opt::decomposition +} // namespace mlir::qco::decomposition diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h similarity index 99% rename from mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h rename to mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index 997753145c..ad37ab1376 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -39,7 +39,7 @@ #include // TODO: unstable, NOLINT(misc-include-cleaner) #include -namespace mqt::ir::opt::decomposition { +namespace mlir::qco::decomposition { /** * Allowed deviation for internal assert statements which ensure the correctness * of the decompositions. @@ -781,4 +781,4 @@ struct TwoQubitWeylDecomposition { return flippedFromOriginal; } }; -} // namespace mqt::ir::opt::decomposition +} // namespace mlir::qco::decomposition diff --git a/mlir/include/mlir/Passes/Passes.h b/mlir/include/mlir/Passes/Passes.h new file mode 100644 index 0000000000..e60409041a --- /dev/null +++ b/mlir/include/mlir/Passes/Passes.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include "mlir/Dialect/QCO/IR/QCODialect.h" + +#include +#include +#include + +namespace mlir { + +class RewritePatternSet; + +} // namespace mlir + +namespace mlir::qco { + +#define GEN_PASS_DECL +#include "mlir/Passes/Passes.h.inc" // IWYU pragma: export + +void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns); + +//===----------------------------------------------------------------------===// +// Registration +//===----------------------------------------------------------------------===// + +/// Generate the code for registering passes. +#define GEN_PASS_REGISTRATION +#include "mlir/Passes/Passes.h.inc" // IWYU pragma: export + +} // namespace mlir::qco diff --git a/mlir/include/mlir/Passes/Passes.td b/mlir/include/mlir/Passes/Passes.td new file mode 100644 index 0000000000..ff071fb838 --- /dev/null +++ b/mlir/include/mlir/Passes/Passes.td @@ -0,0 +1,27 @@ +// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +// Copyright (c) 2025 Munich Quantum Software Company GmbH +// All rights reserved. +// +// SPDX-License-Identifier: MIT +// +// Licensed under the MIT License + +#ifndef QCO_PASSES +#define QCO_PASSES + +include "mlir/Pass/PassBase.td" + +//===----------------------------------------------------------------------===// +// Optimization Passes +//===----------------------------------------------------------------------===// + +def GateDecompositionPass : Pass<"gate-decomposition", "mlir::ModuleOp"> { + let dependentDialects = [ "mlir::arith::ArithDialect", "mlir::qco::QCODialect" ]; + let summary = "This pass performs various gate decompositions to translate quantum gates being used."; + let description = [{ + Decomposes series of operations that operate on up to two qubits into a sequence of up to three + two-qubit basis gates and single-qubit operations. + }]; +} + +#endif // QCO_PASSES diff --git a/mlir/lib/CMakeLists.txt b/mlir/lib/CMakeLists.txt index 8b2ef74e7c..b4a19a14dd 100644 --- a/mlir/lib/CMakeLists.txt +++ b/mlir/lib/CMakeLists.txt @@ -6,7 +6,8 @@ # # Licensed under the MIT License -add_subdirectory(Dialect) add_subdirectory(Conversion) add_subdirectory(Compiler) +add_subdirectory(Dialect) +add_subdirectory(Passes) add_subdirectory(Support) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt b/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt index 7bff1278b6..e7f1f55dac 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt +++ b/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt @@ -7,7 +7,7 @@ # Licensed under the MIT License get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) -set(LIBRARIES ${dialect_libs} MQT::CoreIR Eigen3::Eigen) +set(LIBRARIES ${dialect_libs} MQT::CoreIR) add_compile_options(-fexceptions) file(GLOB_RECURSE TRANSFORMS_SOURCES *.cpp) diff --git a/mlir/lib/Passes/CMakeLists.txt b/mlir/lib/Passes/CMakeLists.txt new file mode 100644 index 0000000000..d94f247083 --- /dev/null +++ b/mlir/lib/Passes/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +set(LIBRARIES ${dialect_libs} MQT::CoreIR Eigen3::Eigen) +add_compile_options(-fexceptions) + +file(GLOB_RECURSE PASSES_SOURCES *.cpp) + +add_mlir_library(QcoPasses ${PASSES_SOURCES} LINK_LIBS PUBLIC ${LIBRARIES} DEPENDS QcoPassesIncGen) + +# collect header files +file(GLOB_RECURSE PASSES_HEADERS_SOURCE ${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Passes/*.h) +file(GLOB_RECURSE PASSES_HEADERS_BUILD ${MQT_MLIR_BUILD_INCLUDE_DIR}/mlir/Passes/*.inc) + +# add public headers using file sets +target_sources( + QcoPasses + PUBLIC FILE_SET + HEADERS + BASE_DIRS + ${MQT_MLIR_SOURCE_INCLUDE_DIR} + FILES + ${PASSES_HEADERS_SOURCE} + FILE_SET + HEADERS + BASE_DIRS + ${MQT_MLIR_BUILD_INCLUDE_DIR} + FILES + ${PASSES_HEADERS_BUILD}) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp b/mlir/lib/Passes/GateDecompositionPass.cpp similarity index 80% rename from mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp rename to mlir/lib/Passes/GateDecompositionPass.cpp index 7db1378553..88198f4347 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecomposition.cpp +++ b/mlir/lib/Passes/GateDecompositionPass.cpp @@ -8,25 +8,25 @@ * Licensed under the MIT License */ -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" +#include "mlir/Passes/Passes.h" #include #include #include #include -namespace mqt::ir::opt { +namespace mlir::qco { -#define GEN_PASS_DEF_GATEDECOMPOSITION -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" +#define GEN_PASS_DEF_GATEDECOMPOSITIONPASS +#include "mlir/Passes/Passes.h.inc" /** * @brief This pass attempts to collect as many operations as possible into a * 4x4 unitary matrix and then decompose it into 1q rotations and 2q * basis gates. */ -struct GateDecomposition final - : impl::GateDecompositionBase { +struct GateDecompositionPass final + : impl::GateDecompositionPassBase { void runOnOperation() override { // Get the current operation being operated on. @@ -49,4 +49,4 @@ struct GateDecomposition final } }; -} // namespace mqt::ir::opt +} // namespace mlir::qco diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp similarity index 80% rename from mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp rename to mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index cbbfee39ad..6372b6bcc5 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -9,16 +9,16 @@ */ #include "ir/operations/OpType.hpp" -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerBasis.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerDecomposition.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Gate.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" +#include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Passes/Decomposition/BasisDecomposer.h" +#include "mlir/Passes/Decomposition/EulerBasis.h" +#include "mlir/Passes/Decomposition/EulerDecomposition.h" +#include "mlir/Passes/Decomposition/Gate.h" +#include "mlir/Passes/Decomposition/GateSequence.h" +#include "mlir/Passes/Decomposition/Helpers.h" +#include "mlir/Passes/Decomposition/UnitaryMatrices.h" +#include "mlir/Passes/Decomposition/WeylDecomposition.h" +#include "mlir/Passes/Passes.h" #include #include @@ -40,7 +40,7 @@ #include #include -namespace mqt::ir::opt { +namespace mlir::qco { /** * @brief This pattern attempts to collect as many operations as possible into a @@ -48,7 +48,7 @@ namespace mqt::ir::opt { * gates. */ struct GateDecompositionPattern final - : mlir::OpInterfaceRewritePattern { + : mlir::OpInterfaceRewritePattern { using EulerBasis = decomposition::EulerBasis; using Gate = decomposition::Gate; @@ -70,7 +70,7 @@ struct GateDecompositionPattern final } mlir::LogicalResult - matchAndRewrite(UnitaryInterface op, + matchAndRewrite(UnitaryOpInterface op, mlir::PatternRewriter& rewriter) const override { auto series = TwoQubitSeries::getTwoQubitSeries(op); @@ -157,23 +157,24 @@ struct GateDecompositionPattern final std::array outQubits{}; struct MlirGate { - UnitaryInterface op; + UnitaryOpInterface op; llvm::SmallVector qubitIds; }; llvm::SmallVector gates; - [[nodiscard]] static TwoQubitSeries getTwoQubitSeries(UnitaryInterface op) { + [[nodiscard]] static TwoQubitSeries + getTwoQubitSeries(UnitaryOpInterface op) { if (isBarrier(op)) { return {}; } TwoQubitSeries result(op); auto getUser = [](mlir::Value qubit, - auto&& filter) -> std::optional { + auto&& filter) -> std::optional { if (qubit) { assert(qubit.hasOneUse()); auto user = - mlir::dyn_cast(*qubit.getUsers().begin()); + mlir::dyn_cast(*qubit.getUsers().begin()); if (user && filter(user)) { return user; } @@ -246,31 +247,31 @@ struct GateDecompositionPattern final /** * Initialize TwoQubitSeries instance with given first operation. */ - explicit TwoQubitSeries(UnitaryInterface initialOperation) { - auto&& in = initialOperation.getAllInQubits(); - auto&& out = initialOperation->getResults(); + explicit TwoQubitSeries(UnitaryOpInterface initialOperation) { if (helpers::isSingleQubitOperation(initialOperation)) { - inQubits = {in[0], mlir::Value{}}; - outQubits = {out[0], mlir::Value{}}; + inQubits = {initialOperation.getInputQubit(0), mlir::Value{}}; + outQubits = {initialOperation.getOutputQubit(0), mlir::Value{}}; gates.push_back({.op = initialOperation, .qubitIds = {0}}); } else if (helpers::isTwoQubitOperation(initialOperation)) { - inQubits = {in[0], in[1]}; - outQubits = {out[0], out[1]}; + inQubits = {initialOperation.getInputQubit(0), + initialOperation.getInputQubit(1)}; + outQubits = {initialOperation.getOutputQubit(0), + initialOperation.getOutputQubit(1)}; gates.push_back({.op = initialOperation, .qubitIds = {0, 1}}); } complexity += helpers::getComplexity(helpers::getQcType(initialOperation), - in.size()); + initialOperation.getNumQubits()); } /** * @return true if series continues, otherwise false * (will always return true) */ - bool appendSingleQubitGate(UnitaryInterface nextGate) { + bool appendSingleQubitGate(UnitaryOpInterface nextGate) { if (isBarrier(nextGate)) { return false; } - auto operand = nextGate.getAllInQubits()[0]; + auto operand = nextGate.getInputQubit(0); // NOLINTNEXTLINE(readability-qualified-auto) auto it = llvm::find(outQubits, operand); if (it == outQubits.end()) { @@ -288,10 +289,9 @@ struct GateDecompositionPattern final /** * @return true if series continues, otherwise false */ - bool appendTwoQubitGate(UnitaryInterface nextGate) { - auto opInQubits = nextGate.getAllInQubits(); - auto&& firstOperand = opInQubits[0]; - auto&& secondOperand = opInQubits[1]; + bool appendTwoQubitGate(UnitaryOpInterface nextGate) { + auto&& firstOperand = nextGate.getInputQubit(0); + auto&& secondOperand = nextGate.getInputQubit(1); auto firstQubitIt = // NOLINT(readability-qualified-auto) llvm::find(outQubits, firstOperand); auto secondQubitIt = // NOLINT(readability-qualified-auto) @@ -304,6 +304,9 @@ struct GateDecompositionPattern final if (it == outQubits.end()) { return false; } + // TODO: this only works because parameters are at end of operands; + // use future getInputQubits() instead + auto&& opInQubits = nextGate->getOperands(); // iterator in the operation input of "old" qubit that already has // previous single-qubit gates in this series auto it2 = llvm::find(opInQubits, firstQubitIt != outQubits.end() @@ -350,14 +353,14 @@ struct GateDecompositionPattern final * in the series. */ void backtrackSingleQubitSeries(QubitId qubitId) { - auto prependSingleQubitGate = [&](UnitaryInterface op) { - inQubits[qubitId] = op.getAllInQubits()[0]; + auto prependSingleQubitGate = [&](UnitaryOpInterface op) { + inQubits[qubitId] = op.getInputQubit(0); gates.insert(gates.begin(), {.op = op, .qubitIds = {qubitId}}); // outQubits do not need to be updated because the final out qubit is // already fixed }; while (auto* op = inQubits[qubitId].getDefiningOp()) { - auto unitaryOp = mlir::dyn_cast(op); + auto unitaryOp = mlir::dyn_cast(op); if (unitaryOp && helpers::isSingleQubitOperation(unitaryOp) && !isBarrier(unitaryOp)) { prependSingleQubitGate(unitaryOp); @@ -367,37 +370,14 @@ struct GateDecompositionPattern final } } - [[nodiscard]] static bool isBarrier(UnitaryInterface op) { + [[nodiscard]] static bool isBarrier(UnitaryOpInterface op) { return llvm::isa_and_nonnull(*op); } }; - /** - * @brief Creates a new rotation gate with no controls. - * - * @tparam OpType The type of the operation to be created. - * @param op The first instance of the rotation gate. - * @param rewriter The pattern rewriter. - * @return A new rotation gate. - */ - template - static OpType createOneParameterGate(mlir::PatternRewriter& rewriter, - mlir::Location location, fp parameter, - mlir::ValueRange inQubits) { - auto parameterValue = rewriter.create( - location, rewriter.getF64Type(), rewriter.getF64FloatAttr(parameter)); - - return rewriter.create( - location, inQubits.getType(), mlir::TypeRange{}, mlir::TypeRange{}, - mlir::DenseF64ArrayAttr{}, mlir::DenseBoolArrayAttr{}, - mlir::ValueRange{parameterValue}, inQubits, mlir::ValueRange{}, - mlir::ValueRange{}); - } - template static OpType createGate(mlir::PatternRewriter& rewriter, mlir::Location location, mlir::ValueRange inQubits, - mlir::ValueRange ctrlQubits, const llvm::SmallVector& parameters) { mlir::SmallVector parameterValues; for (auto&& parameter : parameters) { @@ -406,10 +386,17 @@ struct GateDecompositionPattern final parameterValues.push_back(parameterValue); } - return rewriter.create( - location, inQubits.getType(), ctrlQubits.getType(), mlir::TypeRange{}, - mlir::DenseF64ArrayAttr{}, mlir::DenseBoolArrayAttr{}, parameterValues, - inQubits, ctrlQubits, mlir::ValueRange{}); + return rewriter.create(location, inQubits, parameterValues); + } + + template + static CtrlOp + createControlledGate(mlir::PatternRewriter& rewriter, mlir::Location location, + mlir::ValueRange inQubits, mlir::ValueRange ctrlQubits, + const llvm::SmallVector& parameters) { + auto op = createGate(rewriter, location, inQubits, parameters); + auto opOutQubits = op->getResults(); + return rewriter.create(location, ctrlQubits, opOutQubits); } static void applySeries(mlir::PatternRewriter& rewriter, @@ -423,21 +410,18 @@ struct GateDecompositionPattern final auto updateInQubits = [&inQubits](const llvm::SmallVector& qubitIds, auto&& newGate) { - // TODO: need to handle controls differently? - auto results = newGate.getAllOutQubits(); if (qubitIds.size() == 2) { - inQubits[qubitIds[0]] = results[0]; - inQubits[qubitIds[1]] = results[1]; + inQubits[qubitIds[0]] = newGate.getOutputQubit(0); + inQubits[qubitIds[1]] = newGate.getOutputQubit(1); } else if (qubitIds.size() == 1) { - inQubits[qubitIds[0]] = results[0]; + inQubits[qubitIds[0]] = newGate.getOutputQubit(0); } else { throw std::logic_error{"Invalid number of qubit IDs!"}; } }; if (sequence.hasGlobalPhase()) { - createOneParameterGate(rewriter, location, sequence.globalPhase, - {}); + createGate(rewriter, location, {}, {sequence.globalPhase}); } matrix4x4 unitaryMatrix = helpers::kroneckerProduct( @@ -450,12 +434,12 @@ struct GateDecompositionPattern final if (gate.type == qc::X) { mlir::SmallVector inCtrlQubits; if (gate.qubitId.size() > 1) { - // controls come first + // controls come last inCtrlQubits.push_back(inQubits[gate.qubitId[1]]); } - auto newGate = - createGate(rewriter, location, {inQubits[gate.qubitId[0]]}, - inCtrlQubits, gate.parameter); + auto newGate = createControlledGate(rewriter, location, + {inQubits[gate.qubitId[0]]}, + inCtrlQubits, gate.parameter); updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RX) { mlir::SmallVector qubits; @@ -463,7 +447,7 @@ struct GateDecompositionPattern final qubits.push_back(inQubits[x]); } auto newGate = - createGate(rewriter, location, qubits, {}, gate.parameter); + createGate(rewriter, location, qubits, gate.parameter); updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RY) { mlir::SmallVector qubits; @@ -471,7 +455,7 @@ struct GateDecompositionPattern final qubits.push_back(inQubits[x]); } auto newGate = - createGate(rewriter, location, qubits, {}, gate.parameter); + createGate(rewriter, location, qubits, gate.parameter); updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RZ) { mlir::SmallVector qubits; @@ -479,7 +463,7 @@ struct GateDecompositionPattern final qubits.push_back(inQubits[x]); } auto newGate = - createGate(rewriter, location, qubits, {}, gate.parameter); + createGate(rewriter, location, qubits, gate.parameter); updateInQubits(gate.qubitId, newGate); } else { throw std::runtime_error{"Unknown gate type!"}; @@ -520,4 +504,4 @@ void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns) { eulerBases); } -} // namespace mqt::ir::opt +} // namespace mlir::qco diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir deleted file mode 100644 index 87f8665488..0000000000 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-decomposition.mlir +++ /dev/null @@ -1,540 +0,0 @@ -// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM -// Copyright (c) 2025 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file --gate-decomposition | FileCheck %s - -// ----- -// This test checks if a series equal to the identity will be cancelled out. - -module { - // CHECK-LABEL: func.func @testIdentitySeries - func.func @testIdentitySeries() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // CHECK-NOT: mqtopt.[[ANY:.*]] - - // CHECK: mqtopt.deallocQubit %[[Q0_0]] - // CHECK: mqtopt.deallocQubit %[[Q1_0]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_2, %q1_2 = mqtopt.x() %q0_1 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3, %q1_3 = mqtopt.x() %q0_2 ctrl %q1_2: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_4, %q1_4 = mqtopt.x() %q0_3 ctrl %q1_3: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_5, %q1_5 = mqtopt.x() %q0_4 ctrl %q1_4: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_6, %q1_6 = mqtopt.x() %q0_5 ctrl %q1_5: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_7, %q1_7 = mqtopt.x() %q0_6 ctrl %q1_6: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_8, %q1_8 = mqtopt.x() %q0_7 ctrl %q1_7: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_9, %q1_9 = mqtopt.x() %q0_8 ctrl %q1_8: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_10, %q1_10 = mqtopt.x() %q0_9 ctrl %q1_9: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_11, %q1_11 = mqtopt.x() %q0_10 ctrl %q1_10: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_12, %q1_12 = mqtopt.x() %q0_11 ctrl %q1_11: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_13, %q1_13 = mqtopt.x() %q0_12 ctrl %q1_12: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_14, %q1_14 = mqtopt.x() %q0_13 ctrl %q1_13: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_15, %q1_15 = mqtopt.x() %q0_14 ctrl %q1_14: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_16, %q1_16 = mqtopt.x() %q0_15 ctrl %q1_15: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_17, %q1_17 = mqtopt.x() %q0_16 ctrl %q1_16: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_18, %q1_18 = mqtopt.x() %q0_17 ctrl %q1_17: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_19, %q1_19 = mqtopt.x() %q0_18 ctrl %q1_18: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_20, %q1_20 = mqtopt.x() %q0_19 ctrl %q1_19: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_21, %q1_21 = mqtopt.x() %q0_20 ctrl %q1_20: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_22, %q1_22 = mqtopt.x() %q0_21 ctrl %q1_21: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_23, %q1_23 = mqtopt.x() %q0_22 ctrl %q1_22: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_24, %q1_24 = mqtopt.x() %q0_23 ctrl %q1_23: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_25, %q1_25 = mqtopt.x() %q0_24 ctrl %q1_24: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_27, %q1_27 = mqtopt.x() %q0_25 ctrl %q1_25: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_28, %q1_28 = mqtopt.x() %q0_27 ctrl %q1_27: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_29, %q1_29 = mqtopt.x() %q0_28 ctrl %q1_28: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_30, %q1_30 = mqtopt.x() %q0_29 ctrl %q1_29: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_31, %q1_31 = mqtopt.x() %q0_30 ctrl %q1_30: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_32, %q1_32 = mqtopt.x() %q0_31 ctrl %q1_31: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_33, %q1_33 = mqtopt.x() %q0_32 ctrl %q1_32: !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_33 - mqtopt.deallocQubit %q1_33 - - return - } -} - -// ----- -// This test checks if an odd number of CNOT gates will be reduced to a single CNOT. - -module { - // CHECK-LABEL: func.func @testOddNegationSeries - func.func @testOddNegationSeries() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] - - // ensure no other operations are inserted - // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) - - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q1_1]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_2, %q1_2 = mqtopt.x() %q0_1 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3, %q1_3 = mqtopt.x() %q0_2 ctrl %q1_2: !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_3 - mqtopt.deallocQubit %q1_3 - - return - } -} - -// ----- -// This test checks if an odd number of CNOT gates with ctrl/target swapped will be reduced to a single CNOT. - -module { - // CHECK-LABEL: func.func @testCNotOtherDirection - func.func @testCNotOtherDirection() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // CHECK-NOT: mqtopt.i(%[[ANY:.*]]) - // CHECK: %[[Q1_1:.*]], %[[Q0_1:.*]] = mqtopt.x() %[[Q1_0]] ctrl %[[Q0_0]] - - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q1_1]] - - // ensure no other operations are inserted - // CHECK-NOT: mqtopt.[[ANY:.*]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q1_1, %q0_1 = mqtopt.i() %q1_0 ctrl %q0_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_2, %q0_2 = mqtopt.x() %q1_1 ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_3, %q0_3 = mqtopt.i() %q1_2 ctrl %q0_2: !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_3 - mqtopt.deallocQubit %q1_3 - - return - } -} - -// ----- -// This test checks if a two-qubit series containing a single-qubit gate is decomposed correctly. - -module { - // CHECK-LABEL: func.func @testSeriesOneQubitOpInbetween - func.func @testSeriesOneQubitOpInbetween() { - // CHECK: %[[C0:.*]] = arith.constant 3.14159 - // CHECK: %[[C1:.*]] = arith.constant 1.5707 - - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // CHECK: mqtopt.gphase(%[[C1]]) - // CHECK: %[[Q0_1:.*]] = mqtopt.rx(%[[C0]]) %[[Q0_0]] - - // ensure no other operations are inserted - // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) - - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q1_0]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_2 = mqtopt.x() %q0_1: !mqtopt.Qubit - %q0_3, %q1_2 = mqtopt.x() %q0_2 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_3 - mqtopt.deallocQubit %q1_2 - - return - } -} - -// ----- -// This test checks if a two-qubit series starting on a single-qubit gate is decomposed correctly. - -module { - // CHECK-LABEL: func.func @testSeriesStartingOneQubitOp - func.func @testSeriesStartingOneQubitOp() { - // CHECK: %[[C0:.*]] = arith.constant 3.14159 - // CHECK: %[[C1:.*]] = arith.constant 1.5707 - - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // CHECK: mqtopt.gphase(%[[C1]]) - // CHECK: %[[Q0_1:.*]] = mqtopt.rx(%[[C0]]) %[[Q0_0]] - - // ensure no other operations are inserted - // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) - - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q1_0]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.x() %q0_0: !mqtopt.Qubit - %q0_2, %q1_1 = mqtopt.x() %q0_1 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3, %q1_2 = mqtopt.x() %q0_2 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_3 - mqtopt.deallocQubit %q1_2 - - return - } -} - -// ----- -// This test checks if a two-qubit series ending on a single-qubit gate is decomposed correctly. - -module { - // CHECK-LABEL: func.func @testSeriesEndingOneQubitOp - func.func @testSeriesEndingOneQubitOp() { - // CHECK: %[[C0:.*]] = arith.constant 3.14159 - // CHECK: %[[C1:.*]] = arith.constant 1.5707 - - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // CHECK: mqtopt.gphase(%[[C1]]) - // CHECK: %[[Q0_1:.*]] = mqtopt.rx(%[[C0]]) %[[Q0_0]] - - // ensure no other operations are inserted - // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) - - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q1_0]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_2, %q1_2 = mqtopt.x() %q0_1 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3 = mqtopt.x() %q0_2: !mqtopt.Qubit - - mqtopt.deallocQubit %q0_3 - mqtopt.deallocQubit %q1_2 - - return - } -} - -// ----- -// This test checks if an interrupted series is ignored correctly. - -module { - // CHECK-LABEL: func.func @testInterruptedSeries - func.func @testInterruptedSeries() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit - - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] - // CHECK: %[[Q1_2:.*]], %[[Q0_2:.*]] = mqtopt.x() %[[Q1_1]] ctrl %[[Q0_1]] - // CHECK: %[[Q0_3:.*]], %[[Q2_1:.*]] = mqtopt.x() %[[Q0_2]] ctrl %[[Q2_0]] - // CHECK: %[[Q1_3:.*]], %[[Q0_4:.*]] = mqtopt.x() %[[Q1_2]] ctrl %[[Q0_3]] - // CHECK: %[[Q0_5:.*]], %[[Q1_4:.*]] = mqtopt.x() %[[Q0_4]] ctrl %[[Q1_3]] - - // ensure no other operations are inserted - // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) - - // CHECK: mqtopt.deallocQubit %[[Q0_5]] - // CHECK: mqtopt.deallocQubit %[[Q1_4]] - // CHECK: mqtopt.deallocQubit %[[Q2_1]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_2, %q0_2 = mqtopt.x() %q1_1 ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3, %q2_1 = mqtopt.x() %q0_2 ctrl %q2_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_3, %q0_4 = mqtopt.x() %q1_2 ctrl %q0_3: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_5, %q1_4 = mqtopt.x() %q0_4 ctrl %q1_3: !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_5 - mqtopt.deallocQubit %q1_4 - mqtopt.deallocQubit %q2_1 - - return - } -} - -// ----- -// This test checks if a complex series containing two basis gates is decomposed correctly. - -module { - // CHECK-LABEL: func.func @testTwoBasisGateDecomposition - func.func @testTwoBasisGateDecomposition() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] - // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] - // CHECK-NOT: mqtopt.x() - - // CHECK: mqtopt.deallocQubit %[[ANY:.*]] - // CHECK: mqtopt.deallocQubit %[[ANY:.*]] - - %cst0 = arith.constant 2.5 : f64 - %cst1 = arith.constant 1.2 : f64 - %cst2 = arith.constant 0.5 : f64 - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_x, %q1_x = mqtopt.i() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_1 = mqtopt.h() %q0_x: !mqtopt.Qubit - %q1_1, %q0_2 = mqtopt.x() %q1_x ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3, %q1_2 = mqtopt.rzz(%cst0) %q0_2, %q1_1: !mqtopt.Qubit, !mqtopt.Qubit - %q1_3 = mqtopt.ry(%cst1) %q1_2: !mqtopt.Qubit - %q0_4 = mqtopt.rx(%cst1) %q0_3: !mqtopt.Qubit - %q0_5, %q1_4 = mqtopt.x() %q0_4 ctrl %q1_3: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_6 = mqtopt.rz(%cst2) %q0_5: !mqtopt.Qubit - // make series longer to enforce decomposition - %q0_7, %q1_5 = mqtopt.i() %q0_6 ctrl %q1_4: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_8, %q1_6 = mqtopt.i() %q0_7 ctrl %q1_5: !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_8 - mqtopt.deallocQubit %q1_6 - - return - } -} - -// ----- -// This test checks if a complex series containing at least three basis gates is decomposed correctly. - -module { - // CHECK-LABEL: func.func @testThreeBasisGateDecomposition - func.func @testThreeBasisGateDecomposition() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] - // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] - // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] - // CHECK-NOT: mqtopt.x() - - // CHECK: mqtopt.deallocQubit %[[ANY:.*]] - // CHECK: mqtopt.deallocQubit %[[ANY:.*]] - - %cst0 = arith.constant 2.5 : f64 - %cst1 = arith.constant 1.2 : f64 - %cst2 = arith.constant 0.5 : f64 - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - - %q0_x, %q1_x = mqtopt.i() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_1 = mqtopt.h() %q0_x: !mqtopt.Qubit - %q1_1, %q0_2 = mqtopt.x() %q1_x ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3, %q1_2 = mqtopt.rzz(%cst0) %q0_2, %q1_1: !mqtopt.Qubit, !mqtopt.Qubit - %q1_3 = mqtopt.ry(%cst1) %q1_2: !mqtopt.Qubit - %q0_4 = mqtopt.rx(%cst1) %q0_3: !mqtopt.Qubit - %q0_5, %q1_4 = mqtopt.x() %q0_4 ctrl %q1_3: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_6 = mqtopt.rz(%cst2) %q0_5: !mqtopt.Qubit - %q0_7, %q1_5 = mqtopt.rxx(%cst0) %q0_6, %q1_4: !mqtopt.Qubit, !mqtopt.Qubit - %q0_8, %q1_6 = mqtopt.ryy(%cst2) %q0_7, %q1_5: !mqtopt.Qubit, !mqtopt.Qubit - // make series longer to enforce decomposition - %q0_9, %q1_7 = mqtopt.i() %q0_8 ctrl %q1_6: !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_9 - mqtopt.deallocQubit %q1_7 - mqtopt.deallocQubit %q2_0 - - return - } -} - -// ----- -// This test checks if the repeated application of the decomposition works by "interrupting" the first series and then having a second one. - -module { - // CHECK-LABEL: func.func @testRepeatedDecomposition - func.func @testRepeatedDecomposition() { - return - } -} - -// ----- -// This test checks if two single-qubit series (connected by an identity) remain separate without the insertion of a basis gate. - -module { - // CHECK-LABEL: func.func @testSingleQubitSeries - func.func @testSingleQubitSeries() { - // CHECK: %[[C0:.*]] = arith.constant 2.400000 - // CHECK: %[[C1:.*]] = arith.constant 2.500000 - - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // CHECK: %[[Q0_1:.*]] = mqtopt.ry(%[[C1]]) %[[Q0_0]] - // CHECK: %[[Q1_1:.*]] = mqtopt.rx(%[[C0]]) %[[Q1_0]] - - // ensure no other operations are inserted - // CHECK-NOT: mqtopt.[[ANY:.*]](%[[ANY:.*]]) - - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q1_1]] - - %cst0 = arith.constant 2.5 : f64 - %cst1 = arith.constant 1.2 : f64 - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.i() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_2 = mqtopt.ry(%cst0) %q0_1: !mqtopt.Qubit - %q1_2 = mqtopt.rx(%cst1) %q1_1: !mqtopt.Qubit - %q1_3 = mqtopt.rx(%cst1) %q1_2: !mqtopt.Qubit - - mqtopt.deallocQubit %q0_2 - mqtopt.deallocQubit %q1_3 - - return - } -} - -// ----- -// This test checks if a two-qubit series starting with two separate single-qubit chains is fully decomposed. - -module { - // CHECK-LABEL: func.func @testSeriesSingleQubitBacktracking - func.func @testSeriesSingleQubitBacktracking() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // CHECK: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] - // CHECK-NOT: mqtopt.x() - // CHECK-NOT: mqtopt.h() - - // CHECK: mqtopt.deallocQubit %[[ANY:.*]] - // CHECK: mqtopt.deallocQubit %[[ANY:.*]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %cst0 = arith.constant 1.5 : f64 - %cst1 = arith.constant 0.2 : f64 - %cst2 = arith.constant 0.9 : f64 - - %q0_1 = mqtopt.h() %q0_0: !mqtopt.Qubit - %q0_2 = mqtopt.rx(%cst0) %q0_1: !mqtopt.Qubit - %q0_3 = mqtopt.x() %q0_2: !mqtopt.Qubit - %q1_1 = mqtopt.h() %q1_0: !mqtopt.Qubit - %q1_2 = mqtopt.rz(%cst0) %q1_1: !mqtopt.Qubit - %q0_4, %q1_3 = mqtopt.x() %q0_3 ctrl %q1_2: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_4, %q0_5 = mqtopt.i() %q1_3 ctrl %q0_4: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_5, %q0_6 = mqtopt.i() %q1_4 ctrl %q0_5: !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_6 - mqtopt.deallocQubit %q1_5 - - return - } -} - -// ----- -// This test checks if a single-qubit series that is equal to the identity is fully removed. - -module { - // CHECK-LABEL: func.func @testNegationSeries - func.func @testNegationSeries() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - - // CHECK-NOT: %[[ANY:.*]] = mqtopt.x - - // CHECK: mqtopt.deallocQubit %[[Q0_0]] - - %q0_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.x() %q0_0 : !mqtopt.Qubit - %q0_2 = mqtopt.x() %q0_1 : !mqtopt.Qubit - %q0_3 = mqtopt.x() %q0_2 : !mqtopt.Qubit - %q0_4 = mqtopt.x() %q0_3 : !mqtopt.Qubit - %q0_5 = mqtopt.x() %q0_4 : !mqtopt.Qubit - %q0_6 = mqtopt.x() %q0_5 : !mqtopt.Qubit - - mqtopt.deallocQubit %q0_6 - - return - } -} - -// ----- -// This test checks if a single-qubit series with consecutive rotations will be simplified. - -module { - // CHECK-LABEL: func.func @testMergeRotationSeries - func.func @testMergeRotationSeries() { - // CHECK: %[[C_0:.*]] = arith.constant 2.000000e+00 - - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - - // CHECK: %[[Q0_1:.*]] = mqtopt.rx(%[[C_0]]) %[[Q0_0]] - - // CHECK-NOT: mqtopt.[[ANY:.*]]([[ANY:.*]]) - - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - - %q0_0 = mqtopt.allocQubit - - %c_0 = arith.constant 0.500000e+00 : f64 - %q0_1 = mqtopt.rx(%c_0) %q0_0 : !mqtopt.Qubit - %q0_2 = mqtopt.rx(%c_0) %q0_1 : !mqtopt.Qubit - %q0_3 = mqtopt.rx(%c_0) %q0_2 : !mqtopt.Qubit - %q0_4 = mqtopt.rx(%c_0) %q0_3 : !mqtopt.Qubit - - mqtopt.deallocQubit %q0_4 - - return - } -} - -// ----- -// This test checks if a single-qubit series that cannot be simplified remains as-is. - -module { - // CHECK-LABEL: func.func @testNoMergeSmallSeries - func.func @testNoMergeSmallSeries() { - // CHECK: %[[C_0:.*]] = arith.constant 1.000000e+00 - - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - - // CHECK: %[[Q0_1:.*]] = mqtopt.rx(%[[C_0]]) %[[Q0_0:.*]] - // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[C_0]]) %[[Q0_1:.*]] - // CHECK: %[[Q0_3:.*]] = mqtopt.rz(%[[C_0]]) %[[Q0_2:.*]] - - // CHECK-NOT: %[[ANY:.*]] = mqtopt.rz - // CHECK-NOT: %[[ANY:.*]] = mqtopt.ry - - // CHECK: mqtopt.deallocQubit %[[Q0_3]] - - %q0_0 = mqtopt.allocQubit - - %c_0 = arith.constant 1.000000e+00 : f64 - %q0_1 = mqtopt.rx(%c_0) %q0_0 : !mqtopt.Qubit - %q0_2 = mqtopt.ry(%c_0) %q0_1 : !mqtopt.Qubit - %q0_3 = mqtopt.rz(%c_0) %q0_2 : !mqtopt.Qubit - - mqtopt.deallocQubit %q0_3 - - return - } -} diff --git a/mlir/unittests/CMakeLists.txt b/mlir/unittests/CMakeLists.txt index b8bf8eb187..32a1a44fac 100644 --- a/mlir/unittests/CMakeLists.txt +++ b/mlir/unittests/CMakeLists.txt @@ -13,5 +13,6 @@ add_subdirectory(translation) add_custom_target(mqt-core-mlir-unittests) -add_dependencies(mqt-core-mlir-unittests mqt-core-mlir-compiler-pipeline-test - mqt-core-mlir-translation-test mqt-core-mlir-wireiterator-test) +add_dependencies( + mqt-core-mlir-unittests mqt-core-mlir-decomposition-test mqt-core-mlir-compiler-pipeline-test + mqt-core-mlir-translation-test mqt-core-mlir-wireiterator-test) diff --git a/mlir/unittests/decomposition/CMakeLists.txt b/mlir/unittests/decomposition/CMakeLists.txt index 9aa82c9edb..f845b432a8 100644 --- a/mlir/unittests/decomposition/CMakeLists.txt +++ b/mlir/unittests/decomposition/CMakeLists.txt @@ -13,8 +13,16 @@ if(NOT TARGET ${testname}) # create an executable in which the tests will be stored add_executable(${testname} ${DECOMPOSITION_TEST_SOURCES}) # link the Google test infrastructure and a default main function to the test executable. - target_link_libraries(${testname} PRIVATE GTest::gmock GTest::gtest_main LLVMFileCheck MLIRPass - MLIRTransforms MLIRMQTOptTransforms) + target_link_libraries( + ${testname} + PRIVATE GTest::gmock + GTest::gtest_main + LLVMFileCheck + MLIRPass + MLIRTransforms + MQTCompilerPipeline + MQT::CoreIR + Eigen3::Eigen) # discover tests gtest_discover_tests(${testname} DISCOVERY_TIMEOUT 60) set_target_properties(${testname} PROPERTIES FOLDER unittests) diff --git a/mlir/unittests/decomposition/test_basis_decomposer.cpp b/mlir/unittests/decomposition/test_basis_decomposer.cpp index 7eaeb2a77c..7f9221f5af 100644 --- a/mlir/unittests/decomposition/test_basis_decomposer.cpp +++ b/mlir/unittests/decomposition/test_basis_decomposer.cpp @@ -9,13 +9,13 @@ */ #include "ir/operations/OpType.hpp" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/BasisDecomposer.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerBasis.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Gate.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h" +#include "mlir/Passes/Decomposition/BasisDecomposer.h" +#include "mlir/Passes/Decomposition/EulerBasis.h" +#include "mlir/Passes/Decomposition/Gate.h" +#include "mlir/Passes/Decomposition/GateSequence.h" +#include "mlir/Passes/Decomposition/Helpers.h" +#include "mlir/Passes/Decomposition/UnitaryMatrices.h" +#include "mlir/Passes/Decomposition/WeylDecomposition.h" #include #include @@ -28,8 +28,8 @@ #include #include -using namespace mqt::ir::opt; -using namespace mqt::ir::opt::decomposition; +using namespace mlir::qco; +using namespace mlir::qco::decomposition; namespace { [[nodiscard]] matrix4x4 randomUnitaryMatrix() { diff --git a/mlir/unittests/decomposition/test_euler_decomposition.cpp b/mlir/unittests/decomposition/test_euler_decomposition.cpp index a2eea0013f..442bbb9c3c 100644 --- a/mlir/unittests/decomposition/test_euler_decomposition.cpp +++ b/mlir/unittests/decomposition/test_euler_decomposition.cpp @@ -8,11 +8,11 @@ * Licensed under the MIT License */ -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerBasis.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/EulerDecomposition.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/GateSequence.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h" +#include "mlir/Passes/Decomposition/EulerBasis.h" +#include "mlir/Passes/Decomposition/EulerDecomposition.h" +#include "mlir/Passes/Decomposition/GateSequence.h" +#include "mlir/Passes/Decomposition/Helpers.h" +#include "mlir/Passes/Decomposition/UnitaryMatrices.h" #include #include @@ -25,8 +25,8 @@ #include #include -using namespace mqt::ir::opt; -using namespace mqt::ir::opt::decomposition; +using namespace mlir::qco; +using namespace mlir::qco::decomposition; namespace { [[nodiscard]] matrix2x2 randomUnitaryMatrix() { diff --git a/mlir/unittests/decomposition/test_weyl_decomposition.cpp b/mlir/unittests/decomposition/test_weyl_decomposition.cpp index 77e68fcb8b..9b205abe2b 100644 --- a/mlir/unittests/decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/decomposition/test_weyl_decomposition.cpp @@ -9,9 +9,9 @@ */ #include "ir/operations/OpType.hpp" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/Helpers.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/UnitaryMatrices.h" -#include "mlir/Dialect/MQTOpt/Transforms/Decomposition/WeylDecomposition.h" +#include "mlir/Passes/Decomposition/Helpers.h" +#include "mlir/Passes/Decomposition/UnitaryMatrices.h" +#include "mlir/Passes/Decomposition/WeylDecomposition.h" #include #include @@ -21,8 +21,8 @@ #include #include -using namespace mqt::ir::opt; -using namespace mqt::ir::opt::decomposition; +using namespace mlir::qco; +using namespace mlir::qco::decomposition; namespace { [[nodiscard]] matrix4x4 randomUnitaryMatrix() { From 9a7006b69739b97ea72e94b26352f511a1e22e80 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 20 Dec 2025 12:01:03 +0100 Subject: [PATCH 143/237] add GateDecompositionPass to compiler pipeline --- mlir/include/mlir/Compiler/CompilerPipeline.h | 5 +++++ mlir/lib/Compiler/CMakeLists.txt | 3 ++- mlir/lib/Compiler/CompilerPipeline.cpp | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/mlir/include/mlir/Compiler/CompilerPipeline.h b/mlir/include/mlir/Compiler/CompilerPipeline.h index 8e8ae639d9..7c097e6ea6 100644 --- a/mlir/include/mlir/Compiler/CompilerPipeline.h +++ b/mlir/include/mlir/Compiler/CompilerPipeline.h @@ -119,6 +119,11 @@ class QuantumCompilerPipeline { */ static void addCleanupPasses(PassManager& pm); + /** + * @brief Add all available optimization passes + */ + static void addOptimizationPasses(PassManager& pm); + /** * @brief Configure PassManager with diagnostic options * diff --git a/mlir/lib/Compiler/CMakeLists.txt b/mlir/lib/Compiler/CMakeLists.txt index 1e4483754d..7096a2554e 100644 --- a/mlir/lib/Compiler/CMakeLists.txt +++ b/mlir/lib/Compiler/CMakeLists.txt @@ -21,7 +21,8 @@ add_mlir_library( QCOToQC QCToQIR MQT::MLIRSupport - MQT::ProjectOptions) + MQT::ProjectOptions + QcoPasses) # collect header files file(GLOB_RECURSE COMPILER_HEADERS_SOURCE "${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Compiler/*.h") diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index d8cbd3b8be..2375ce8ca1 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -13,6 +13,7 @@ #include "mlir/Conversion/QCOToQC/QCOToQC.h" #include "mlir/Conversion/QCToQCO/QCToQCO.h" #include "mlir/Conversion/QCToQIR/QCToQIR.h" +#include "mlir/Passes/Passes.h" #include "mlir/Support/PrettyPrinting.h" #include @@ -64,6 +65,11 @@ void QuantumCompilerPipeline::addCleanupPasses(PassManager& pm) { pm.addPass(createRemoveDeadValuesPass()); } +void QuantumCompilerPipeline::addOptimizationPasses(PassManager& pm) { + // Always run all optimization passes for now + pm.addPass(qco::createGateDecompositionPass()); +} + void QuantumCompilerPipeline::configurePassManager(PassManager& pm) const { // Enable timing statistics if requested if (config_.enableTiming) { From 935ad7dc6cbe91fc38cba59f93b8c08584d426ff Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 20 Dec 2025 11:02:02 +0000 Subject: [PATCH 144/237] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/lib/Passes/CMakeLists.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mlir/lib/Passes/CMakeLists.txt b/mlir/lib/Passes/CMakeLists.txt index d94f247083..8632a3cca7 100644 --- a/mlir/lib/Passes/CMakeLists.txt +++ b/mlir/lib/Passes/CMakeLists.txt @@ -12,7 +12,14 @@ add_compile_options(-fexceptions) file(GLOB_RECURSE PASSES_SOURCES *.cpp) -add_mlir_library(QcoPasses ${PASSES_SOURCES} LINK_LIBS PUBLIC ${LIBRARIES} DEPENDS QcoPassesIncGen) +add_mlir_library( + QcoPasses + ${PASSES_SOURCES} + LINK_LIBS + PUBLIC + ${LIBRARIES} + DEPENDS + QcoPassesIncGen) # collect header files file(GLOB_RECURSE PASSES_HEADERS_SOURCE ${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Passes/*.h) From d296638c0d7b342513ff07b3ac8e07091f96d5b5 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 28 Dec 2025 17:10:12 +0100 Subject: [PATCH 145/237] add UnitaryMatrixType to operation and add Eigen dependency to dialect --- mlir/include/mlir/Dialect/QCO/IR/CMakeLists.txt | 2 +- mlir/include/mlir/Dialect/QCO/IR/QCODialect.h | 3 +++ mlir/lib/Dialect/QCO/IR/CMakeLists.txt | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/IR/CMakeLists.txt b/mlir/include/mlir/Dialect/QCO/IR/CMakeLists.txt index bddaba3d7a..fd4b0372d1 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/CMakeLists.txt +++ b/mlir/include/mlir/Dialect/QCO/IR/CMakeLists.txt @@ -7,6 +7,6 @@ # Licensed under the MIT License add_mlir_dialect(QCOOps qco) -add_mlir_interface(QCOInterfaces) +add_mlir_interface(QCOInterfaces LINK_LIBS PUBLIC Eigen3::Eigen) add_mlir_doc(QCOOps MLIRQCODialect Dialects/ -gen-dialect-doc) add_mlir_doc(QCOInterfaces MLIRQCOInterfaces Dialects/ -gen-op-interface-docs -dialect=qco) diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h index d1087ab6f7..455c49543e 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h +++ b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h @@ -29,6 +29,7 @@ #include #include #include +#include #define DIALECT_NAME_QCO "qco" @@ -66,6 +67,8 @@ template class TargetAndParameterArityTrait { template class Impl : public OpTrait::TraitBase { public: + using UnitaryMatrixType = Eigen::Matrix, 1 << T, 1 << T>; + static size_t getNumQubits() { return T; } static size_t getNumTargets() { return T; } static size_t getNumControls() { return 0; } diff --git a/mlir/lib/Dialect/QCO/IR/CMakeLists.txt b/mlir/lib/Dialect/QCO/IR/CMakeLists.txt index 567a80611b..028d3520b9 100644 --- a/mlir/lib/Dialect/QCO/IR/CMakeLists.txt +++ b/mlir/lib/Dialect/QCO/IR/CMakeLists.txt @@ -24,7 +24,8 @@ add_mlir_dialect_library( LINK_LIBS PUBLIC MLIRIR - MLIRSideEffectInterfaces) + MLIRSideEffectInterfaces + Eigen3::Eigen) # collect header files file(GLOB_RECURSE IR_HEADERS_SOURCE "${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Dialect/QCO/IR/*.h") From a8dbfcba6f1896da7a1cd242147ba778970f6b85 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 28 Dec 2025 17:24:47 +0100 Subject: [PATCH 146/237] first non-working attempt --- .../mlir/Dialect/QCO/IR/QCOInterfaces.td | 17 ++++- mlir/include/mlir/Dialect/QCO/IR/QCOOps.td | 62 +++++++++---------- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td index 8e71a52a55..82ed149164 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td @@ -15,7 +15,7 @@ include "mlir/IR/OpBase.td" // UnitaryOpInterface //===----------------------------------------------------------------------===// -def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> { +class UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> { let description = [{ This interface provides a unified API for all operations that apply or produce a unitary transformation in the QCO dialect. This includes base @@ -107,7 +107,22 @@ def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> { "Returns the base symbol/mnemonic of the operation.", "StringRef", "getBaseSymbol", (ins) >, + + // Unitary matrix methods + InterfaceMethod< + "Returns the unitary matrix definition of the operation.", + "Eigen::MatrixXcd", "getUnitaryMatrix", (ins), + matrixDefinition, [{ throw std::logic_error{ "Operation has no matrix definition!" } }] + >, ]; + + let extraClassDeclaration = [{ + template + MatrixType getUnitaryMatrix() { + static_assert(std::is_same_v, + "Provided matrix type does not equal expected type for the contained operation."); + } + }]; } #endif // QCO_INTERFACES diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td index 1dea491ab5..24b09a25e6 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td @@ -256,7 +256,7 @@ def TwoTargetTwoParameter : TargetAndParameterArityTrait<2, 2>; // Unitary Operations //===----------------------------------------------------------------------===// -def GPhaseOp : QCOOp<"gphase", traits = [UnitaryOpInterface, ZeroTargetOneParameter, MemoryEffects<[MemWrite]>]> { +def GPhaseOp : QCOOp<"gphase", traits = [UnitaryOpInterface<>, ZeroTargetOneParameter, MemoryEffects<[MemWrite]>]> { let summary = "Apply a global phase to the state"; let description = [{ Applies a global phase to the state. @@ -281,7 +281,7 @@ def GPhaseOp : QCOOp<"gphase", traits = [UnitaryOpInterface, ZeroTargetOneParame let hasCanonicalizer = 1; } -def IdOp : QCOOp<"id", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def IdOp : QCOOp<"id", traits = [UnitaryOpInterface<>, OneTargetZeroParameter]> { let summary = "Apply an Id gate to a qubit"; let description = [{ Applies an Id gate to a qubit and returns the transformed qubit. @@ -303,7 +303,7 @@ def IdOp : QCOOp<"id", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let hasCanonicalizer = 1; } -def XOp : QCOOp<"x", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def XOp : QCOOp<"x", traits = [UnitaryOpInterface<[{ return getMatrixX(); }]>, OneTargetZeroParameter]> { let summary = "Apply an X gate to a qubit"; let description = [{ Applies an X gate to a qubit and returns the transformed qubit. @@ -325,7 +325,7 @@ def XOp : QCOOp<"x", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let hasCanonicalizer = 1; } -def YOp : QCOOp<"y", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def YOp : QCOOp<"y", traits = [UnitaryOpInterface<[{ return getMatrixY(); }]>, OneTargetZeroParameter]> { let summary = "Apply a Y gate to a qubit"; let description = [{ Applies a Y gate to a qubit and returns the transformed qubit. @@ -347,7 +347,7 @@ def YOp : QCOOp<"y", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let hasCanonicalizer = 1; } -def ZOp : QCOOp<"z", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def ZOp : QCOOp<"z", traits = [UnitaryOpInterface<[{ return getMatrixZ(); }]>, OneTargetZeroParameter]> { let summary = "Apply a Z gate to a qubit"; let description = [{ Applies a Z gate to a qubit and returns the transformed qubit. @@ -369,7 +369,7 @@ def ZOp : QCOOp<"z", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let hasCanonicalizer = 1; } -def HOp : QCOOp<"h", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def HOp : QCOOp<"h", traits = [UnitaryOpInterface<[{ return getMatrixH(); }]>, OneTargetZeroParameter]> { let summary = "Apply a H gate to a qubit"; let description = [{ Applies a H gate to a qubit and returns the transformed qubit. @@ -391,7 +391,7 @@ def HOp : QCOOp<"h", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let hasCanonicalizer = 1; } -def SOp : QCOOp<"s", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def SOp : QCOOp<"s", traits = [UnitaryOpInterface<[{ return getMatrixS(); }]>, OneTargetZeroParameter]> { let summary = "Apply an S gate to a qubit"; let description = [{ Applies an S gate to a qubit and returns the transformed qubit. @@ -413,7 +413,7 @@ def SOp : QCOOp<"s", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let hasCanonicalizer = 1; } -def SdgOp : QCOOp<"sdg", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def SdgOp : QCOOp<"sdg", traits = [UnitaryOpInterface<>, OneTargetZeroParameter]> { let summary = "Apply an Sdg gate to a qubit"; let description = [{ Applies an Sdg gate to a qubit and returns the transformed qubit. @@ -435,7 +435,7 @@ def SdgOp : QCOOp<"sdg", traits = [UnitaryOpInterface, OneTargetZeroParameter]> let hasCanonicalizer = 1; } -def TOp : QCOOp<"t", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def TOp : QCOOp<"t", traits = [UnitaryOpInterface<>, OneTargetZeroParameter]> { let summary = "Apply a T gate to a qubit"; let description = [{ Applies a T gate to a qubit and returns the transformed qubit. @@ -457,7 +457,7 @@ def TOp : QCOOp<"t", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let hasCanonicalizer = 1; } -def TdgOp : QCOOp<"tdg", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def TdgOp : QCOOp<"tdg", traits = [UnitaryOpInterface<>, OneTargetZeroParameter]> { let summary = "Apply a Tdg gate to a qubit"; let description = [{ Applies a Tdg gate to a qubit and returns the transformed qubit. @@ -479,7 +479,7 @@ def TdgOp : QCOOp<"tdg", traits = [UnitaryOpInterface, OneTargetZeroParameter]> let hasCanonicalizer = 1; } -def SXOp : QCOOp<"sx", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def SXOp : QCOOp<"sx", traits = [UnitaryOpInterface<>, OneTargetZeroParameter]> { let summary = "Apply an SX gate to a qubit"; let description = [{ Applies an SX gate to a qubit and returns the transformed qubit. @@ -501,7 +501,7 @@ def SXOp : QCOOp<"sx", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let hasCanonicalizer = 1; } -def SXdgOp : QCOOp<"sxdg", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def SXdgOp : QCOOp<"sxdg", traits = [UnitaryOpInterface<>, OneTargetZeroParameter]> { let summary = "Apply an SXdg gate to a qubit"; let description = [{ Applies an SXdg gate to a qubit and returns the transformed qubit. @@ -523,7 +523,7 @@ def SXdgOp : QCOOp<"sxdg", traits = [UnitaryOpInterface, OneTargetZeroParameter] let hasCanonicalizer = 1; } -def RXOp : QCOOp<"rx", traits = [UnitaryOpInterface, OneTargetOneParameter]> { +def RXOp : QCOOp<"rx", traits = [UnitaryOpInterface<[{ return getMatrixRX($_op.getParameter(0)); }]>, OneTargetOneParameter]> { let summary = "Apply an RX gate to a qubit"; let description = [{ Applies an RX gate to a qubit and returns the transformed qubit. @@ -550,7 +550,7 @@ def RXOp : QCOOp<"rx", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let hasCanonicalizer = 1; } -def RYOp : QCOOp<"ry", traits = [UnitaryOpInterface, OneTargetOneParameter]> { +def RYOp : QCOOp<"ry", traits = [UnitaryOpInterface<>, OneTargetOneParameter]> { let summary = "Apply an RY gate to a qubit"; let description = [{ Applies an RY gate to a qubit and returns the transformed qubit. @@ -577,7 +577,7 @@ def RYOp : QCOOp<"ry", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let hasCanonicalizer = 1; } -def RZOp : QCOOp<"rz", traits = [UnitaryOpInterface, OneTargetOneParameter]> { +def RZOp : QCOOp<"rz", traits = [UnitaryOpInterface<>, OneTargetOneParameter]> { let summary = "Apply an RZ gate to a qubit"; let description = [{ Applies an RZ gate to a qubit and returns the transformed qubit. @@ -604,7 +604,7 @@ def RZOp : QCOOp<"rz", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let hasCanonicalizer = 1; } -def POp : QCOOp<"p", traits = [UnitaryOpInterface, OneTargetOneParameter]> { +def POp : QCOOp<"p", traits = [UnitaryOpInterface<>, OneTargetOneParameter]> { let summary = "Apply a P gate to a qubit"; let description = [{ Applies a P gate to a qubit and returns the transformed qubit. @@ -631,7 +631,7 @@ def POp : QCOOp<"p", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let hasCanonicalizer = 1; } -def ROp : QCOOp<"r", traits = [UnitaryOpInterface, OneTargetTwoParameter]> { +def ROp : QCOOp<"r", traits = [UnitaryOpInterface<>, OneTargetTwoParameter]> { let summary = "Apply an R gate to a qubit"; let description = [{ Applies an R gate to a qubit and returns the transformed qubit. @@ -659,7 +659,7 @@ def ROp : QCOOp<"r", traits = [UnitaryOpInterface, OneTargetTwoParameter]> { let hasCanonicalizer = 1; } -def U2Op : QCOOp<"u2", traits = [UnitaryOpInterface, OneTargetTwoParameter]> { +def U2Op : QCOOp<"u2", traits = [UnitaryOpInterface<>, OneTargetTwoParameter]> { let summary = "Apply a U2 gate to a qubit"; let description = [{ Applies a U2 gate to a qubit and returns the transformed qubit. @@ -687,7 +687,7 @@ def U2Op : QCOOp<"u2", traits = [UnitaryOpInterface, OneTargetTwoParameter]> { let hasCanonicalizer = 1; } -def UOp : QCOOp<"u", traits = [UnitaryOpInterface, OneTargetThreeParameter]> { +def UOp : QCOOp<"u", traits = [UnitaryOpInterface<>, OneTargetThreeParameter]> { let summary = "Apply a U gate to a qubit"; let description = [{ Applies a U gate to a qubit and returns the transformed qubit. @@ -716,7 +716,7 @@ def UOp : QCOOp<"u", traits = [UnitaryOpInterface, OneTargetThreeParameter]> { let hasCanonicalizer = 1; } -def SWAPOp : QCOOp<"swap", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> { +def SWAPOp : QCOOp<"swap", traits = [UnitaryOpInterface<>, TwoTargetZeroParameter]> { let summary = "Apply a SWAP gate to two qubits"; let description = [{ Applies a SWAP gate to two qubits and returns the transformed qubits. @@ -739,7 +739,7 @@ def SWAPOp : QCOOp<"swap", traits = [UnitaryOpInterface, TwoTargetZeroParameter] let hasCanonicalizer = 1; } -def iSWAPOp : QCOOp<"iswap", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> { +def iSWAPOp : QCOOp<"iswap", traits = [UnitaryOpInterface<>, TwoTargetZeroParameter]> { let summary = "Apply a iSWAP gate to two qubits"; let description = [{ Applies a iSWAP gate to two qubits and returns the transformed qubits. @@ -760,7 +760,7 @@ def iSWAPOp : QCOOp<"iswap", traits = [UnitaryOpInterface, TwoTargetZeroParamete }]; } -def DCXOp : QCOOp<"dcx", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> { +def DCXOp : QCOOp<"dcx", traits = [UnitaryOpInterface<>, TwoTargetZeroParameter]> { let summary = "Apply a DCX gate to two qubits"; let description = [{ Applies a DCX gate to two qubits and returns the transformed qubits. @@ -781,7 +781,7 @@ def DCXOp : QCOOp<"dcx", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> }]; } -def ECROp : QCOOp<"ecr", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> { +def ECROp : QCOOp<"ecr", traits = [UnitaryOpInterface<>, TwoTargetZeroParameter]> { let summary = "Apply an ECR gate to two qubits"; let description = [{ Applies an ECR gate to two qubits and returns the transformed qubits. @@ -804,7 +804,7 @@ def ECROp : QCOOp<"ecr", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> let hasCanonicalizer = 1; } -def RXXOp : QCOOp<"rxx", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { +def RXXOp : QCOOp<"rxx", traits = [UnitaryOpInterface<>, TwoTargetOneParameter]> { let summary = "Apply an RXX gate to two qubits"; let description = [{ Applies an RXX gate to two qubits and returns the transformed qubits. @@ -832,7 +832,7 @@ def RXXOp : QCOOp<"rxx", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let hasCanonicalizer = 1; } -def RYYOp : QCOOp<"ryy", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { +def RYYOp : QCOOp<"ryy", traits = [UnitaryOpInterface<>, TwoTargetOneParameter]> { let summary = "Apply an RYY gate to two qubits"; let description = [{ Applies an RYY gate to two qubits and returns the transformed qubits. @@ -860,7 +860,7 @@ def RYYOp : QCOOp<"ryy", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let hasCanonicalizer = 1; } -def RZXOp : QCOOp<"rzx", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { +def RZXOp : QCOOp<"rzx", traits = [UnitaryOpInterface<>, TwoTargetOneParameter]> { let summary = "Apply an RZX gate to two qubits"; let description = [{ Applies an RZX gate to two qubits and returns the transformed qubits. @@ -888,7 +888,7 @@ def RZXOp : QCOOp<"rzx", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let hasCanonicalizer = 1; } -def RZZOp : QCOOp<"rzz", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { +def RZZOp : QCOOp<"rzz", traits = [UnitaryOpInterface<>, TwoTargetOneParameter]> { let summary = "Apply an RZZ gate to two qubits"; let description = [{ Applies an RZZ gate to two qubits and returns the transformed qubits. @@ -916,7 +916,7 @@ def RZZOp : QCOOp<"rzz", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let hasCanonicalizer = 1; } -def XXPlusYYOp : QCOOp<"xx_plus_yy", traits = [UnitaryOpInterface, TwoTargetTwoParameter]> { +def XXPlusYYOp : QCOOp<"xx_plus_yy", traits = [UnitaryOpInterface<>, TwoTargetTwoParameter]> { let summary = "Apply an XX+YY gate to two qubits"; let description = [{ Applies an XX+YY gate to two qubits and returns the transformed qubits. @@ -945,7 +945,7 @@ def XXPlusYYOp : QCOOp<"xx_plus_yy", traits = [UnitaryOpInterface, TwoTargetTwoP let hasCanonicalizer = 1; } -def XXMinusYYOp : QCOOp<"xx_minus_yy", traits = [UnitaryOpInterface, TwoTargetTwoParameter]> { +def XXMinusYYOp : QCOOp<"xx_minus_yy", traits = [UnitaryOpInterface<>, TwoTargetTwoParameter]> { let summary = "Apply an XX-YY gate to two qubits"; let description = [{ Applies an XX-YY gate to two qubits and returns the transformed qubits. @@ -974,7 +974,7 @@ def XXMinusYYOp : QCOOp<"xx_minus_yy", traits = [UnitaryOpInterface, TwoTargetTw let hasCanonicalizer = 1; } -def BarrierOp : QCOOp<"barrier", traits = [UnitaryOpInterface]> { +def BarrierOp : QCOOp<"barrier", traits = [UnitaryOpInterface<>]> { let summary = "Apply a barrier gate to a set of qubits"; let description = [{ Applies a barrier gate to a set of qubits and returns the transformed qubits. @@ -1038,7 +1038,7 @@ def YieldOp : QCOOp<"yield", traits = [Terminator]> { def CtrlOp : QCOOp<"ctrl", traits = [ - UnitaryOpInterface, + UnitaryOpInterface<>, AttrSizedOperandSegments, AttrSizedResultSegments, SameOperandsAndResultType, From e9da9b0540fa92ebb048d5e0369502d669da6d92 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 4 Jan 2026 17:15:42 +0100 Subject: [PATCH 147/237] second non-working attempt --- mlir/include/mlir/Dialect/QCO/IR/QCODialect.h | 55 +++- .../mlir/Dialect/QCO/IR/QCOInterfaces.td | 25 +- mlir/include/mlir/Dialect/QCO/IR/QCOOps.td | 76 +++-- mlir/include/mlir/Dialect/Utils/MatrixUtils.h | 305 ++++++++++++++++++ 4 files changed, 416 insertions(+), 45 deletions(-) create mode 100644 mlir/include/mlir/Dialect/Utils/MatrixUtils.h diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h index 455c49543e..2fdb566159 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h +++ b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h @@ -21,6 +21,9 @@ #pragma clang diagnostic pop #endif +#include "mlir/Dialect/Utils/MatrixUtils.h" + +#include #include #include #include @@ -29,7 +32,6 @@ #include #include #include -#include #define DIALECT_NAME_QCO "qco" @@ -44,6 +46,7 @@ //===----------------------------------------------------------------------===// #define GET_TYPEDEF_CLASSES +#include "mlir/Dialect/QCO/IR/QCOInterfaces.h.inc" #include "mlir/Dialect/QCO/IR/QCOOpsTypes.h.inc" //===----------------------------------------------------------------------===// @@ -67,8 +70,6 @@ template class TargetAndParameterArityTrait { template class Impl : public OpTrait::TraitBase { public: - using UnitaryMatrixType = Eigen::Matrix, 1 << T, 1 << T>; - static size_t getNumQubits() { return T; } static size_t getNumTargets() { return T; } static size_t getNumControls() { return 0; } @@ -111,6 +112,11 @@ template class TargetAndParameterArityTrait { return this->getOperation()->getOperand(T + i); } + /** + * @brief Retrieve mlir::FloatAttr of static mlir::Value. + * @details The returned float attribute can be used to get the value of the + * given parameter as a C++ type or perform other mlir operations. + */ [[nodiscard]] static FloatAttr getStaticParameter(Value param) { auto constantOp = param.getDefiningOp(); if (!constantOp) { @@ -142,9 +148,50 @@ template class TargetAndParameterArityTrait { }; }; +/** + * @brief Retrieve C++ type of static mlir::Value. + * @details The returned float attribute can be used to get the value of the + * given parameter as a C++ type. + */ +[[nodiscard]] static std::optional staticParameterAsDouble(Value param) { + using DummyArityType = + TargetAndParameterArityTrait<0, 0>::Impl; + auto floatAttr = DummyArityType::getStaticParameter(param); + if (!floatAttr) { + return std::nullopt; + } + return floatAttr.getValueAsDouble(); +} + +/** + * @brief Trait for operations with a fixed number of target qubits which have + * a pre-defined unitary matrix definition. + * @tparam T The target arity. Should match the value given to + * TargetAndParameterArityTrait if an operation has both traits. + * @tparam MatrixDefinition Function defining the operation. The operation + * will be provided as the only argument of the + * function. + */ +template , (1 << T), (1 << T)> ( + *MatrixDefinition)(UnitaryOpInterface)> +class UnitaryMatrixTrait { +public: + template + class Impl : public OpTrait::TraitBase { + public: + using UnitaryMatrixType = + Eigen::Matrix, 1 << T, 1 << T>; + + UnitaryMatrixType getUnitaryMatrixDefinition() { + const auto& op = this->getOperation(); + return MatrixDefinition(llvm::dyn_cast(op)); + } + }; +}; + } // namespace mlir::qco -#include "mlir/Dialect/QCO/IR/QCOInterfaces.h.inc" // IWYU pragma: export +// #include "mlir/Dialect/QCO/IR/QCOInterfaces.h.inc" // IWYU pragma: export //===----------------------------------------------------------------------===// // Operations diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td index 82ed149164..f0ab077a08 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td @@ -15,7 +15,7 @@ include "mlir/IR/OpBase.td" // UnitaryOpInterface //===----------------------------------------------------------------------===// -class UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> { +def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> { let description = [{ This interface provides a unified API for all operations that apply or produce a unitary transformation in the QCO dialect. This includes base @@ -112,17 +112,22 @@ class UnitaryOpInterface : OpInterface<"UnitaryOpI InterfaceMethod< "Returns the unitary matrix definition of the operation.", "Eigen::MatrixXcd", "getUnitaryMatrix", (ins), - matrixDefinition, [{ throw std::logic_error{ "Operation has no matrix definition!" } }] - >, + [{ return $_op.getUnitaryMatrixDefinition(); }] + >, + // does not work :( + //InterfaceMethod< + // "Returns the unitary matrix definition of the operation.", + // "template MatrixType", "getUnitaryMatrix", (ins), + // [{ return $_op.getUnitaryMatrixDefinition(); }] + //>, ]; - let extraClassDeclaration = [{ - template - MatrixType getUnitaryMatrix() { - static_assert(std::is_same_v, - "Provided matrix type does not equal expected type for the contained operation."); - } - }]; + //let extraClassDeclaration = [{ + // template + // MatrixType getUnitaryMatrix() { + // return $_op.getUnitaryMatrixDefinition(); + // } + //}]; } #endif // QCO_INTERFACES diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td index 24b09a25e6..cfc4d3bdea 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td @@ -243,6 +243,11 @@ class TargetAndParameterArityTrait let cppNamespace = "::mlir::qco"; } +class UnitaryMatrixTrait + : ParamNativeOpTrait<"UnitaryMatrixTrait", !cast(T) # ", [](UnitaryOpInterface op) -> Eigen::Matrix, 1 << " # !cast(T) # ", " # "1 << " # !cast(T) # "> { " # MatrixDefinitionBody # " }"> { + let cppNamespace = "::mlir::qco"; +} + def ZeroTargetOneParameter : TargetAndParameterArityTrait<0, 1>; def OneTargetZeroParameter : TargetAndParameterArityTrait<1, 0>; def OneTargetOneParameter : TargetAndParameterArityTrait<1, 1>; @@ -252,11 +257,20 @@ def TwoTargetZeroParameter : TargetAndParameterArityTrait<2, 0>; def TwoTargetOneParameter : TargetAndParameterArityTrait<2, 1>; def TwoTargetTwoParameter : TargetAndParameterArityTrait<2, 2>; +class ZeroTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<0, 1>, UnitaryMatrixTrait<0, MatrixDefinitionBody>; +class OneTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 0>, UnitaryMatrixTrait<1, MatrixDefinitionBody>; +class OneTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 1>, UnitaryMatrixTrait<1, MatrixDefinitionBody>; +class OneTargetTwoParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 2>, UnitaryMatrixTrait<1, MatrixDefinitionBody>; +class OneTargetThreeParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 3>, UnitaryMatrixTrait<1, MatrixDefinitionBody>; +class TwoTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 0>, UnitaryMatrixTrait<2, MatrixDefinitionBody>; +class TwoTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 1>, UnitaryMatrixTrait<2, MatrixDefinitionBody>; +class TwoTargetTwoParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 2>, UnitaryMatrixTrait<2, MatrixDefinitionBody>; + //===----------------------------------------------------------------------===// // Unitary Operations //===----------------------------------------------------------------------===// -def GPhaseOp : QCOOp<"gphase", traits = [UnitaryOpInterface<>, ZeroTargetOneParameter, MemoryEffects<[MemWrite]>]> { +def GPhaseOp : QCOOp<"gphase", traits = [UnitaryOpInterface, ZeroTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixGPhase(staticParameterAsDouble(op.getParameter(0)).value()); }]>, MemoryEffects<[MemWrite]>]> { let summary = "Apply a global phase to the state"; let description = [{ Applies a global phase to the state. @@ -281,7 +295,7 @@ def GPhaseOp : QCOOp<"gphase", traits = [UnitaryOpInterface<>, ZeroTargetOnePara let hasCanonicalizer = 1; } -def IdOp : QCOOp<"id", traits = [UnitaryOpInterface<>, OneTargetZeroParameter]> { +def IdOp : QCOOp<"id", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply an Id gate to a qubit"; let description = [{ Applies an Id gate to a qubit and returns the transformed qubit. @@ -303,7 +317,7 @@ def IdOp : QCOOp<"id", traits = [UnitaryOpInterface<>, OneTargetZeroParameter]> let hasCanonicalizer = 1; } -def XOp : QCOOp<"x", traits = [UnitaryOpInterface<[{ return getMatrixX(); }]>, OneTargetZeroParameter]> { +def XOp : QCOOp<"x", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply an X gate to a qubit"; let description = [{ Applies an X gate to a qubit and returns the transformed qubit. @@ -325,7 +339,7 @@ def XOp : QCOOp<"x", traits = [UnitaryOpInterface<[{ return getMatrixX(); }]>, O let hasCanonicalizer = 1; } -def YOp : QCOOp<"y", traits = [UnitaryOpInterface<[{ return getMatrixY(); }]>, OneTargetZeroParameter]> { +def YOp : QCOOp<"y", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply a Y gate to a qubit"; let description = [{ Applies a Y gate to a qubit and returns the transformed qubit. @@ -347,7 +361,7 @@ def YOp : QCOOp<"y", traits = [UnitaryOpInterface<[{ return getMatrixY(); }]>, O let hasCanonicalizer = 1; } -def ZOp : QCOOp<"z", traits = [UnitaryOpInterface<[{ return getMatrixZ(); }]>, OneTargetZeroParameter]> { +def ZOp : QCOOp<"z", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply a Z gate to a qubit"; let description = [{ Applies a Z gate to a qubit and returns the transformed qubit. @@ -369,7 +383,7 @@ def ZOp : QCOOp<"z", traits = [UnitaryOpInterface<[{ return getMatrixZ(); }]>, O let hasCanonicalizer = 1; } -def HOp : QCOOp<"h", traits = [UnitaryOpInterface<[{ return getMatrixH(); }]>, OneTargetZeroParameter]> { +def HOp : QCOOp<"h", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply a H gate to a qubit"; let description = [{ Applies a H gate to a qubit and returns the transformed qubit. @@ -391,7 +405,7 @@ def HOp : QCOOp<"h", traits = [UnitaryOpInterface<[{ return getMatrixH(); }]>, O let hasCanonicalizer = 1; } -def SOp : QCOOp<"s", traits = [UnitaryOpInterface<[{ return getMatrixS(); }]>, OneTargetZeroParameter]> { +def SOp : QCOOp<"s", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply an S gate to a qubit"; let description = [{ Applies an S gate to a qubit and returns the transformed qubit. @@ -413,7 +427,7 @@ def SOp : QCOOp<"s", traits = [UnitaryOpInterface<[{ return getMatrixS(); }]>, O let hasCanonicalizer = 1; } -def SdgOp : QCOOp<"sdg", traits = [UnitaryOpInterface<>, OneTargetZeroParameter]> { +def SdgOp : QCOOp<"sdg", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply an Sdg gate to a qubit"; let description = [{ Applies an Sdg gate to a qubit and returns the transformed qubit. @@ -435,7 +449,7 @@ def SdgOp : QCOOp<"sdg", traits = [UnitaryOpInterface<>, OneTargetZeroParameter] let hasCanonicalizer = 1; } -def TOp : QCOOp<"t", traits = [UnitaryOpInterface<>, OneTargetZeroParameter]> { +def TOp : QCOOp<"t", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply a T gate to a qubit"; let description = [{ Applies a T gate to a qubit and returns the transformed qubit. @@ -457,7 +471,7 @@ def TOp : QCOOp<"t", traits = [UnitaryOpInterface<>, OneTargetZeroParameter]> { let hasCanonicalizer = 1; } -def TdgOp : QCOOp<"tdg", traits = [UnitaryOpInterface<>, OneTargetZeroParameter]> { +def TdgOp : QCOOp<"tdg", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply a Tdg gate to a qubit"; let description = [{ Applies a Tdg gate to a qubit and returns the transformed qubit. @@ -479,7 +493,7 @@ def TdgOp : QCOOp<"tdg", traits = [UnitaryOpInterface<>, OneTargetZeroParameter] let hasCanonicalizer = 1; } -def SXOp : QCOOp<"sx", traits = [UnitaryOpInterface<>, OneTargetZeroParameter]> { +def SXOp : QCOOp<"sx", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply an SX gate to a qubit"; let description = [{ Applies an SX gate to a qubit and returns the transformed qubit. @@ -501,7 +515,7 @@ def SXOp : QCOOp<"sx", traits = [UnitaryOpInterface<>, OneTargetZeroParameter]> let hasCanonicalizer = 1; } -def SXdgOp : QCOOp<"sxdg", traits = [UnitaryOpInterface<>, OneTargetZeroParameter]> { +def SXdgOp : QCOOp<"sxdg", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply an SXdg gate to a qubit"; let description = [{ Applies an SXdg gate to a qubit and returns the transformed qubit. @@ -523,7 +537,7 @@ def SXdgOp : QCOOp<"sxdg", traits = [UnitaryOpInterface<>, OneTargetZeroParamete let hasCanonicalizer = 1; } -def RXOp : QCOOp<"rx", traits = [UnitaryOpInterface<[{ return getMatrixRX($_op.getParameter(0)); }]>, OneTargetOneParameter]> { +def RXOp : QCOOp<"rx", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let summary = "Apply an RX gate to a qubit"; let description = [{ Applies an RX gate to a qubit and returns the transformed qubit. @@ -550,7 +564,7 @@ def RXOp : QCOOp<"rx", traits = [UnitaryOpInterface<[{ return getMatrixRX($_op.g let hasCanonicalizer = 1; } -def RYOp : QCOOp<"ry", traits = [UnitaryOpInterface<>, OneTargetOneParameter]> { +def RYOp : QCOOp<"ry", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let summary = "Apply an RY gate to a qubit"; let description = [{ Applies an RY gate to a qubit and returns the transformed qubit. @@ -577,7 +591,7 @@ def RYOp : QCOOp<"ry", traits = [UnitaryOpInterface<>, OneTargetOneParameter]> { let hasCanonicalizer = 1; } -def RZOp : QCOOp<"rz", traits = [UnitaryOpInterface<>, OneTargetOneParameter]> { +def RZOp : QCOOp<"rz", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let summary = "Apply an RZ gate to a qubit"; let description = [{ Applies an RZ gate to a qubit and returns the transformed qubit. @@ -604,7 +618,7 @@ def RZOp : QCOOp<"rz", traits = [UnitaryOpInterface<>, OneTargetOneParameter]> { let hasCanonicalizer = 1; } -def POp : QCOOp<"p", traits = [UnitaryOpInterface<>, OneTargetOneParameter]> { +def POp : QCOOp<"p", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let summary = "Apply a P gate to a qubit"; let description = [{ Applies a P gate to a qubit and returns the transformed qubit. @@ -631,7 +645,7 @@ def POp : QCOOp<"p", traits = [UnitaryOpInterface<>, OneTargetOneParameter]> { let hasCanonicalizer = 1; } -def ROp : QCOOp<"r", traits = [UnitaryOpInterface<>, OneTargetTwoParameter]> { +def ROp : QCOOp<"r", traits = [UnitaryOpInterface, OneTargetTwoParameter]> { let summary = "Apply an R gate to a qubit"; let description = [{ Applies an R gate to a qubit and returns the transformed qubit. @@ -659,7 +673,7 @@ def ROp : QCOOp<"r", traits = [UnitaryOpInterface<>, OneTargetTwoParameter]> { let hasCanonicalizer = 1; } -def U2Op : QCOOp<"u2", traits = [UnitaryOpInterface<>, OneTargetTwoParameter]> { +def U2Op : QCOOp<"u2", traits = [UnitaryOpInterface, OneTargetTwoParameter]> { let summary = "Apply a U2 gate to a qubit"; let description = [{ Applies a U2 gate to a qubit and returns the transformed qubit. @@ -687,7 +701,7 @@ def U2Op : QCOOp<"u2", traits = [UnitaryOpInterface<>, OneTargetTwoParameter]> { let hasCanonicalizer = 1; } -def UOp : QCOOp<"u", traits = [UnitaryOpInterface<>, OneTargetThreeParameter]> { +def UOp : QCOOp<"u", traits = [UnitaryOpInterface, OneTargetThreeParameter]> { let summary = "Apply a U gate to a qubit"; let description = [{ Applies a U gate to a qubit and returns the transformed qubit. @@ -716,7 +730,7 @@ def UOp : QCOOp<"u", traits = [UnitaryOpInterface<>, OneTargetThreeParameter]> { let hasCanonicalizer = 1; } -def SWAPOp : QCOOp<"swap", traits = [UnitaryOpInterface<>, TwoTargetZeroParameter]> { +def SWAPOp : QCOOp<"swap", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> { let summary = "Apply a SWAP gate to two qubits"; let description = [{ Applies a SWAP gate to two qubits and returns the transformed qubits. @@ -739,7 +753,7 @@ def SWAPOp : QCOOp<"swap", traits = [UnitaryOpInterface<>, TwoTargetZeroParamete let hasCanonicalizer = 1; } -def iSWAPOp : QCOOp<"iswap", traits = [UnitaryOpInterface<>, TwoTargetZeroParameter]> { +def iSWAPOp : QCOOp<"iswap", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> { let summary = "Apply a iSWAP gate to two qubits"; let description = [{ Applies a iSWAP gate to two qubits and returns the transformed qubits. @@ -760,7 +774,7 @@ def iSWAPOp : QCOOp<"iswap", traits = [UnitaryOpInterface<>, TwoTargetZeroParame }]; } -def DCXOp : QCOOp<"dcx", traits = [UnitaryOpInterface<>, TwoTargetZeroParameter]> { +def DCXOp : QCOOp<"dcx", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> { let summary = "Apply a DCX gate to two qubits"; let description = [{ Applies a DCX gate to two qubits and returns the transformed qubits. @@ -781,7 +795,7 @@ def DCXOp : QCOOp<"dcx", traits = [UnitaryOpInterface<>, TwoTargetZeroParameter] }]; } -def ECROp : QCOOp<"ecr", traits = [UnitaryOpInterface<>, TwoTargetZeroParameter]> { +def ECROp : QCOOp<"ecr", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> { let summary = "Apply an ECR gate to two qubits"; let description = [{ Applies an ECR gate to two qubits and returns the transformed qubits. @@ -804,7 +818,7 @@ def ECROp : QCOOp<"ecr", traits = [UnitaryOpInterface<>, TwoTargetZeroParameter] let hasCanonicalizer = 1; } -def RXXOp : QCOOp<"rxx", traits = [UnitaryOpInterface<>, TwoTargetOneParameter]> { +def RXXOp : QCOOp<"rxx", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let summary = "Apply an RXX gate to two qubits"; let description = [{ Applies an RXX gate to two qubits and returns the transformed qubits. @@ -832,7 +846,7 @@ def RXXOp : QCOOp<"rxx", traits = [UnitaryOpInterface<>, TwoTargetOneParameter]> let hasCanonicalizer = 1; } -def RYYOp : QCOOp<"ryy", traits = [UnitaryOpInterface<>, TwoTargetOneParameter]> { +def RYYOp : QCOOp<"ryy", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let summary = "Apply an RYY gate to two qubits"; let description = [{ Applies an RYY gate to two qubits and returns the transformed qubits. @@ -860,7 +874,7 @@ def RYYOp : QCOOp<"ryy", traits = [UnitaryOpInterface<>, TwoTargetOneParameter]> let hasCanonicalizer = 1; } -def RZXOp : QCOOp<"rzx", traits = [UnitaryOpInterface<>, TwoTargetOneParameter]> { +def RZXOp : QCOOp<"rzx", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let summary = "Apply an RZX gate to two qubits"; let description = [{ Applies an RZX gate to two qubits and returns the transformed qubits. @@ -888,7 +902,7 @@ def RZXOp : QCOOp<"rzx", traits = [UnitaryOpInterface<>, TwoTargetOneParameter]> let hasCanonicalizer = 1; } -def RZZOp : QCOOp<"rzz", traits = [UnitaryOpInterface<>, TwoTargetOneParameter]> { +def RZZOp : QCOOp<"rzz", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let summary = "Apply an RZZ gate to two qubits"; let description = [{ Applies an RZZ gate to two qubits and returns the transformed qubits. @@ -916,7 +930,7 @@ def RZZOp : QCOOp<"rzz", traits = [UnitaryOpInterface<>, TwoTargetOneParameter]> let hasCanonicalizer = 1; } -def XXPlusYYOp : QCOOp<"xx_plus_yy", traits = [UnitaryOpInterface<>, TwoTargetTwoParameter]> { +def XXPlusYYOp : QCOOp<"xx_plus_yy", traits = [UnitaryOpInterface, TwoTargetTwoParameter]> { let summary = "Apply an XX+YY gate to two qubits"; let description = [{ Applies an XX+YY gate to two qubits and returns the transformed qubits. @@ -945,7 +959,7 @@ def XXPlusYYOp : QCOOp<"xx_plus_yy", traits = [UnitaryOpInterface<>, TwoTargetTw let hasCanonicalizer = 1; } -def XXMinusYYOp : QCOOp<"xx_minus_yy", traits = [UnitaryOpInterface<>, TwoTargetTwoParameter]> { +def XXMinusYYOp : QCOOp<"xx_minus_yy", traits = [UnitaryOpInterface, TwoTargetTwoParameter]> { let summary = "Apply an XX-YY gate to two qubits"; let description = [{ Applies an XX-YY gate to two qubits and returns the transformed qubits. @@ -974,7 +988,7 @@ def XXMinusYYOp : QCOOp<"xx_minus_yy", traits = [UnitaryOpInterface<>, TwoTarget let hasCanonicalizer = 1; } -def BarrierOp : QCOOp<"barrier", traits = [UnitaryOpInterface<>]> { +def BarrierOp : QCOOp<"barrier", traits = [UnitaryOpInterface]> { let summary = "Apply a barrier gate to a set of qubits"; let description = [{ Applies a barrier gate to a set of qubits and returns the transformed qubits. @@ -1038,7 +1052,7 @@ def YieldOp : QCOOp<"yield", traits = [Terminator]> { def CtrlOp : QCOOp<"ctrl", traits = [ - UnitaryOpInterface<>, + UnitaryOpInterface, AttrSizedOperandSegments, AttrSizedResultSegments, SameOperandsAndResultType, diff --git a/mlir/include/mlir/Dialect/Utils/MatrixUtils.h b/mlir/include/mlir/Dialect/Utils/MatrixUtils.h new file mode 100644 index 0000000000..045b6472cf --- /dev/null +++ b/mlir/include/mlir/Dialect/Utils/MatrixUtils.h @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace mlir::utils { + +inline Eigen::Matrix, 1, 1> getMatrixGPhase(double theta) { + using namespace std::complex_literals; + return Eigen::Matrix, 1, 1>{std::exp(1i * theta)}; +} + +inline Eigen::Matrix2cd getMatrixId() { + using namespace std::complex_literals; + return Eigen::Matrix2cd{{1.0 + 0i, 0.0 + 0i}, // row 0 + {0.0 + 0i, 1.0 + 0i}}; // row 1 +} + +inline Eigen::Matrix2cd getMatrixX() { + using namespace std::complex_literals; + return Eigen::Matrix2cd{{0.0 + 0i, 1.0 + 0i}, // row 0 + {1.0 + 0i, 0.0 + 0i}}; // row 1 +} + +inline Eigen::Matrix2cd getMatrixY() { + using namespace std::complex_literals; + return Eigen::Matrix2cd{{0.0 + 0i, 0.0 - 1i}, // row 0 + {0.0 + 1i, 0.0 + 0i}}; // row 1 +} + +inline Eigen::Matrix2cd getMatrixZ() { + using namespace std::complex_literals; + return Eigen::Matrix2cd{{1.0 + 0i, 0.0 + 0i}, // row 0 + {0.0 + 0i, -1.0 + 0i}}; // row 1 +} + +inline Eigen::Matrix2cd getMatrixH() { + using namespace std::complex_literals; + const std::complex m00 = 1.0 / std::sqrt(2) + 0i; + const std::complex m11 = -1.0 / std::sqrt(2) + 0i; + return Eigen::Matrix2cd{{m00, m00}, {m00, m11}}; +} + +inline Eigen::Matrix2cd getMatrixS() { + using namespace std::complex_literals; + return Eigen::Matrix2cd{{1.0 + 0i, 0.0 + 0i}, // row 0 + {0.0 + 0i, 0.0 + 1i}}; // row 1 +} + +inline Eigen::Matrix2cd getMatrixSdg() { + using namespace std::complex_literals; + return Eigen::Matrix2cd{{1.0 + 0i, 0.0 + 0i}, // row 0 + {0.0 + 0i, 0.0 - 1i}}; // row 1 +} + +inline Eigen::Matrix2cd getMatrixT() { + using namespace std::complex_literals; + const std::complex m00 = 1.0 + 0i; + const std::complex m01 = 0.0 + 0i; + const std::complex m11 = std::exp(1i * std::numbers::pi / 4.0); + return Eigen::Matrix2cd{{m00, m01}, {m01, m11}}; +} + +inline Eigen::Matrix2cd getMatrixTdg() { + using namespace std::complex_literals; + const std::complex m00 = 1.0 + 0i; + const std::complex m01 = 0.0 + 0i; + const std::complex m11 = std::exp(-1i * std::numbers::pi / 4.0); + return Eigen::Matrix2cd{{m00, m01}, {m01, m11}}; +} + +inline Eigen::Matrix2cd getMatrixSX() { + using namespace std::complex_literals; + const std::complex m00 = (1.0 + 1i) / 2.0; + const std::complex m01 = (1.0 - 1i) / 2.0; + return Eigen::Matrix2cd{{m00, m01}, {m01, m00}}; +} + +inline Eigen::Matrix2cd getMatrixSXdg() { + using namespace std::complex_literals; + const std::complex m00 = (1.0 - 1i) / 2.0; + const std::complex m01 = (1.0 + 1i) / 2.0; + return Eigen::Matrix2cd{{m00, m01}, {m01, m00}}; +} + +inline Eigen::Matrix2cd getMatrixRX(double theta) { + using namespace std::complex_literals; + const std::complex m00 = std::cos(theta / 2.0) + 0i; + const std::complex m01 = -1i * std::sin(theta / 2.0); + return Eigen::Matrix2cd{{m00, m01}, {m01, m00}}; +} + +inline Eigen::Matrix2cd getMatrixRY(double theta) { + using namespace std::complex_literals; + const std::complex m00 = std::cos(theta / 2.0) + 0i; + const std::complex m01 = -std::sin(theta / 2.0) + 0i; + return Eigen::Matrix2cd{{m00, m01}, {-m01, m00}}; +} + +inline Eigen::Matrix2cd getMatrixRZ(double theta) { + using namespace std::complex_literals; + const std::complex m00 = std::exp(-1i * theta / 2.0); + const std::complex m01 = 0.0 + 0i; + const std::complex m11 = std::exp(1i * theta / 2.0); + return Eigen::Matrix2cd{{m00, m01}, {m01, m11}}; +} + +inline Eigen::Matrix2cd getMatrixP(double theta) { + using namespace std::complex_literals; + const std::complex m00 = 1.0 + 0i; + const std::complex m01 = 0.0 + 0i; + const std::complex m11 = std::exp(1i * theta); + return Eigen::Matrix2cd{{m00, m01}, {m01, m11}}; +} + +inline Eigen::Matrix2cd getMatrixU2(double phi, double lambda) { + using namespace std::complex_literals; + const std::complex m00 = 1.0 / std::sqrt(2) + 0i; + const std::complex m01 = -std::exp(1i * lambda) / std::sqrt(2); + const std::complex m10 = std::exp(1i * phi) / std::sqrt(2); + const std::complex m11 = std::exp(1i * (phi + lambda)) / std::sqrt(2); + return Eigen::Matrix2cd{{m00, m01}, {m10, m11}}; +} + +inline Eigen::Matrix2cd getMatrixU(double theta, double phi, double lambda) { + using namespace std::complex_literals; + const std::complex m00 = std::cos(theta / 2.0) + 0i; + const std::complex m01 = + -std::exp(1i * lambda) * std::sin(theta / 2.0); + const std::complex m10 = std::exp(1i * phi) * std::sin(theta / 2.0); + const std::complex m11 = + std::exp(1i * (phi + lambda)) * std::cos(theta / 2.0); + return Eigen::Matrix2cd{{m00, m01}, {m10, m11}}; +} + +inline Eigen::Matrix2cd getMatrixR(double theta, double phi) { + using namespace std::complex_literals; + const std::complex m00 = std::cos(theta / 2.0) + 0i; + const std::complex m01 = + -1i * std::exp(-1i * phi) * std::sin(theta / 2.0); + const std::complex m10 = + -1i * std::exp(1i * phi) * std::sin(theta / 2.0); + const std::complex m11 = std::cos(theta / 2.0) + 0i; + return Eigen::Matrix2cd{{m00, m01}, {m10, m11}}; +} + +inline Eigen::Matrix4cd getMatrixSWAP() { + using namespace std::complex_literals; + return Eigen::Matrix4cd{{1.0 + 0i, 0.0 + 0i, 0.0 + 0i, 0.0 + 0i}, // row 0 + {0.0 + 0i, 0.0 + 0i, 1.0 + 0i, 0.0 + 0i}, // row 1 + {0.0 + 0i, 1.0 + 0i, 0.0 + 0i, 0.0 + 0i}, // row 2 + {0.0 + 0i, 0.0 + 0i, 0.0 + 0i, 1.0 + 0i}}; // row 3 +} + +inline Eigen::Matrix4cd getMatrixiSWAP() { + using namespace std::complex_literals; + return Eigen::Matrix4cd{{1.0 + 0i, 0.0 + 0i, 0.0 + 0i, 0.0 + 0i}, // row 0 + {0.0 + 0i, 0.0 + 0i, 0.0 + 1i, 0.0 + 0i}, // row 1 + {0.0 + 0i, 0.0 + 1i, 0.0 + 0i, 0.0 + 0i}, // row 2 + {0.0 + 0i, 0.0 + 0i, 0.0 + 0i, 1.0 + 0i}}; // row 3 +} + +inline Eigen::Matrix4cd getMatrixDCX() { + using namespace std::complex_literals; + return Eigen::Matrix4cd{{1.0 + 0i, 0.0 + 0i, 0.0 + 0i, 0.0 + 0i}, // row 0 + {0.0 + 0i, 0.0 + 0i, 1.0 + 0i, 0.0 + 0i}, // row 1 + {0.0 + 0i, 0.0 + 0i, 0.0 + 0i, 1.0 + 0i}, // row 2 + {0.0 + 0i, 1.0 + 0i, 0.0 + 0i, 0.0 + 0i}}; // row 3 +} + +inline Eigen::Matrix4cd getMatrixECR() { + using namespace std::complex_literals; + const std::complex m0 = 0.0 + 0i; + const std::complex m1 = 1.0 / std::sqrt(2) + 0i; + const std::complex mi = 0.0 + 1i / std::sqrt(2); + return Eigen::Matrix4cd{{m0, m0, m1, mi}, // row 0 + {m0, m0, mi, m1}, // row 1 + {m1, -mi, m0, m0}, // row 2 + {-mi, m1, m0, m0}}; // row 3 +} + +inline Eigen::Matrix4cd getMatrixRXX(double theta) { + using namespace std::complex_literals; + const std::complex m0 = 0.0 + 0i; + const std::complex mc = std::cos(theta / 2.0) + 0i; + const std::complex ms = -1i * std::sin(theta / 2.0); + return Eigen::Matrix4cd{{mc, m0, m0, ms}, // row 0 + {m0, mc, ms, m0}, // row 1 + {m0, ms, mc, m0}, // row 2 + {ms, m0, m0, mc}}; // row 3 +} + +inline Eigen::Matrix4cd getMatrixRYY(double theta) { + using namespace std::complex_literals; + const std::complex m0 = 0.0 + 0i; + const std::complex mc = std::cos(theta / 2.0) + 0i; + const std::complex ms = 1i * std::sin(theta / 2.0); + return Eigen::Matrix4cd{{mc, m0, m0, ms}, // row 0 + {m0, mc, -ms, m0}, // row 1 + {m0, -ms, mc, m0}, // row 2 + {ms, m0, m0, mc}}; // row 3 +} + +inline Eigen::Matrix4cd getMatrixRZX(double theta) { + using namespace std::complex_literals; + const std::complex m0 = 0.0 + 0i; + const std::complex mc = std::cos(theta / 2.0) + 0i; + const std::complex ms = -1i * std::sin(theta / 2.0); + return Eigen::Matrix4cd{{mc, -ms, m0, m0}, // row 0 + {-ms, mc, m0, m0}, // row 1 + {m0, m0, mc, ms}, // row 2 + {m0, m0, ms, mc}}; // row 3 +} + +inline Eigen::Matrix4cd getMatrixRZZ(double theta) { + using namespace std::complex_literals; + const std::complex m0 = 0.0 + 0i; + const std::complex mp = std::exp(1i * theta / 2.0); + const std::complex mm = std::exp(-1i * theta / 2.0); + return Eigen::Matrix4cd{{mm, m0, m0, m0}, // row 0 + {m0, mp, m0, m0}, // row 1 + {m0, m0, mp, m0}, // row 2 + {m0, m0, m0, mm}}; // row 3 +} + +inline Eigen::Matrix4cd getMatrixXXPlusYY(double theta, double beta) { + using namespace std::complex_literals; + const std::complex m0 = 0.0 + 0i; + const std::complex m1 = 1.0 + 0i; + const std::complex mc = std::cos(theta / 2.0) + 0i; + const std::complex msp = + -1i * std::sin(theta / 2.0) * std::exp(1i * beta); + const std::complex msm = + -1i * std::sin(theta / 2.0) * std::exp(-1i * beta); + return Eigen::Matrix4cd{{m1, m0, m0, m0}, // row 0 + {m0, mc, msm, m0}, // row 1 + {m0, msp, mc, m0}, // row 2 + {m0, m0, m0, m1}}; // row 3 +} + +inline Eigen::Matrix4cd getMatrixXXMinusYY(double theta, double beta) { + using namespace std::complex_literals; + const std::complex m0 = 0.0 + 0i; + const std::complex m1 = 1.0 + 0i; + const std::complex mc = std::cos(theta / 2.0) + 0i; + const std::complex msp = + std::sin(theta / 2.0) * std::exp(-1i * beta) + 0i; + const std::complex msm = + -std::sin(theta / 2.0) * std::exp(1i * beta) + 0i; + return Eigen::Matrix4cd{{mc, m0, m0, msm}, // row 0 + {m0, m1, m0, m0}, // row 1 + {m0, m0, m1, m0}, // row 2 + {msp, m0, m0, mc}}; // row 3 +} + +// TODO? change target type to template +inline Eigen::MatrixXcd getMatrixCtrl(size_t numControls, + mlir::DenseElementsAttr target) { + // Get dimensions of target matrix + const auto& targetType = llvm::dyn_cast(target.getType()); + if (!targetType || targetType.getRank() != 2 || + targetType.getDimSize(0) != targetType.getDimSize(1)) { + llvm::report_fatal_error("Invalid target matrix"); + } + const auto targetDim = targetType.getDimSize(0); + + // Get values of target matrix + const auto& targetMatrix = target.getValues>(); + + // Define dimensions and type of output matrix + const auto dim = static_cast(std::pow(2, numControls) * targetDim); + + // Allocate output matrix + Eigen::MatrixXcd matrix = Eigen::MatrixXcd::Identity(dim, dim); + + // TODO: apply permutation such that target qubits are last + + // Fill output matrix + for (int64_t i = 0; i < targetDim; ++i) { + for (int64_t j = 0; j < targetDim; ++j) { + matrix(dim - targetDim - j, dim - targetDim - i) = + targetMatrix[i * targetDim + j]; + } + } + + // TODO: undo permutation + return matrix; +} + +} // namespace mlir::utils From 5b325a5704e9ebfed96240f30e348f3d37fad194 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 4 Jan 2026 22:51:57 +0100 Subject: [PATCH 148/237] compiling approach --- mlir/include/mlir/Dialect/QCO/IR/QCODialect.h | 80 ++++++++++--------- .../mlir/Dialect/QCO/IR/QCOInterfaces.td | 8 +- mlir/include/mlir/Dialect/QCO/IR/QCOOps.td | 50 ++++++------ 3 files changed, 73 insertions(+), 65 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h index 2fdb566159..feaa2e5594 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h +++ b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h @@ -55,6 +55,13 @@ namespace mlir::qco { +/** + * @brief Retrieve C++ type of static mlir::Value. + * @details The returned float attribute can be used to get the value of the + * given parameter as a C++ type. + */ +[[nodiscard]] inline std::optional +tryGetParameterAsDouble(UnitaryOpInterface op, size_t i); /** * @brief Trait for operations with a fixed number of target qubits and * parameters @@ -64,8 +71,16 @@ namespace mlir::qco { * verification and code generation optimizations. * @tparam T The target arity. * @tparam P The parameter arity. + * @tparam HasMatrixDefinition If false, operation does not provide a matrix + * definition. + * @tparam MatrixDefinition A function returning the matrix definition of the + * operation. The operation will be provided as the + * only argument of the function. */ -template class TargetAndParameterArityTrait { +template , (1 << T), (1 << T)> ( + *MatrixDefinition)(UnitaryOpInterface)> +class TargetAndParameterArityTrait { public: template class Impl : public OpTrait::TraitBase { @@ -125,6 +140,17 @@ template class TargetAndParameterArityTrait { return dyn_cast(constantOp.getValue()); } + using UnitaryMatrixType = + Eigen::Matrix, 1 << T, 1 << T>; + + static constexpr auto HAS_MATRIX_DEFINITION = HasMatrixDefinition; + [[nodiscard]] UnitaryMatrixType getUnitaryMatrixDefinition() + requires(HAS_MATRIX_DEFINITION) + { + const auto& op = this->getOperation(); + return MatrixDefinition(llvm::dyn_cast(op)); + } + Value getInputForOutput(Value output) { const auto& op = this->getOperation(); for (size_t i = 0; i < T; ++i) { @@ -148,51 +174,29 @@ template class TargetAndParameterArityTrait { }; }; -/** - * @brief Retrieve C++ type of static mlir::Value. - * @details The returned float attribute can be used to get the value of the - * given parameter as a C++ type. - */ -[[nodiscard]] static std::optional staticParameterAsDouble(Value param) { - using DummyArityType = - TargetAndParameterArityTrait<0, 0>::Impl; - auto floatAttr = DummyArityType::getStaticParameter(param); +} // namespace mlir::qco + +// #include "mlir/Dialect/QCO/IR/QCOInterfaces.h.inc" // IWYU pragma: export + +//===----------------------------------------------------------------------===// +// Operations Helpers +//===----------------------------------------------------------------------===// + +namespace mlir::qco { + +[[nodiscard]] inline std::optional +tryGetParameterAsDouble(UnitaryOpInterface op, size_t i) { + using DummyArityType = TargetAndParameterArityTrait<0, 0, false, nullptr>; + const auto param = op.getParameter(i); + const auto floatAttr = DummyArityType::Impl::getStaticParameter(param); if (!floatAttr) { return std::nullopt; } return floatAttr.getValueAsDouble(); } -/** - * @brief Trait for operations with a fixed number of target qubits which have - * a pre-defined unitary matrix definition. - * @tparam T The target arity. Should match the value given to - * TargetAndParameterArityTrait if an operation has both traits. - * @tparam MatrixDefinition Function defining the operation. The operation - * will be provided as the only argument of the - * function. - */ -template , (1 << T), (1 << T)> ( - *MatrixDefinition)(UnitaryOpInterface)> -class UnitaryMatrixTrait { -public: - template - class Impl : public OpTrait::TraitBase { - public: - using UnitaryMatrixType = - Eigen::Matrix, 1 << T, 1 << T>; - - UnitaryMatrixType getUnitaryMatrixDefinition() { - const auto& op = this->getOperation(); - return MatrixDefinition(llvm::dyn_cast(op)); - } - }; -}; - } // namespace mlir::qco -// #include "mlir/Dialect/QCO/IR/QCOInterfaces.h.inc" // IWYU pragma: export - //===----------------------------------------------------------------------===// // Operations //===----------------------------------------------------------------------===// diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td index f0ab077a08..fb4d1bab26 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td @@ -112,7 +112,13 @@ def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> { InterfaceMethod< "Returns the unitary matrix definition of the operation.", "Eigen::MatrixXcd", "getUnitaryMatrix", (ins), - [{ return $_op.getUnitaryMatrixDefinition(); }] + [{ + if constexpr (decltype($_op)::HAS_MATRIX_DEFINITION) { + return $_op.getUnitaryMatrixDefinition(); + } else { + llvm::reportFatalUsageError("Operation has no unitary matrix definition!"); + } + }] >, // does not work :( //InterfaceMethod< diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td index cfc4d3bdea..43d5b46644 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td @@ -238,39 +238,36 @@ def ResetOp : QCOOp<"reset", [Idempotent, SameOperandsAndResultType]> { // Traits //===----------------------------------------------------------------------===// -class TargetAndParameterArityTrait - : ParamNativeOpTrait<"TargetAndParameterArityTrait", !strconcat(!cast(T), ",", !cast(P))> { +class TargetAndParameterArityTrait + : ParamNativeOpTrait<"TargetAndParameterArityTrait", !cast(T) # "," # !cast(P) # "," # !cast(HasMatrixDefinition) # ", [](UnitaryOpInterface op) -> Eigen::Matrix, 1 << " # !cast(T) # ", " # "1 << " # !cast(T) # "> { " # MatrixDefinitionBody # " }"> { let cppNamespace = "::mlir::qco"; } -class UnitaryMatrixTrait - : ParamNativeOpTrait<"UnitaryMatrixTrait", !cast(T) # ", [](UnitaryOpInterface op) -> Eigen::Matrix, 1 << " # !cast(T) # ", " # "1 << " # !cast(T) # "> { " # MatrixDefinitionBody # " }"> { - let cppNamespace = "::mlir::qco"; -} - -def ZeroTargetOneParameter : TargetAndParameterArityTrait<0, 1>; -def OneTargetZeroParameter : TargetAndParameterArityTrait<1, 0>; -def OneTargetOneParameter : TargetAndParameterArityTrait<1, 1>; -def OneTargetTwoParameter : TargetAndParameterArityTrait<1, 2>; -def OneTargetThreeParameter : TargetAndParameterArityTrait<1, 3>; -def TwoTargetZeroParameter : TargetAndParameterArityTrait<2, 0>; -def TwoTargetOneParameter : TargetAndParameterArityTrait<2, 1>; -def TwoTargetTwoParameter : TargetAndParameterArityTrait<2, 2>; - -class ZeroTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<0, 1>, UnitaryMatrixTrait<0, MatrixDefinitionBody>; -class OneTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 0>, UnitaryMatrixTrait<1, MatrixDefinitionBody>; -class OneTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 1>, UnitaryMatrixTrait<1, MatrixDefinitionBody>; -class OneTargetTwoParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 2>, UnitaryMatrixTrait<1, MatrixDefinitionBody>; -class OneTargetThreeParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 3>, UnitaryMatrixTrait<1, MatrixDefinitionBody>; -class TwoTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 0>, UnitaryMatrixTrait<2, MatrixDefinitionBody>; -class TwoTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 1>, UnitaryMatrixTrait<2, MatrixDefinitionBody>; -class TwoTargetTwoParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 2>, UnitaryMatrixTrait<2, MatrixDefinitionBody>; +def ZeroTargetZeroParameter : TargetAndParameterArityTrait<0, 0, false, [{ return Eigen::Matrix, 1, 1>::Zero(); }]>; +def ZeroTargetOneParameter : TargetAndParameterArityTrait<0, 1, false, [{ return Eigen::Matrix, 1, 1>::Zero(); }]>; +def OneTargetZeroParameter : TargetAndParameterArityTrait<1, 0, false, [{ return Eigen::Matrix2cd::Zero(); }]>; +def OneTargetOneParameter : TargetAndParameterArityTrait<1, 1, false, [{ return Eigen::Matrix2cd::Zero(); }]>; +def OneTargetTwoParameter : TargetAndParameterArityTrait<1, 2, false, [{ return Eigen::Matrix2cd::Zero(); }]>; +def OneTargetThreeParameter : TargetAndParameterArityTrait<1, 3, false, [{ return Eigen::Matrix2cd::Zero(); }]>; +def TwoTargetZeroParameter : TargetAndParameterArityTrait<2, 0, false, [{ return Eigen::Matrix4cd::Zero(); }]>; +def TwoTargetOneParameter : TargetAndParameterArityTrait<2, 1, false, [{ return Eigen::Matrix4cd::Zero(); }]>; +def TwoTargetTwoParameter : TargetAndParameterArityTrait<2, 2, false, [{ return Eigen::Matrix4cd::Zero(); }]>; + +class ZeroTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<0, 0, true, MatrixDefinitionBody>; +class ZeroTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<0, 1, true, MatrixDefinitionBody>; +class OneTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 0, true, MatrixDefinitionBody>; +class OneTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 1, true, MatrixDefinitionBody>; +class OneTargetTwoParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 2, true, MatrixDefinitionBody>; +class OneTargetThreeParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 3, true, MatrixDefinitionBody>; +class TwoTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 0, true, MatrixDefinitionBody>; +class TwoTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 1, true, MatrixDefinitionBody>; +class TwoTargetTwoParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 2, true, MatrixDefinitionBody>; //===----------------------------------------------------------------------===// // Unitary Operations //===----------------------------------------------------------------------===// -def GPhaseOp : QCOOp<"gphase", traits = [UnitaryOpInterface, ZeroTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixGPhase(staticParameterAsDouble(op.getParameter(0)).value()); }]>, MemoryEffects<[MemWrite]>]> { +def GPhaseOp : QCOOp<"gphase", traits = [UnitaryOpInterface, ZeroTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixGPhase(tryGetParameterAsDouble(op, 0).value()); }]>, MemoryEffects<[MemWrite]>]> { let summary = "Apply a global phase to the state"; let description = [{ Applies a global phase to the state. @@ -988,7 +985,7 @@ def XXMinusYYOp : QCOOp<"xx_minus_yy", traits = [UnitaryOpInterface, TwoTargetTw let hasCanonicalizer = 1; } -def BarrierOp : QCOOp<"barrier", traits = [UnitaryOpInterface]> { +def BarrierOp : QCOOp<"barrier", traits = [UnitaryOpInterface, ZeroTargetZeroParameter]> { let summary = "Apply a barrier gate to a set of qubits"; let description = [{ Applies a barrier gate to a set of qubits and returns the transformed qubits. @@ -1053,6 +1050,7 @@ def YieldOp : QCOOp<"yield", traits = [Terminator]> { def CtrlOp : QCOOp<"ctrl", traits = [ UnitaryOpInterface, + ZeroTargetZeroParameterUnitaryMatrix<[{ /* TODO: define operation helper? */ }]>, AttrSizedOperandSegments, AttrSizedResultSegments, SameOperandsAndResultType, From b3161cc3a3d45f91c5fce2a1cf2cd3a256385501 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 5 Jan 2026 15:04:23 +0100 Subject: [PATCH 149/237] formatting --- mlir/include/mlir/Dialect/QCO/IR/QCODialect.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h index feaa2e5594..dc94d8d5e0 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h +++ b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h @@ -61,7 +61,7 @@ namespace mlir::qco { * given parameter as a C++ type. */ [[nodiscard]] inline std::optional -tryGetParameterAsDouble(UnitaryOpInterface op, size_t i); +tryGetParameterAsDouble(UnitaryOpInterface op, size_t i); /** * @brief Trait for operations with a fixed number of target qubits and * parameters @@ -188,7 +188,8 @@ namespace mlir::qco { tryGetParameterAsDouble(UnitaryOpInterface op, size_t i) { using DummyArityType = TargetAndParameterArityTrait<0, 0, false, nullptr>; const auto param = op.getParameter(i); - const auto floatAttr = DummyArityType::Impl::getStaticParameter(param); + const auto floatAttr = + DummyArityType::Impl::getStaticParameter(param); if (!floatAttr) { return std::nullopt; } From 01886fb8b55ec1cc1ce77c39027a83959b93eb2a Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 5 Jan 2026 15:05:23 +0100 Subject: [PATCH 150/237] avoid use of implementation-dependent argument names --- mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td index fb4d1bab26..6d8bd7a2aa 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td @@ -89,17 +89,17 @@ def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> { InterfaceMethod< "Returns true if the operation has any control qubits, otherwise false.", "bool", "isControlled", (ins), - [{ return getNumControls(impl, tablegen_opaque_val) > 0; }] + [{ return $_op.getNumControls() > 0; }] >, InterfaceMethod< "Returns true if the operation only acts on a single qubit.", "bool", "isSingleQubit", (ins), - [{ return getNumQubits(impl, tablegen_opaque_val) == 1; }] + [{ return $_op.getNumQubits() == 1; }] >, InterfaceMethod< "Returns true if the operation acts on two qubits.", "bool", "isTwoQubit", (ins), - [{ return getNumQubits(impl, tablegen_opaque_val) == 2; }] + [{ return $_op.getNumQubits() == 2; }] >, // Identification From 30198a97820962d2ea83d4d2249c0f51804aefa0 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 5 Jan 2026 15:05:53 +0100 Subject: [PATCH 151/237] finally got template function working --- .../mlir/Dialect/QCO/IR/QCOInterfaces.td | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td index 6d8bd7a2aa..b2cdc35c24 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td @@ -120,20 +120,14 @@ def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> { } }] >, - // does not work :( - //InterfaceMethod< - // "Returns the unitary matrix definition of the operation.", - // "template MatrixType", "getUnitaryMatrix", (ins), - // [{ return $_op.getUnitaryMatrixDefinition(); }] - //>, ]; - //let extraClassDeclaration = [{ - // template - // MatrixType getUnitaryMatrix() { - // return $_op.getUnitaryMatrixDefinition(); - // } - //}]; + let extraTraitClassDeclaration = [{ + template + MatrixType getUnitaryMatrix() { + return $_op.getUnitaryMatrixDefinition(); + } + }]; } #endif // QCO_INTERFACES From 1c7dd1d8b43427985a8eac6ebf5539c0316d753f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 5 Jan 2026 17:31:48 +0100 Subject: [PATCH 152/237] add to compiler pipeline and add (failing) test case --- mlir/lib/Compiler/CompilerPipeline.cpp | 2 +- .../pipeline/test_compiler_pipeline.cpp | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index 2375ce8ca1..53a8b38d7e 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -166,7 +166,7 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, pm.clear(); // Stage 5: Optimization passes - // TODO: Add optimization passes + addOptimizationPasses(pm); addCleanupPasses(pm); if (failed(pm.run(module))) { return failure(); diff --git a/mlir/unittests/pipeline/test_compiler_pipeline.cpp b/mlir/unittests/pipeline/test_compiler_pipeline.cpp index 2b0f1854f2..1d8b2ec720 100644 --- a/mlir/unittests/pipeline/test_compiler_pipeline.cpp +++ b/mlir/unittests/pipeline/test_compiler_pipeline.cpp @@ -3725,4 +3725,57 @@ TEST_F(CompilerPipelineTest, Bell) { }); } +TEST_F(CompilerPipelineTest, TwoQubitDecomposition) { + ::qc::QuantumComputation comp; + comp.addQubitRegister(2, "q"); + + auto buildCircuit = [](auto& circuit, auto&& qubitGetter) { + auto constant0 = 2.5; + auto constant1 = 1.2; + auto constant2 = 0.5; + circuit.h(qubitGetter(0)); + circuit.cx(qubitGetter(0), qubitGetter(1)); + circuit.rzz(constant0, qubitGetter(0), qubitGetter(1)); + circuit.ry(constant1, qubitGetter(1)); + circuit.ry(constant1, qubitGetter(0)); + circuit.cx(qubitGetter(1), qubitGetter(0)); + circuit.rz(constant2, qubitGetter(0)); + circuit.rxx(constant0, qubitGetter(0), qubitGetter(1)); + circuit.ryy(constant2, qubitGetter(0), qubitGetter(1)); + // make series longer to enforce decomposition + circuit.rxx(0.1, qubitGetter(1), qubitGetter(0)); + circuit.rzz(0.1, qubitGetter(1), qubitGetter(0)); + circuit.rxx(0.1, qubitGetter(1), qubitGetter(0)); + circuit.rzz(0.1, qubitGetter(1), qubitGetter(0)); + circuit.rxx(0.1, qubitGetter(1), qubitGetter(0)); + circuit.rzz(0.1, qubitGetter(1), qubitGetter(0)); + circuit.rxx(0.1, qubitGetter(1), qubitGetter(0)); + circuit.rzz(0.1, qubitGetter(1), qubitGetter(0)); + }; + + buildCircuit(comp, [](auto&& index) { return index; }); + + const auto module = importQuantumCircuit(comp); + ASSERT_TRUE(module); + ASSERT_TRUE(runPipeline(module.get()).succeeded()); + + const auto qco = buildQCOIR([&](qco::QCOProgramBuilder& b) { + auto reg = b.allocQubitRegister(2, "q"); + buildCircuit(b, [®](auto&& index) { return reg[index]; }); + }); + + const auto optimizedQco = buildQCOIR([&](qco::QCOProgramBuilder& b) { + auto reg = b.allocQubitRegister(2, "q"); + buildCircuit(b, [®](auto&& index) { return reg[index]; }); + }); + + verifyAllStages({ + .qcImport = nullptr, + .qcoConversion = qco.get(), + .optimization = optimizedQco.get(), + .qcConversion = nullptr, + .qirConversion = nullptr, + }); +} + } // namespace From a8fbec24345f50c7c0de8e6a0f85fdd5818a15ae Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 6 Jan 2026 19:26:22 +0100 Subject: [PATCH 153/237] make unitary function const, remove HasMatrixDefinition bool template parameter --- mlir/include/mlir/Dialect/QCO/IR/QCODialect.h | 25 +++++--- .../mlir/Dialect/QCO/IR/QCOInterfaces.td | 5 +- mlir/include/mlir/Dialect/QCO/IR/QCOOps.td | 58 +++++++++---------- 3 files changed, 48 insertions(+), 40 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h index dc94d8d5e0..163c3d1c7a 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h +++ b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h @@ -71,13 +71,13 @@ tryGetParameterAsDouble(UnitaryOpInterface op, size_t i); * verification and code generation optimizations. * @tparam T The target arity. * @tparam P The parameter arity. - * @tparam HasMatrixDefinition If false, operation does not provide a matrix - * definition. * @tparam MatrixDefinition A function returning the matrix definition of the * operation. The operation will be provided as the - * only argument of the function. + * only argument of the function. If the operation does + * not have a matrix definition, set this value to + * nullptr. */ -template , (1 << T), (1 << T)> ( *MatrixDefinition)(UnitaryOpInterface)> class TargetAndParameterArityTrait { @@ -143,11 +143,10 @@ class TargetAndParameterArityTrait { using UnitaryMatrixType = Eigen::Matrix, 1 << T, 1 << T>; - static constexpr auto HAS_MATRIX_DEFINITION = HasMatrixDefinition; - [[nodiscard]] UnitaryMatrixType getUnitaryMatrixDefinition() - requires(HAS_MATRIX_DEFINITION) + [[nodiscard]] UnitaryMatrixType getUnitaryMatrixDefinition() const + requires(MatrixDefinition != nullptr) { - const auto& op = this->getOperation(); + const auto* op = this->getConstOperation(); return MatrixDefinition(llvm::dyn_cast(op)); } @@ -171,6 +170,14 @@ class TargetAndParameterArityTrait { llvm::reportFatalUsageError( "Given qubit is not an input of the operation"); } + + protected: + [[nodiscard]] const Operation* getConstOperation() const { + auto* concrete = static_cast(this); + // use dereference operator instead of getOperation() of mlir::Op; the + // operator provides a const overload, getOperation() does not + return *concrete; + } }; }; @@ -186,7 +193,7 @@ namespace mlir::qco { [[nodiscard]] inline std::optional tryGetParameterAsDouble(UnitaryOpInterface op, size_t i) { - using DummyArityType = TargetAndParameterArityTrait<0, 0, false, nullptr>; + using DummyArityType = TargetAndParameterArityTrait<0, 0, nullptr>; const auto param = op.getParameter(i); const auto floatAttr = DummyArityType::Impl::getStaticParameter(param); diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td index b2cdc35c24..9dd27f38ad 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td @@ -28,6 +28,7 @@ def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> { let cppNamespace = "::mlir::qco"; + // TODO: fix const correctness? let methods = [ // Qubit accessors InterfaceMethod< @@ -113,7 +114,7 @@ def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> { "Returns the unitary matrix definition of the operation.", "Eigen::MatrixXcd", "getUnitaryMatrix", (ins), [{ - if constexpr (decltype($_op)::HAS_MATRIX_DEFINITION) { + if constexpr (requires { $_op.getUnitaryMatrixDefinition(); }) { return $_op.getUnitaryMatrixDefinition(); } else { llvm::reportFatalUsageError("Operation has no unitary matrix definition!"); @@ -124,7 +125,7 @@ def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> { let extraTraitClassDeclaration = [{ template - MatrixType getUnitaryMatrix() { + MatrixType getFastUnitaryMatrix() const { return $_op.getUnitaryMatrixDefinition(); } }]; diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td index 43d5b46644..b316cb63f1 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td @@ -238,30 +238,30 @@ def ResetOp : QCOOp<"reset", [Idempotent, SameOperandsAndResultType]> { // Traits //===----------------------------------------------------------------------===// -class TargetAndParameterArityTrait - : ParamNativeOpTrait<"TargetAndParameterArityTrait", !cast(T) # "," # !cast(P) # "," # !cast(HasMatrixDefinition) # ", [](UnitaryOpInterface op) -> Eigen::Matrix, 1 << " # !cast(T) # ", " # "1 << " # !cast(T) # "> { " # MatrixDefinitionBody # " }"> { +class TargetAndParameterArityTrait + : ParamNativeOpTrait<"TargetAndParameterArityTrait", !cast(T) # ", " # !cast(P) # ", " # !cond(!empty(MatrixDefinitionBody): "nullptr", true: "[](UnitaryOpInterface op) -> Eigen::Matrix, 1 << " # !cast(T) # ", " # "1 << " # !cast(T) # "> { " # MatrixDefinitionBody # " }")> { let cppNamespace = "::mlir::qco"; } -def ZeroTargetZeroParameter : TargetAndParameterArityTrait<0, 0, false, [{ return Eigen::Matrix, 1, 1>::Zero(); }]>; -def ZeroTargetOneParameter : TargetAndParameterArityTrait<0, 1, false, [{ return Eigen::Matrix, 1, 1>::Zero(); }]>; -def OneTargetZeroParameter : TargetAndParameterArityTrait<1, 0, false, [{ return Eigen::Matrix2cd::Zero(); }]>; -def OneTargetOneParameter : TargetAndParameterArityTrait<1, 1, false, [{ return Eigen::Matrix2cd::Zero(); }]>; -def OneTargetTwoParameter : TargetAndParameterArityTrait<1, 2, false, [{ return Eigen::Matrix2cd::Zero(); }]>; -def OneTargetThreeParameter : TargetAndParameterArityTrait<1, 3, false, [{ return Eigen::Matrix2cd::Zero(); }]>; -def TwoTargetZeroParameter : TargetAndParameterArityTrait<2, 0, false, [{ return Eigen::Matrix4cd::Zero(); }]>; -def TwoTargetOneParameter : TargetAndParameterArityTrait<2, 1, false, [{ return Eigen::Matrix4cd::Zero(); }]>; -def TwoTargetTwoParameter : TargetAndParameterArityTrait<2, 2, false, [{ return Eigen::Matrix4cd::Zero(); }]>; - -class ZeroTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<0, 0, true, MatrixDefinitionBody>; -class ZeroTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<0, 1, true, MatrixDefinitionBody>; -class OneTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 0, true, MatrixDefinitionBody>; -class OneTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 1, true, MatrixDefinitionBody>; -class OneTargetTwoParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 2, true, MatrixDefinitionBody>; -class OneTargetThreeParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 3, true, MatrixDefinitionBody>; -class TwoTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 0, true, MatrixDefinitionBody>; -class TwoTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 1, true, MatrixDefinitionBody>; -class TwoTargetTwoParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 2, true, MatrixDefinitionBody>; +def ZeroTargetZeroParameter : TargetAndParameterArityTrait<0, 0>; +def ZeroTargetOneParameter : TargetAndParameterArityTrait<0, 1>; +def OneTargetZeroParameter : TargetAndParameterArityTrait<1, 0>; +def OneTargetOneParameter : TargetAndParameterArityTrait<1, 1>; +def OneTargetTwoParameter : TargetAndParameterArityTrait<1, 2>; +def OneTargetThreeParameter : TargetAndParameterArityTrait<1, 3>; +def TwoTargetZeroParameter : TargetAndParameterArityTrait<2, 0>; +def TwoTargetOneParameter : TargetAndParameterArityTrait<2, 1>; +def TwoTargetTwoParameter : TargetAndParameterArityTrait<2, 2>; + +class ZeroTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<0, 0, MatrixDefinitionBody>; +class ZeroTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<0, 1, MatrixDefinitionBody>; +class OneTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 0, MatrixDefinitionBody>; +class OneTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 1, MatrixDefinitionBody>; +class OneTargetTwoParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 2, MatrixDefinitionBody>; +class OneTargetThreeParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 3, MatrixDefinitionBody>; +class TwoTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 0, MatrixDefinitionBody>; +class TwoTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 1, MatrixDefinitionBody>; +class TwoTargetTwoParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 2, MatrixDefinitionBody>; //===----------------------------------------------------------------------===// // Unitary Operations @@ -314,7 +314,7 @@ def IdOp : QCOOp<"id", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let hasCanonicalizer = 1; } -def XOp : QCOOp<"x", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def XOp : QCOOp<"x", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixX(); }]>]> { let summary = "Apply an X gate to a qubit"; let description = [{ Applies an X gate to a qubit and returns the transformed qubit. @@ -380,7 +380,7 @@ def ZOp : QCOOp<"z", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let hasCanonicalizer = 1; } -def HOp : QCOOp<"h", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def HOp : QCOOp<"h", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixH(); }]>]> { let summary = "Apply a H gate to a qubit"; let description = [{ Applies a H gate to a qubit and returns the transformed qubit. @@ -534,7 +534,7 @@ def SXdgOp : QCOOp<"sxdg", traits = [UnitaryOpInterface, OneTargetZeroParameter] let hasCanonicalizer = 1; } -def RXOp : QCOOp<"rx", traits = [UnitaryOpInterface, OneTargetOneParameter]> { +def RXOp : QCOOp<"rx", traits = [UnitaryOpInterface, OneTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRX(tryGetParameterAsDouble(op, 0).value()); }]>]> { let summary = "Apply an RX gate to a qubit"; let description = [{ Applies an RX gate to a qubit and returns the transformed qubit. @@ -561,7 +561,7 @@ def RXOp : QCOOp<"rx", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let hasCanonicalizer = 1; } -def RYOp : QCOOp<"ry", traits = [UnitaryOpInterface, OneTargetOneParameter]> { +def RYOp : QCOOp<"ry", traits = [UnitaryOpInterface, OneTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRY(tryGetParameterAsDouble(op, 0).value()); }]>]> { let summary = "Apply an RY gate to a qubit"; let description = [{ Applies an RY gate to a qubit and returns the transformed qubit. @@ -588,7 +588,7 @@ def RYOp : QCOOp<"ry", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let hasCanonicalizer = 1; } -def RZOp : QCOOp<"rz", traits = [UnitaryOpInterface, OneTargetOneParameter]> { +def RZOp : QCOOp<"rz", traits = [UnitaryOpInterface, OneTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRZ(tryGetParameterAsDouble(op, 0).value()); }]>]> { let summary = "Apply an RZ gate to a qubit"; let description = [{ Applies an RZ gate to a qubit and returns the transformed qubit. @@ -815,7 +815,7 @@ def ECROp : QCOOp<"ecr", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> let hasCanonicalizer = 1; } -def RXXOp : QCOOp<"rxx", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { +def RXXOp : QCOOp<"rxx", traits = [UnitaryOpInterface, TwoTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRXX(tryGetParameterAsDouble(op, 0).value(); }]>]> { let summary = "Apply an RXX gate to two qubits"; let description = [{ Applies an RXX gate to two qubits and returns the transformed qubits. @@ -843,7 +843,7 @@ def RXXOp : QCOOp<"rxx", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let hasCanonicalizer = 1; } -def RYYOp : QCOOp<"ryy", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { +def RYYOp : QCOOp<"ryy", traits = [UnitaryOpInterface, TwoTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRYY(tryGetParameterAsDouble(op, 0).value(); }]>]> { let summary = "Apply an RYY gate to two qubits"; let description = [{ Applies an RYY gate to two qubits and returns the transformed qubits. @@ -899,7 +899,7 @@ def RZXOp : QCOOp<"rzx", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let hasCanonicalizer = 1; } -def RZZOp : QCOOp<"rzz", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { +def RZZOp : QCOOp<"rzz", traits = [UnitaryOpInterface, TwoTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRZZ(tryGetParameterAsDouble(op, 0).value(); }]>]> { let summary = "Apply an RZZ gate to two qubits"; let description = [{ Applies an RZZ gate to two qubits and returns the transformed qubits. From f9f7f04e8c0d290795a376931f9b92d8ff28bf0e Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 6 Jan 2026 19:27:02 +0100 Subject: [PATCH 154/237] start working on ctrl matrix --- mlir/include/mlir/Dialect/QCO/IR/QCODialect.h | 25 +++++++++++++++++++ mlir/include/mlir/Dialect/QCO/IR/QCOOps.td | 2 +- mlir/include/mlir/Dialect/Utils/MatrixUtils.h | 19 ++++++-------- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h index 163c3d1c7a..eb3409ebb6 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h +++ b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #define DIALECT_NAME_QCO "qco" @@ -203,6 +204,30 @@ tryGetParameterAsDouble(UnitaryOpInterface op, size_t i) { return floatAttr.getValueAsDouble(); } +[[nodiscard]] inline Eigen::MatrixXcd getBlockMatrix(size_t dim, mlir::Region& region) { + assert(dim == 1); // TODO: remove once permutations are properly handled + + Eigen::MatrixXcd result = Eigen::MatrixXcd::Identity(1 << dim, 1 << dim); + for (auto&& block : region) { + for (auto&& op : block) { + auto unitaryOp = llvm::dyn_cast(op); + if (unitaryOp) { + return result; + } + auto matrix = unitaryOp.getUnitaryMatrix(); + size_t matrixDim = matrix.cols(); + if (matrixDim < dim) { + // TODO: adjust according to permutation (position in targets?) + auto paddingDim = dim - matrixDim; + auto padding = Eigen::MatrixXcd::Identity(1 << paddingDim, 1 << paddingDim); + matrix = Eigen::kroneckerProduct(matrix, padding); + } + result = matrix * result; + } + } + return result; +} + } // namespace mlir::qco //===----------------------------------------------------------------------===// diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td index b316cb63f1..c94f0e2366 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td @@ -1050,7 +1050,7 @@ def YieldOp : QCOOp<"yield", traits = [Terminator]> { def CtrlOp : QCOOp<"ctrl", traits = [ UnitaryOpInterface, - ZeroTargetZeroParameterUnitaryMatrix<[{ /* TODO: define operation helper? */ }]>, + ZeroTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixCtrl(); }]>, AttrSizedOperandSegments, AttrSizedResultSegments, SameOperandsAndResultType, diff --git a/mlir/include/mlir/Dialect/Utils/MatrixUtils.h b/mlir/include/mlir/Dialect/Utils/MatrixUtils.h index 045b6472cf..20ff7eed5f 100644 --- a/mlir/include/mlir/Dialect/Utils/MatrixUtils.h +++ b/mlir/include/mlir/Dialect/Utils/MatrixUtils.h @@ -10,6 +10,8 @@ #pragma once +#include "mlir/Dialect/QCO/IR/QCODialect.h" +#include #include #include #include @@ -268,22 +270,14 @@ inline Eigen::Matrix4cd getMatrixXXMinusYY(double theta, double beta) { {msp, m0, m0, mc}}; // row 3 } -// TODO? change target type to template inline Eigen::MatrixXcd getMatrixCtrl(size_t numControls, - mlir::DenseElementsAttr target) { + Eigen::MatrixXcd targetMatrix) { // Get dimensions of target matrix - const auto& targetType = llvm::dyn_cast(target.getType()); - if (!targetType || targetType.getRank() != 2 || - targetType.getDimSize(0) != targetType.getDimSize(1)) { - llvm::report_fatal_error("Invalid target matrix"); - } - const auto targetDim = targetType.getDimSize(0); - - // Get values of target matrix - const auto& targetMatrix = target.getValues>(); + const auto targetDim = targetMatrix.cols(); + assert(targetMatrix.cols() == targetMatrix.rows()); // Define dimensions and type of output matrix - const auto dim = static_cast(std::pow(2, numControls) * targetDim); + const auto dim = static_cast((1 << numControls) * targetDim); // Allocate output matrix Eigen::MatrixXcd matrix = Eigen::MatrixXcd::Identity(dim, dim); @@ -299,6 +293,7 @@ inline Eigen::MatrixXcd getMatrixCtrl(size_t numControls, } // TODO: undo permutation + return matrix; } From d8d7b1908192c634369286558530704740e3cf60 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 6 Jan 2026 19:27:31 +0100 Subject: [PATCH 155/237] use new unitary matrix functions in GateDecompositionPattern --- .../Patterns/GateDecompositionPattern.cpp | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 6372b6bcc5..e02795933f 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -172,9 +172,18 @@ struct GateDecompositionPattern final auto getUser = [](mlir::Value qubit, auto&& filter) -> std::optional { if (qubit) { - assert(qubit.hasOneUse()); - auto user = - mlir::dyn_cast(*qubit.getUsers().begin()); + auto userIt = qubit.getUsers().begin(); + // qubit may have more than one use if it is in a ctrl block (one use + // for gate, one use for ctrl); we want to use the ctrl operation + // since it is relevant for the total unitary matrix of the circuit + assert(qubit.hasOneUse() || qubit.hasNUses(2)); + if (!qubit.hasOneUse()) { + // TODO: use wire iterator for proper handling + while (!mlir::dyn_cast(*userIt)) { + ++userIt; + } + } + auto user = mlir::dyn_cast(*userIt); if (user && filter(user)) { return user; } @@ -204,13 +213,11 @@ struct GateDecompositionPattern final return result; } - [[nodiscard]] matrix2x2 getSingleQubitUnitaryMatrix() const { + [[nodiscard]] matrix2x2 getSingleQubitUnitaryMatrix() { auto unitaryMatrix = decomposition::IDENTITY_GATE; for (auto&& gate : gates) { - auto gateMatrix = decomposition::getSingleQubitMatrix( - {.type = helpers::getQcType(gate.op), - .parameter = helpers::getParameters(gate.op), - .qubitId = gate.qubitIds}); + // auto gateMatrix = gate.op.getFastUnitaryMatrix(); + auto gateMatrix = gate.op.getUnitaryMatrix(); unitaryMatrix = gateMatrix * unitaryMatrix; } @@ -218,14 +225,23 @@ struct GateDecompositionPattern final return unitaryMatrix; } - [[nodiscard]] matrix4x4 getUnitaryMatrix() const { + [[nodiscard]] matrix4x4 getUnitaryMatrix() { matrix4x4 unitaryMatrix = helpers::kroneckerProduct( decomposition::IDENTITY_GATE, decomposition::IDENTITY_GATE); for (auto&& gate : gates) { - auto gateMatrix = decomposition::getTwoQubitMatrix( - {.type = helpers::getQcType(gate.op), - .parameter = helpers::getParameters(gate.op), - .qubitId = gate.qubitIds}); + auto gateMatrix = gate.op.getUnitaryMatrix(); + if (gate.op.isSingleQubit()) { + assert(gate.qubitIds.size() == 1); + // TODO: use helpers::kroneckerProduct or Eigen::kroneckerProduct? + if (gate.qubitIds[0] == 0) { + gateMatrix = Eigen::kroneckerProduct(decomposition::IDENTITY_GATE, + gateMatrix); + } else if (gate.qubitIds[0] == 1) { + gateMatrix = Eigen::kroneckerProduct(gateMatrix, + decomposition::IDENTITY_GATE); + } + } + // auto gateMatrix = gate.op.getFastUnitaryMatrix(); unitaryMatrix = gateMatrix * unitaryMatrix; } From ce79ed299631f7330f8bf655a2364ac83e174bae Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 6 Jan 2026 19:27:58 +0100 Subject: [PATCH 156/237] add debug prints --- mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index e02795933f..17caf6cecd 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -112,9 +112,20 @@ struct GateDecompositionPattern final } } } + + llvm::errs() << "Found series (" << series.complexity << "): "; + for (auto&& gate : series.gates) { + llvm::errs() << gate.op->getName().stripDialect().str() << ", "; + } + if (!bestSequence) { return mlir::failure(); } + llvm::errs() << "\nDecomposition (" << bestSequence->complexity() << "): "; + for (auto&& gate : bestSequence->gates) { + llvm::errs() << qc::toString(gate.type) << ", "; + } + llvm::errs() << "\n"; // only accept new sequence if it shortens existing series by more than two // gates; this prevents an oscillation with phase gates if (bestSequence->complexity() + 2 >= series.complexity) { From 6c965ed401f9c109dc17468a54e05d45f842f86a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 18:30:37 +0000 Subject: [PATCH 157/237] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/include/mlir/Dialect/QCO/IR/QCODialect.h | 6 ++++-- mlir/include/mlir/Dialect/Utils/MatrixUtils.h | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h index eb3409ebb6..2333d5ec20 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h +++ b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h @@ -204,7 +204,8 @@ tryGetParameterAsDouble(UnitaryOpInterface op, size_t i) { return floatAttr.getValueAsDouble(); } -[[nodiscard]] inline Eigen::MatrixXcd getBlockMatrix(size_t dim, mlir::Region& region) { +[[nodiscard]] inline Eigen::MatrixXcd getBlockMatrix(size_t dim, + mlir::Region& region) { assert(dim == 1); // TODO: remove once permutations are properly handled Eigen::MatrixXcd result = Eigen::MatrixXcd::Identity(1 << dim, 1 << dim); @@ -219,7 +220,8 @@ tryGetParameterAsDouble(UnitaryOpInterface op, size_t i) { if (matrixDim < dim) { // TODO: adjust according to permutation (position in targets?) auto paddingDim = dim - matrixDim; - auto padding = Eigen::MatrixXcd::Identity(1 << paddingDim, 1 << paddingDim); + auto padding = + Eigen::MatrixXcd::Identity(1 << paddingDim, 1 << paddingDim); matrix = Eigen::kroneckerProduct(matrix, padding); } result = matrix * result; diff --git a/mlir/include/mlir/Dialect/Utils/MatrixUtils.h b/mlir/include/mlir/Dialect/Utils/MatrixUtils.h index 20ff7eed5f..ba097b3aa4 100644 --- a/mlir/include/mlir/Dialect/Utils/MatrixUtils.h +++ b/mlir/include/mlir/Dialect/Utils/MatrixUtils.h @@ -11,13 +11,14 @@ #pragma once #include "mlir/Dialect/QCO/IR/QCODialect.h" -#include + #include #include #include #include #include #include +#include #include namespace mlir::utils { From 15883885d3530161dbd41c37b25bc74aa29fe5fa Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 7 Jan 2026 13:12:15 +0100 Subject: [PATCH 158/237] add helpers for CtrlOp matrix --- mlir/include/mlir/Dialect/QCO/IR/QCODialect.h | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h index 2333d5ec20..f4b6e32af7 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h +++ b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -204,8 +205,28 @@ tryGetParameterAsDouble(UnitaryOpInterface op, size_t i) { return floatAttr.getValueAsDouble(); } +[[nodiscard]] inline std::pair +permutate(const Eigen::MatrixXcd& inputMatrix, + const Eigen::VectorXi& permutation) { + const auto swapMatrix = utils::getMatrixSWAP(); + + auto dim = inputMatrix.cols(); + assert(inputMatrix.cols() == inputMatrix.rows()); + assert(dim == permutation.size()); + + Eigen::MatrixXcd permutatedMatrix(dim, dim); + Eigen::VectorXi undoPermutation(permutation.size()); + for (int i = 0; i < permutation.size(); ++i) { + undoPermutation(permutation(i)) = i; + // TODO + } + + return {permutatedMatrix, undoPermutation}; +} + [[nodiscard]] inline Eigen::MatrixXcd getBlockMatrix(size_t dim, mlir::Region& region) { + // TODO: check if dim == region.getArguments().size() assert(dim == 1); // TODO: remove once permutations are properly handled Eigen::MatrixXcd result = Eigen::MatrixXcd::Identity(1 << dim, 1 << dim); @@ -218,11 +239,15 @@ tryGetParameterAsDouble(UnitaryOpInterface op, size_t i) { auto matrix = unitaryOp.getUnitaryMatrix(); size_t matrixDim = matrix.cols(); if (matrixDim < dim) { - // TODO: adjust according to permutation (position in targets?) + // TODO: permutate such that operation qubits are next to each other; + // then perform front/back padding accordingly + auto paddingDim = dim - matrixDim; auto padding = Eigen::MatrixXcd::Identity(1 << paddingDim, 1 << paddingDim); matrix = Eigen::kroneckerProduct(matrix, padding); + + // TODO: undo permutation } result = matrix * result; } @@ -230,6 +255,8 @@ tryGetParameterAsDouble(UnitaryOpInterface op, size_t i) { return result; } +mlir::Region& getCtrlBody(UnitaryOpInterface op); + } // namespace mlir::qco //===----------------------------------------------------------------------===// @@ -238,3 +265,11 @@ tryGetParameterAsDouble(UnitaryOpInterface op, size_t i) { #define GET_OP_CLASSES #include "mlir/Dialect/QCO/IR/QCOOps.h.inc" // IWYU pragma: export + +namespace mlir::qco { + +[[nodiscard]] inline mlir::Region& getCtrlBody(UnitaryOpInterface op) { + return llvm::cast(op).getBody(); +} + +} // namespace mlir::qco From 00491d7fa641713cd47920ea1a19f7eb21d76fed Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 7 Jan 2026 13:13:02 +0100 Subject: [PATCH 159/237] add matrix definition to all operations --- mlir/include/mlir/Dialect/QCO/IR/QCOOps.td | 48 +++++++++---------- mlir/include/mlir/Dialect/Utils/MatrixUtils.h | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td index c94f0e2366..4732f41393 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td @@ -292,7 +292,7 @@ def GPhaseOp : QCOOp<"gphase", traits = [UnitaryOpInterface, ZeroTargetOneParame let hasCanonicalizer = 1; } -def IdOp : QCOOp<"id", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def IdOp : QCOOp<"id", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixId(); }]>]> { let summary = "Apply an Id gate to a qubit"; let description = [{ Applies an Id gate to a qubit and returns the transformed qubit. @@ -336,7 +336,7 @@ def XOp : QCOOp<"x", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitary let hasCanonicalizer = 1; } -def YOp : QCOOp<"y", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def YOp : QCOOp<"y", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixY(); }]>]> { let summary = "Apply a Y gate to a qubit"; let description = [{ Applies a Y gate to a qubit and returns the transformed qubit. @@ -358,7 +358,7 @@ def YOp : QCOOp<"y", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let hasCanonicalizer = 1; } -def ZOp : QCOOp<"z", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def ZOp : QCOOp<"z", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixZ(); }]>]> { let summary = "Apply a Z gate to a qubit"; let description = [{ Applies a Z gate to a qubit and returns the transformed qubit. @@ -402,7 +402,7 @@ def HOp : QCOOp<"h", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitary let hasCanonicalizer = 1; } -def SOp : QCOOp<"s", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def SOp : QCOOp<"s", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixS(); }]>]> { let summary = "Apply an S gate to a qubit"; let description = [{ Applies an S gate to a qubit and returns the transformed qubit. @@ -424,7 +424,7 @@ def SOp : QCOOp<"s", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let hasCanonicalizer = 1; } -def SdgOp : QCOOp<"sdg", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def SdgOp : QCOOp<"sdg", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixSdg(); }]>]> { let summary = "Apply an Sdg gate to a qubit"; let description = [{ Applies an Sdg gate to a qubit and returns the transformed qubit. @@ -446,7 +446,7 @@ def SdgOp : QCOOp<"sdg", traits = [UnitaryOpInterface, OneTargetZeroParameter]> let hasCanonicalizer = 1; } -def TOp : QCOOp<"t", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def TOp : QCOOp<"t", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixT(); }]>]> { let summary = "Apply a T gate to a qubit"; let description = [{ Applies a T gate to a qubit and returns the transformed qubit. @@ -468,7 +468,7 @@ def TOp : QCOOp<"t", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let hasCanonicalizer = 1; } -def TdgOp : QCOOp<"tdg", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def TdgOp : QCOOp<"tdg", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixTdg(); }]>]> { let summary = "Apply a Tdg gate to a qubit"; let description = [{ Applies a Tdg gate to a qubit and returns the transformed qubit. @@ -490,7 +490,7 @@ def TdgOp : QCOOp<"tdg", traits = [UnitaryOpInterface, OneTargetZeroParameter]> let hasCanonicalizer = 1; } -def SXOp : QCOOp<"sx", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def SXOp : QCOOp<"sx", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixSX(); }]>]> { let summary = "Apply an SX gate to a qubit"; let description = [{ Applies an SX gate to a qubit and returns the transformed qubit. @@ -512,7 +512,7 @@ def SXOp : QCOOp<"sx", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let hasCanonicalizer = 1; } -def SXdgOp : QCOOp<"sxdg", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { +def SXdgOp : QCOOp<"sxdg", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixSXdg(); }]>]> { let summary = "Apply an SXdg gate to a qubit"; let description = [{ Applies an SXdg gate to a qubit and returns the transformed qubit. @@ -615,7 +615,7 @@ def RZOp : QCOOp<"rz", traits = [UnitaryOpInterface, OneTargetOneParameterUnitar let hasCanonicalizer = 1; } -def POp : QCOOp<"p", traits = [UnitaryOpInterface, OneTargetOneParameter]> { +def POp : QCOOp<"p", traits = [UnitaryOpInterface, OneTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixP(tryGetParameterAsDouble(op, 0).value()); }]>]> { let summary = "Apply a P gate to a qubit"; let description = [{ Applies a P gate to a qubit and returns the transformed qubit. @@ -642,7 +642,7 @@ def POp : QCOOp<"p", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let hasCanonicalizer = 1; } -def ROp : QCOOp<"r", traits = [UnitaryOpInterface, OneTargetTwoParameter]> { +def ROp : QCOOp<"r", traits = [UnitaryOpInterface, OneTargetTwoParameterUnitaryMatrix<[{ return utils::getMatrixR(tryGetParameterAsDouble(op, 0).value(), tryGetParameterAsDouble(op, 1).value()); }]>]> { let summary = "Apply an R gate to a qubit"; let description = [{ Applies an R gate to a qubit and returns the transformed qubit. @@ -670,7 +670,7 @@ def ROp : QCOOp<"r", traits = [UnitaryOpInterface, OneTargetTwoParameter]> { let hasCanonicalizer = 1; } -def U2Op : QCOOp<"u2", traits = [UnitaryOpInterface, OneTargetTwoParameter]> { +def U2Op : QCOOp<"u2", traits = [UnitaryOpInterface, OneTargetTwoParameterUnitaryMatrix<[{ return utils::getMatrixU2(tryGetParameterAsDouble(op, 0).value(), tryGetParameterAsDouble(op, 1).value()); }]>]> { let summary = "Apply a U2 gate to a qubit"; let description = [{ Applies a U2 gate to a qubit and returns the transformed qubit. @@ -698,7 +698,7 @@ def U2Op : QCOOp<"u2", traits = [UnitaryOpInterface, OneTargetTwoParameter]> { let hasCanonicalizer = 1; } -def UOp : QCOOp<"u", traits = [UnitaryOpInterface, OneTargetThreeParameter]> { +def UOp : QCOOp<"u", traits = [UnitaryOpInterface, OneTargetThreeParameterUnitaryMatrix<[{ return utils::getMatrixU(tryGetParameterAsDouble(op, 0).value(), tryGetParameterAsDouble(op, 1).value(), tryGetParameterAsDouble(op, 2).value()); }]>]> { let summary = "Apply a U gate to a qubit"; let description = [{ Applies a U gate to a qubit and returns the transformed qubit. @@ -727,7 +727,7 @@ def UOp : QCOOp<"u", traits = [UnitaryOpInterface, OneTargetThreeParameter]> { let hasCanonicalizer = 1; } -def SWAPOp : QCOOp<"swap", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> { +def SWAPOp : QCOOp<"swap", traits = [UnitaryOpInterface, TwoTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixSWAP(); }]>]> { let summary = "Apply a SWAP gate to two qubits"; let description = [{ Applies a SWAP gate to two qubits and returns the transformed qubits. @@ -750,7 +750,7 @@ def SWAPOp : QCOOp<"swap", traits = [UnitaryOpInterface, TwoTargetZeroParameter] let hasCanonicalizer = 1; } -def iSWAPOp : QCOOp<"iswap", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> { +def iSWAPOp : QCOOp<"iswap", traits = [UnitaryOpInterface, TwoTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixiSWAP(); }]>]> { let summary = "Apply a iSWAP gate to two qubits"; let description = [{ Applies a iSWAP gate to two qubits and returns the transformed qubits. @@ -771,7 +771,7 @@ def iSWAPOp : QCOOp<"iswap", traits = [UnitaryOpInterface, TwoTargetZeroParamete }]; } -def DCXOp : QCOOp<"dcx", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> { +def DCXOp : QCOOp<"dcx", traits = [UnitaryOpInterface, TwoTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixDCX(); }]>]> { let summary = "Apply a DCX gate to two qubits"; let description = [{ Applies a DCX gate to two qubits and returns the transformed qubits. @@ -792,7 +792,7 @@ def DCXOp : QCOOp<"dcx", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> }]; } -def ECROp : QCOOp<"ecr", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> { +def ECROp : QCOOp<"ecr", traits = [UnitaryOpInterface, TwoTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixECR(); }]>]> { let summary = "Apply an ECR gate to two qubits"; let description = [{ Applies an ECR gate to two qubits and returns the transformed qubits. @@ -815,7 +815,7 @@ def ECROp : QCOOp<"ecr", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> let hasCanonicalizer = 1; } -def RXXOp : QCOOp<"rxx", traits = [UnitaryOpInterface, TwoTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRXX(tryGetParameterAsDouble(op, 0).value(); }]>]> { +def RXXOp : QCOOp<"rxx", traits = [UnitaryOpInterface, TwoTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRXX(tryGetParameterAsDouble(op, 0).value()); }]>]> { let summary = "Apply an RXX gate to two qubits"; let description = [{ Applies an RXX gate to two qubits and returns the transformed qubits. @@ -843,7 +843,7 @@ def RXXOp : QCOOp<"rxx", traits = [UnitaryOpInterface, TwoTargetOneParameterUnit let hasCanonicalizer = 1; } -def RYYOp : QCOOp<"ryy", traits = [UnitaryOpInterface, TwoTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRYY(tryGetParameterAsDouble(op, 0).value(); }]>]> { +def RYYOp : QCOOp<"ryy", traits = [UnitaryOpInterface, TwoTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRYY(tryGetParameterAsDouble(op, 0).value()); }]>]> { let summary = "Apply an RYY gate to two qubits"; let description = [{ Applies an RYY gate to two qubits and returns the transformed qubits. @@ -871,7 +871,7 @@ def RYYOp : QCOOp<"ryy", traits = [UnitaryOpInterface, TwoTargetOneParameterUnit let hasCanonicalizer = 1; } -def RZXOp : QCOOp<"rzx", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { +def RZXOp : QCOOp<"rzx", traits = [UnitaryOpInterface, TwoTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRZX(tryGetParameterAsDouble(op, 0).value()); }]>]> { let summary = "Apply an RZX gate to two qubits"; let description = [{ Applies an RZX gate to two qubits and returns the transformed qubits. @@ -899,7 +899,7 @@ def RZXOp : QCOOp<"rzx", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let hasCanonicalizer = 1; } -def RZZOp : QCOOp<"rzz", traits = [UnitaryOpInterface, TwoTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRZZ(tryGetParameterAsDouble(op, 0).value(); }]>]> { +def RZZOp : QCOOp<"rzz", traits = [UnitaryOpInterface, TwoTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRZZ(tryGetParameterAsDouble(op, 0).value()); }]>]> { let summary = "Apply an RZZ gate to two qubits"; let description = [{ Applies an RZZ gate to two qubits and returns the transformed qubits. @@ -927,7 +927,7 @@ def RZZOp : QCOOp<"rzz", traits = [UnitaryOpInterface, TwoTargetOneParameterUnit let hasCanonicalizer = 1; } -def XXPlusYYOp : QCOOp<"xx_plus_yy", traits = [UnitaryOpInterface, TwoTargetTwoParameter]> { +def XXPlusYYOp : QCOOp<"xx_plus_yy", traits = [UnitaryOpInterface, TwoTargetTwoParameterUnitaryMatrix<[{ return utils::getMatrixXXPlusYY(tryGetParameterAsDouble(op, 0).value(), tryGetParameterAsDouble(op, 1).value()); }]>]> { let summary = "Apply an XX+YY gate to two qubits"; let description = [{ Applies an XX+YY gate to two qubits and returns the transformed qubits. @@ -956,7 +956,7 @@ def XXPlusYYOp : QCOOp<"xx_plus_yy", traits = [UnitaryOpInterface, TwoTargetTwoP let hasCanonicalizer = 1; } -def XXMinusYYOp : QCOOp<"xx_minus_yy", traits = [UnitaryOpInterface, TwoTargetTwoParameter]> { +def XXMinusYYOp : QCOOp<"xx_minus_yy", traits = [UnitaryOpInterface, TwoTargetTwoParameterUnitaryMatrix<[{ return utils::getMatrixXXMinusYY(tryGetParameterAsDouble(op, 0).value(), tryGetParameterAsDouble(op, 1).value()); }]>]> { let summary = "Apply an XX-YY gate to two qubits"; let description = [{ Applies an XX-YY gate to two qubits and returns the transformed qubits. @@ -1050,7 +1050,7 @@ def YieldOp : QCOOp<"yield", traits = [Terminator]> { def CtrlOp : QCOOp<"ctrl", traits = [ UnitaryOpInterface, - ZeroTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixCtrl(); }]>, + ZeroTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixCtrl(op.getNumControls(), getBlockMatrix(op.getNumTargets(), getCtrlBody(op))); }]>, AttrSizedOperandSegments, AttrSizedResultSegments, SameOperandsAndResultType, diff --git a/mlir/include/mlir/Dialect/Utils/MatrixUtils.h b/mlir/include/mlir/Dialect/Utils/MatrixUtils.h index ba097b3aa4..02c4fdfa68 100644 --- a/mlir/include/mlir/Dialect/Utils/MatrixUtils.h +++ b/mlir/include/mlir/Dialect/Utils/MatrixUtils.h @@ -289,7 +289,7 @@ inline Eigen::MatrixXcd getMatrixCtrl(size_t numControls, for (int64_t i = 0; i < targetDim; ++i) { for (int64_t j = 0; j < targetDim; ++j) { matrix(dim - targetDim - j, dim - targetDim - i) = - targetMatrix[i * targetDim + j]; + targetMatrix(j, i); } } From 69ebd05a068715f009a8f4a2faf3765e929777d5 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 7 Jan 2026 18:04:21 +0100 Subject: [PATCH 160/237] fixes for unitary matrix definition --- mlir/include/mlir/Dialect/QCO/IR/QCODialect.h | 13 +++----- .../mlir/Dialect/QCO/IR/QCOInterfaces.td | 2 +- mlir/include/mlir/Dialect/QCO/IR/QCOOps.td | 31 ++++++++++++------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h index f4b6e32af7..4098090a57 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h +++ b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h @@ -24,6 +24,7 @@ #include "mlir/Dialect/Utils/MatrixUtils.h" #include +#include #include #include #include @@ -31,7 +32,6 @@ #include #include #include -#include #include #include @@ -79,9 +79,8 @@ tryGetParameterAsDouble(UnitaryOpInterface op, size_t i); * not have a matrix definition, set this value to * nullptr. */ -template , (1 << T), (1 << T)> ( - *MatrixDefinition)(UnitaryOpInterface)> +template class TargetAndParameterArityTrait { public: template @@ -142,9 +141,6 @@ class TargetAndParameterArityTrait { return dyn_cast(constantOp.getValue()); } - using UnitaryMatrixType = - Eigen::Matrix, 1 << T, 1 << T>; - [[nodiscard]] UnitaryMatrixType getUnitaryMatrixDefinition() const requires(MatrixDefinition != nullptr) { @@ -195,7 +191,8 @@ namespace mlir::qco { [[nodiscard]] inline std::optional tryGetParameterAsDouble(UnitaryOpInterface op, size_t i) { - using DummyArityType = TargetAndParameterArityTrait<0, 0, nullptr>; + using DummyArityType = + TargetAndParameterArityTrait<0, 0, Eigen::MatrixXcd, nullptr>; const auto param = op.getParameter(i); const auto floatAttr = DummyArityType::Impl::getStaticParameter(param); diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td index 9dd27f38ad..97b3776af5 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td @@ -117,7 +117,7 @@ def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> { if constexpr (requires { $_op.getUnitaryMatrixDefinition(); }) { return $_op.getUnitaryMatrixDefinition(); } else { - llvm::reportFatalUsageError("Operation has no unitary matrix definition!"); + llvm::reportFatalUsageError("Operation '" + $_op.getBaseSymbol() + "' has no unitary matrix definition!"); } }] >, diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td index 4732f41393..2086d6abaa 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td @@ -238,8 +238,14 @@ def ResetOp : QCOOp<"reset", [Idempotent, SameOperandsAndResultType]> { // Traits //===----------------------------------------------------------------------===// -class TargetAndParameterArityTrait - : ParamNativeOpTrait<"TargetAndParameterArityTrait", !cast(T) # ", " # !cast(P) # ", " # !cond(!empty(MatrixDefinitionBody): "nullptr", true: "[](UnitaryOpInterface op) -> Eigen::Matrix, 1 << " # !cast(T) # ", " # "1 << " # !cast(T) # "> { " # MatrixDefinitionBody # " }")> { +class MatrixDefinitionLambda { + code MatrixType = "Eigen::Matrix, " # MatrixSize # ", " # MatrixSize # ">"; + code Code = !cond(!empty(MatrixDefinitionBody): "nullptr", true: "[](UnitaryOpInterface op) -> " # MatrixType # " { " # MatrixDefinitionBody # " }"); +} +class FixedSizeMatrixDefinitionLambda : MatrixDefinitionLambda<"1 << " # !cast(T), MatrixDefinitionBody>; + +class TargetAndParameterArityTrait> + : ParamNativeOpTrait<"TargetAndParameterArityTrait", !cast(T) # ", " # !cast(P) # ", " # MatrixDefinition.MatrixType # ", " # MatrixDefinition.Code> { let cppNamespace = "::mlir::qco"; } @@ -253,15 +259,16 @@ def TwoTargetZeroParameter : TargetAndParameterArityTrait<2, 0>; def TwoTargetOneParameter : TargetAndParameterArityTrait<2, 1>; def TwoTargetTwoParameter : TargetAndParameterArityTrait<2, 2>; -class ZeroTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<0, 0, MatrixDefinitionBody>; -class ZeroTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<0, 1, MatrixDefinitionBody>; -class OneTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 0, MatrixDefinitionBody>; -class OneTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 1, MatrixDefinitionBody>; -class OneTargetTwoParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 2, MatrixDefinitionBody>; -class OneTargetThreeParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 3, MatrixDefinitionBody>; -class TwoTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 0, MatrixDefinitionBody>; -class TwoTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 1, MatrixDefinitionBody>; -class TwoTargetTwoParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 2, MatrixDefinitionBody>; +class DynamicTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<0, 0, MatrixDefinitionLambda<"Eigen::Dynamic", MatrixDefinitionBody>>; + +class ZeroTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<0, 1, FixedSizeMatrixDefinitionLambda<0, MatrixDefinitionBody>>; +class OneTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 0, FixedSizeMatrixDefinitionLambda<1, MatrixDefinitionBody>>; +class OneTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 1, FixedSizeMatrixDefinitionLambda<1, MatrixDefinitionBody>>; +class OneTargetTwoParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 2, FixedSizeMatrixDefinitionLambda<1, MatrixDefinitionBody>>; +class OneTargetThreeParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 3, FixedSizeMatrixDefinitionLambda<1, MatrixDefinitionBody>>; +class TwoTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 0, FixedSizeMatrixDefinitionLambda<2, MatrixDefinitionBody>>; +class TwoTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 1, FixedSizeMatrixDefinitionLambda<2, MatrixDefinitionBody>>; +class TwoTargetTwoParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 2, FixedSizeMatrixDefinitionLambda<2, MatrixDefinitionBody>>; //===----------------------------------------------------------------------===// // Unitary Operations @@ -1050,7 +1057,7 @@ def YieldOp : QCOOp<"yield", traits = [Terminator]> { def CtrlOp : QCOOp<"ctrl", traits = [ UnitaryOpInterface, - ZeroTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixCtrl(op.getNumControls(), getBlockMatrix(op.getNumTargets(), getCtrlBody(op))); }]>, + DynamicTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixCtrl(op.getNumControls(), getBlockMatrix(op.getNumTargets(), getCtrlBody(op))); }]>, AttrSizedOperandSegments, AttrSizedResultSegments, SameOperandsAndResultType, From df2223688193af9e62d803779cbcecfdb3beca49 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 7 Jan 2026 18:04:33 +0100 Subject: [PATCH 161/237] fixes for gate decomposition pattern --- .../Patterns/GateDecompositionPattern.cpp | 82 +++++++++---------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 17caf6cecd..9faab66817 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -246,10 +246,12 @@ struct GateDecompositionPattern final // TODO: use helpers::kroneckerProduct or Eigen::kroneckerProduct? if (gate.qubitIds[0] == 0) { gateMatrix = Eigen::kroneckerProduct(decomposition::IDENTITY_GATE, - gateMatrix); + gateMatrix) + .eval(); } else if (gate.qubitIds[0] == 1) { gateMatrix = Eigen::kroneckerProduct(gateMatrix, - decomposition::IDENTITY_GATE); + decomposition::IDENTITY_GATE) + .eval(); } } // auto gateMatrix = gate.op.getFastUnitaryMatrix(); @@ -402,28 +404,32 @@ struct GateDecompositionPattern final } }; - template + template static OpType createGate(mlir::PatternRewriter& rewriter, - mlir::Location location, mlir::ValueRange inQubits, - const llvm::SmallVector& parameters) { - mlir::SmallVector parameterValues; - for (auto&& parameter : parameters) { - auto parameterValue = rewriter.create( - location, rewriter.getF64Type(), rewriter.getF64FloatAttr(parameter)); - parameterValues.push_back(parameterValue); - } - - return rewriter.create(location, inQubits, parameterValues); + mlir::Location location, + Args&&... inQubitsAndParams) { + return rewriter.create(location, + std::forward(inQubitsAndParams)...); } - template - static CtrlOp - createControlledGate(mlir::PatternRewriter& rewriter, mlir::Location location, - mlir::ValueRange inQubits, mlir::ValueRange ctrlQubits, - const llvm::SmallVector& parameters) { - auto op = createGate(rewriter, location, inQubits, parameters); - auto opOutQubits = op->getResults(); - return rewriter.create(location, ctrlQubits, opOutQubits); + template + static CtrlOp createControlledGate(mlir::PatternRewriter& rewriter, + mlir::Location location, + mlir::ValueRange ctrlQubits, + Args&&... inQubitsAndParams) { + llvm::SmallVector inQubits; + auto collectInQubits = [&inQubits](auto&& x) { + if constexpr (std::is_same_v, + mlir::Value>) { + // if argument is a qubit, add it to list; otherwise, do nothing + inQubits.push_back(std::forward(x)); + } + }; + (collectInQubits(inQubitsAndParams), ...); + return rewriter.create( + location, ctrlQubits, mlir::ValueRange{inQubits}, + createGate(rewriter, location, + std::forward(inQubitsAndParams)...)); } static void applySeries(mlir::PatternRewriter& rewriter, @@ -448,7 +454,7 @@ struct GateDecompositionPattern final }; if (sequence.hasGlobalPhase()) { - createGate(rewriter, location, {}, {sequence.globalPhase}); + createGate(rewriter, location, sequence.globalPhase); } matrix4x4 unitaryMatrix = helpers::kroneckerProduct( @@ -464,33 +470,23 @@ struct GateDecompositionPattern final // controls come last inCtrlQubits.push_back(inQubits[gate.qubitId[1]]); } - auto newGate = createControlledGate(rewriter, location, - {inQubits[gate.qubitId[0]]}, - inCtrlQubits, gate.parameter); + auto newGate = createControlledGate( + rewriter, location, {inQubits[gate.qubitId[0]]}, inCtrlQubits); updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RX) { - mlir::SmallVector qubits; - for (auto&& x : gate.qubitId) { - qubits.push_back(inQubits[x]); - } - auto newGate = - createGate(rewriter, location, qubits, gate.parameter); + assert(gate.qubitId.size() == 1); + auto newGate = createGate( + rewriter, location, inQubits[gate.qubitId[0]], gate.parameter[0]); updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RY) { - mlir::SmallVector qubits; - for (auto&& x : gate.qubitId) { - qubits.push_back(inQubits[x]); - } - auto newGate = - createGate(rewriter, location, qubits, gate.parameter); + assert(gate.qubitId.size() == 1); + auto newGate = createGate( + rewriter, location, inQubits[gate.qubitId[0]], gate.parameter[0]); updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RZ) { - mlir::SmallVector qubits; - for (auto&& x : gate.qubitId) { - qubits.push_back(inQubits[x]); - } - auto newGate = - createGate(rewriter, location, qubits, gate.parameter); + assert(gate.qubitId.size() == 1); + auto newGate = createGate( + rewriter, location, inQubits[gate.qubitId[0]], gate.parameter[0]); updateInQubits(gate.qubitId, newGate); } else { throw std::runtime_error{"Unknown gate type!"}; From 31f4a027296cb7df82243aeab07b452cc7699358 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 7 Jan 2026 18:10:06 +0100 Subject: [PATCH 162/237] fix formatting in MatrixUtils.h --- mlir/include/mlir/Dialect/Utils/MatrixUtils.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mlir/include/mlir/Dialect/Utils/MatrixUtils.h b/mlir/include/mlir/Dialect/Utils/MatrixUtils.h index 02c4fdfa68..16fa93e785 100644 --- a/mlir/include/mlir/Dialect/Utils/MatrixUtils.h +++ b/mlir/include/mlir/Dialect/Utils/MatrixUtils.h @@ -288,8 +288,7 @@ inline Eigen::MatrixXcd getMatrixCtrl(size_t numControls, // Fill output matrix for (int64_t i = 0; i < targetDim; ++i) { for (int64_t j = 0; j < targetDim; ++j) { - matrix(dim - targetDim - j, dim - targetDim - i) = - targetMatrix(j, i); + matrix(dim - targetDim - j, dim - targetDim - i) = targetMatrix(j, i); } } From 9e2e0e6f85947c966d1a4bfbaf33749ab3714680 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 7 Jan 2026 19:15:29 +0100 Subject: [PATCH 163/237] (dirty) fix for CtrlOp in gate decomposition pattern; runs, but SSA error at end and wrong result --- .../lib/Passes/Patterns/GateDecompositionPattern.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 9faab66817..662991b68a 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -183,7 +183,8 @@ struct GateDecompositionPattern final auto getUser = [](mlir::Value qubit, auto&& filter) -> std::optional { if (qubit) { - auto userIt = qubit.getUsers().begin(); + auto users = qubit.getUsers(); + auto userIt = users.begin(); // qubit may have more than one use if it is in a ctrl block (one use // for gate, one use for ctrl); we want to use the ctrl operation // since it is relevant for the total unitary matrix of the circuit @@ -192,6 +193,10 @@ struct GateDecompositionPattern final // TODO: use wire iterator for proper handling while (!mlir::dyn_cast(*userIt)) { ++userIt; + if (userIt == users.end()) { + // TODO: should not happen? + return std::nullopt; + } } } auto user = mlir::dyn_cast(*userIt); @@ -471,7 +476,7 @@ struct GateDecompositionPattern final inCtrlQubits.push_back(inQubits[gate.qubitId[1]]); } auto newGate = createControlledGate( - rewriter, location, {inQubits[gate.qubitId[0]]}, inCtrlQubits); + rewriter, location, inCtrlQubits, inQubits[gate.qubitId[0]]); updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RX) { assert(gate.qubitId.size() == 1); @@ -501,6 +506,9 @@ struct GateDecompositionPattern final rewriter.replaceAllUsesWith(series.outQubits, inQubits); } for (auto&& gate : llvm::reverse(series.gates)) { + if (auto ctrlOp = llvm::dyn_cast(*gate.op)) { + rewriter.eraseBlock(&ctrlOp.getBody().front()); + } rewriter.eraseOp(gate.op); } } From 0bb5a86ac159bd92ef3878b5b30e380a7e694982 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 20 Jan 2026 01:02:51 +0100 Subject: [PATCH 164/237] :memo: Add PR of unitary matrices to CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c177b98656..46e626d28c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Added -- ✨ Add initial infrastructure for new QC and QCO MLIR dialects ([#1264], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**]) +- ✨ Add initial infrastructure for new QC and QCO MLIR dialects ([#1264], [#1402], [#1426], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**]) ### Changed @@ -329,6 +329,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool [#1436]: https://github.com/munich-quantum-toolkit/core/pull/1436 [#1430]: https://github.com/munich-quantum-toolkit/core/pull/1430 [#1428]: https://github.com/munich-quantum-toolkit/core/pull/1428 +[#1426]: https://github.com/munich-quantum-toolkit/core/pull/1426 [#1415]: https://github.com/munich-quantum-toolkit/core/pull/1415 [#1414]: https://github.com/munich-quantum-toolkit/core/pull/1414 [#1413]: https://github.com/munich-quantum-toolkit/core/pull/1413 From a0051a98d4335bc170856355a2ee36ff3a1acb19 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 21 Jan 2026 10:39:42 +0100 Subject: [PATCH 165/237] remove matrix draft remains --- .clang-tidy | 2 - CMakeLists.txt | 12 - .../mlir/Dialect/QCO/IR/CMakeLists.txt | 2 +- mlir/include/mlir/Dialect/QCO/IR/QCODialect.h | 109 +------ mlir/include/mlir/Dialect/QCO/IR/QCOOps.td | 73 ++--- mlir/include/mlir/Dialect/Utils/MatrixUtils.h | 300 ------------------ 6 files changed, 33 insertions(+), 465 deletions(-) delete mode 100644 mlir/include/mlir/Dialect/Utils/MatrixUtils.h diff --git a/.clang-tidy b/.clang-tidy index b7cfa66dcd..3d9e3b1bfd 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -75,5 +75,3 @@ CheckOptions: value: CamelCase - key: readability-identifier-naming.VariableCase value: camelBack - - key: misc-include-cleaner.IgnoreHeaders - value: "Eigen/.*;unsupported/Eigen/.*" diff --git a/CMakeLists.txt b/CMakeLists.txt index bcba881fbb..01867014a5 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,18 +23,6 @@ include(PreventInSourceBuilds) include(PackageAddTest) include(Cache) include(AddMQTCoreLibrary) -include(FetchContent) - -FetchContent_Declare( - Eigen - GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git - GIT_TAG 3.4.1 - GIT_SHALLOW TRUE) -FetchContent_MakeAvailable(Eigen) -if(WIN32 AND ${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL ARM64) - message(STATUS "Enabling non-optimal vectorization in Eigen to avoid alignment issues") - add_compile_definitions(EIGEN_DONT_ALIGN_STATICALLY) -endif() option(BUILD_MQT_CORE_BINDINGS "Build the MQT Core Python bindings" OFF) if(BUILD_MQT_CORE_BINDINGS) diff --git a/mlir/include/mlir/Dialect/QCO/IR/CMakeLists.txt b/mlir/include/mlir/Dialect/QCO/IR/CMakeLists.txt index 4fc8d0dd46..220c177b86 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/CMakeLists.txt +++ b/mlir/include/mlir/Dialect/QCO/IR/CMakeLists.txt @@ -7,6 +7,6 @@ # Licensed under the MIT License add_mlir_dialect(QCOOps qco) -add_mlir_interface(QCOInterfaces LINK_LIBS PUBLIC Eigen3::Eigen) +add_mlir_interface(QCOInterfaces) add_mlir_doc(QCOOps MLIRQCODialect Dialects/ -gen-dialect-doc) add_mlir_doc(QCOInterfaces MLIRQCOInterfaces Dialects/ -gen-op-interface-docs -dialect=qco) diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h index 869ec834fb..67fd0c12a2 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h +++ b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h @@ -29,7 +29,6 @@ #include #include #include -#include #include #define DIALECT_NAME_QCO "qco" @@ -45,7 +44,6 @@ //===----------------------------------------------------------------------===// #define GET_TYPEDEF_CLASSES -#include "mlir/Dialect/QCO/IR/QCOInterfaces.h.inc" #include "mlir/Dialect/QCO/IR/QCOOpsTypes.h.inc" //===----------------------------------------------------------------------===// @@ -54,13 +52,6 @@ namespace mlir::qco { -/** - * @brief Retrieve C++ type of static mlir::Value. - * @details The returned float attribute can be used to get the value of the - * given parameter as a C++ type. - */ -[[nodiscard]] inline std::optional -tryGetParameterAsDouble(UnitaryOpInterface op, size_t i); /** * @brief Trait for operations with a fixed number of target qubits and * parameters @@ -70,15 +61,8 @@ tryGetParameterAsDouble(UnitaryOpInterface op, size_t i); * verification and code generation optimizations. * @tparam T The target arity. * @tparam P The parameter arity. - * @tparam MatrixDefinition A function returning the matrix definition of the - * operation. The operation will be provided as the - * only argument of the function. If the operation does - * not have a matrix definition, set this value to - * nullptr. */ -template -class TargetAndParameterArityTrait { +template class TargetAndParameterArityTrait { public: template class Impl : public OpTrait::TraitBase { @@ -145,93 +129,12 @@ class TargetAndParameterArityTrait { llvm::reportFatalUsageError( "Given qubit is not an input of the operation"); } - - protected: - [[nodiscard]] const Operation* getConstOperation() const { - auto* concrete = static_cast(this); - // use dereference operator instead of getOperation() of mlir::Op; the - // operator provides a const overload, getOperation() does not - return *concrete; - } }; }; } // namespace mlir::qco -// #include "mlir/Dialect/QCO/IR/QCOInterfaces.h.inc" // IWYU pragma: export - -//===----------------------------------------------------------------------===// -// Operations Helpers -//===----------------------------------------------------------------------===// - -namespace mlir::qco { - -[[nodiscard]] inline std::optional -tryGetParameterAsDouble(UnitaryOpInterface op, size_t i) { - using DummyArityType = - TargetAndParameterArityTrait<0, 0, Eigen::MatrixXcd, nullptr>; - const auto param = op.getParameter(i); - const auto floatAttr = - DummyArityType::Impl::getStaticParameter(param); - if (!floatAttr) { - return std::nullopt; - } - return floatAttr.getValueAsDouble(); -} - -[[nodiscard]] inline std::pair -permutate(const Eigen::MatrixXcd& inputMatrix, - const Eigen::VectorXi& permutation) { - const auto swapMatrix = utils::getMatrixSWAP(); - - auto dim = inputMatrix.cols(); - assert(inputMatrix.cols() == inputMatrix.rows()); - assert(dim == permutation.size()); - - Eigen::MatrixXcd permutatedMatrix(dim, dim); - Eigen::VectorXi undoPermutation(permutation.size()); - for (int i = 0; i < permutation.size(); ++i) { - undoPermutation(permutation(i)) = i; - // TODO - } - - return {permutatedMatrix, undoPermutation}; -} - -[[nodiscard]] inline Eigen::MatrixXcd getBlockMatrix(size_t dim, - mlir::Region& region) { - // TODO: check if dim == region.getArguments().size() - assert(dim == 1); // TODO: remove once permutations are properly handled - - Eigen::MatrixXcd result = Eigen::MatrixXcd::Identity(1 << dim, 1 << dim); - for (auto&& block : region) { - for (auto&& op : block) { - auto unitaryOp = llvm::dyn_cast(op); - if (unitaryOp) { - return result; - } - auto matrix = unitaryOp.getUnitaryMatrix(); - size_t matrixDim = matrix.cols(); - if (matrixDim < dim) { - // TODO: permutate such that operation qubits are next to each other; - // then perform front/back padding accordingly - - auto paddingDim = dim - matrixDim; - auto padding = - Eigen::MatrixXcd::Identity(1 << paddingDim, 1 << paddingDim); - matrix = Eigen::kroneckerProduct(matrix, padding); - - // TODO: undo permutation - } - result = matrix * result; - } - } - return result; -} - -mlir::Region& getCtrlBody(UnitaryOpInterface op); - -} // namespace mlir::qco +#include "mlir/Dialect/QCO/IR/QCOInterfaces.h.inc" // IWYU pragma: export //===----------------------------------------------------------------------===// // Operations @@ -239,11 +142,3 @@ mlir::Region& getCtrlBody(UnitaryOpInterface op); #define GET_OP_CLASSES #include "mlir/Dialect/QCO/IR/QCOOps.h.inc" // IWYU pragma: export - -namespace mlir::qco { - -[[nodiscard]] inline mlir::Region& getCtrlBody(UnitaryOpInterface op) { - return llvm::cast(op).getBody(); -} - -} // namespace mlir::qco diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td index cace18c758..1ccf16320c 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td @@ -243,7 +243,6 @@ class TargetAndParameterArityTrait let cppNamespace = "::mlir::qco"; } -def ZeroTargetZeroParameter : TargetAndParameterArityTrait<0, 0>; def ZeroTargetOneParameter : TargetAndParameterArityTrait<0, 1>; def OneTargetZeroParameter : TargetAndParameterArityTrait<1, 0>; def OneTargetOneParameter : TargetAndParameterArityTrait<1, 1>; @@ -253,22 +252,11 @@ def TwoTargetZeroParameter : TargetAndParameterArityTrait<2, 0>; def TwoTargetOneParameter : TargetAndParameterArityTrait<2, 1>; def TwoTargetTwoParameter : TargetAndParameterArityTrait<2, 2>; -class DynamicTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<0, 0, MatrixDefinitionLambda<"Eigen::Dynamic", MatrixDefinitionBody>>; - -class ZeroTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<0, 1, FixedSizeMatrixDefinitionLambda<0, MatrixDefinitionBody>>; -class OneTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 0, FixedSizeMatrixDefinitionLambda<1, MatrixDefinitionBody>>; -class OneTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 1, FixedSizeMatrixDefinitionLambda<1, MatrixDefinitionBody>>; -class OneTargetTwoParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 2, FixedSizeMatrixDefinitionLambda<1, MatrixDefinitionBody>>; -class OneTargetThreeParameterUnitaryMatrix : TargetAndParameterArityTrait<1, 3, FixedSizeMatrixDefinitionLambda<1, MatrixDefinitionBody>>; -class TwoTargetZeroParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 0, FixedSizeMatrixDefinitionLambda<2, MatrixDefinitionBody>>; -class TwoTargetOneParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 1, FixedSizeMatrixDefinitionLambda<2, MatrixDefinitionBody>>; -class TwoTargetTwoParameterUnitaryMatrix : TargetAndParameterArityTrait<2, 2, FixedSizeMatrixDefinitionLambda<2, MatrixDefinitionBody>>; - //===----------------------------------------------------------------------===// // Unitary Operations //===----------------------------------------------------------------------===// -def GPhaseOp : QCOOp<"gphase", traits = [UnitaryOpInterface, ZeroTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixGPhase(tryGetParameterAsDouble(op, 0).value()); }]>, MemoryEffects<[MemWrite]>]> { +def GPhaseOp : QCOOp<"gphase", traits = [UnitaryOpInterface, ZeroTargetOneParameter, MemoryEffects<[MemWrite]>]> { let summary = "Apply a global phase to the state"; let description = [{ Applies a global phase to the state. @@ -294,7 +282,7 @@ def GPhaseOp : QCOOp<"gphase", traits = [UnitaryOpInterface, ZeroTargetOneParame let hasCanonicalizer = 1; } -def IdOp : QCOOp<"id", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixId(); }]>]> { +def IdOp : QCOOp<"id", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply an Id gate to a qubit"; let description = [{ Applies an Id gate to a qubit and returns the transformed qubit. @@ -317,7 +305,7 @@ def IdOp : QCOOp<"id", traits = [UnitaryOpInterface, OneTargetZeroParameterUnita let hasCanonicalizer = 1; } -def XOp : QCOOp<"x", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixX(); }]>]> { +def XOp : QCOOp<"x", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply an X gate to a qubit"; let description = [{ Applies an X gate to a qubit and returns the transformed qubit. @@ -340,7 +328,7 @@ def XOp : QCOOp<"x", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitary let hasCanonicalizer = 1; } -def YOp : QCOOp<"y", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixY(); }]>]> { +def YOp : QCOOp<"y", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply a Y gate to a qubit"; let description = [{ Applies a Y gate to a qubit and returns the transformed qubit. @@ -363,7 +351,7 @@ def YOp : QCOOp<"y", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitary let hasCanonicalizer = 1; } -def ZOp : QCOOp<"z", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixZ(); }]>]> { +def ZOp : QCOOp<"z", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply a Z gate to a qubit"; let description = [{ Applies a Z gate to a qubit and returns the transformed qubit. @@ -386,7 +374,7 @@ def ZOp : QCOOp<"z", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitary let hasCanonicalizer = 1; } -def HOp : QCOOp<"h", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixH(); }]>]> { +def HOp : QCOOp<"h", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply a H gate to a qubit"; let description = [{ Applies a H gate to a qubit and returns the transformed qubit. @@ -409,7 +397,7 @@ def HOp : QCOOp<"h", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitary let hasCanonicalizer = 1; } -def SOp : QCOOp<"s", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixS(); }]>]> { +def SOp : QCOOp<"s", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply an S gate to a qubit"; let description = [{ Applies an S gate to a qubit and returns the transformed qubit. @@ -432,7 +420,7 @@ def SOp : QCOOp<"s", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitary let hasCanonicalizer = 1; } -def SdgOp : QCOOp<"sdg", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixSdg(); }]>]> { +def SdgOp : QCOOp<"sdg", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply an Sdg gate to a qubit"; let description = [{ Applies an Sdg gate to a qubit and returns the transformed qubit. @@ -455,7 +443,7 @@ def SdgOp : QCOOp<"sdg", traits = [UnitaryOpInterface, OneTargetZeroParameterUni let hasCanonicalizer = 1; } -def TOp : QCOOp<"t", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixT(); }]>]> { +def TOp : QCOOp<"t", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply a T gate to a qubit"; let description = [{ Applies a T gate to a qubit and returns the transformed qubit. @@ -478,7 +466,7 @@ def TOp : QCOOp<"t", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitary let hasCanonicalizer = 1; } -def TdgOp : QCOOp<"tdg", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixTdg(); }]>]> { +def TdgOp : QCOOp<"tdg", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply a Tdg gate to a qubit"; let description = [{ Applies a Tdg gate to a qubit and returns the transformed qubit. @@ -501,7 +489,7 @@ def TdgOp : QCOOp<"tdg", traits = [UnitaryOpInterface, OneTargetZeroParameterUni let hasCanonicalizer = 1; } -def SXOp : QCOOp<"sx", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixSX(); }]>]> { +def SXOp : QCOOp<"sx", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply an SX gate to a qubit"; let description = [{ Applies an SX gate to a qubit and returns the transformed qubit. @@ -524,7 +512,7 @@ def SXOp : QCOOp<"sx", traits = [UnitaryOpInterface, OneTargetZeroParameterUnita let hasCanonicalizer = 1; } -def SXdgOp : QCOOp<"sxdg", traits = [UnitaryOpInterface, OneTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixSXdg(); }]>]> { +def SXdgOp : QCOOp<"sxdg", traits = [UnitaryOpInterface, OneTargetZeroParameter]> { let summary = "Apply an SXdg gate to a qubit"; let description = [{ Applies an SXdg gate to a qubit and returns the transformed qubit. @@ -547,7 +535,7 @@ def SXdgOp : QCOOp<"sxdg", traits = [UnitaryOpInterface, OneTargetZeroParameterU let hasCanonicalizer = 1; } -def RXOp : QCOOp<"rx", traits = [UnitaryOpInterface, OneTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRX(tryGetParameterAsDouble(op, 0).value()); }]>]> { +def RXOp : QCOOp<"rx", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let summary = "Apply an RX gate to a qubit"; let description = [{ Applies an RX gate to a qubit and returns the transformed qubit. @@ -575,7 +563,7 @@ def RXOp : QCOOp<"rx", traits = [UnitaryOpInterface, OneTargetOneParameterUnitar let hasCanonicalizer = 1; } -def RYOp : QCOOp<"ry", traits = [UnitaryOpInterface, OneTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRY(tryGetParameterAsDouble(op, 0).value()); }]>]> { +def RYOp : QCOOp<"ry", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let summary = "Apply an RY gate to a qubit"; let description = [{ Applies an RY gate to a qubit and returns the transformed qubit. @@ -603,7 +591,7 @@ def RYOp : QCOOp<"ry", traits = [UnitaryOpInterface, OneTargetOneParameterUnitar let hasCanonicalizer = 1; } -def RZOp : QCOOp<"rz", traits = [UnitaryOpInterface, OneTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRZ(tryGetParameterAsDouble(op, 0).value()); }]>]> { +def RZOp : QCOOp<"rz", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let summary = "Apply an RZ gate to a qubit"; let description = [{ Applies an RZ gate to a qubit and returns the transformed qubit. @@ -631,7 +619,7 @@ def RZOp : QCOOp<"rz", traits = [UnitaryOpInterface, OneTargetOneParameterUnitar let hasCanonicalizer = 1; } -def POp : QCOOp<"p", traits = [UnitaryOpInterface, OneTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixP(tryGetParameterAsDouble(op, 0).value()); }]>]> { +def POp : QCOOp<"p", traits = [UnitaryOpInterface, OneTargetOneParameter]> { let summary = "Apply a P gate to a qubit"; let description = [{ Applies a P gate to a qubit and returns the transformed qubit. @@ -659,7 +647,7 @@ def POp : QCOOp<"p", traits = [UnitaryOpInterface, OneTargetOneParameterUnitaryM let hasCanonicalizer = 1; } -def ROp : QCOOp<"r", traits = [UnitaryOpInterface, OneTargetTwoParameterUnitaryMatrix<[{ return utils::getMatrixR(tryGetParameterAsDouble(op, 0).value(), tryGetParameterAsDouble(op, 1).value()); }]>]> { +def ROp : QCOOp<"r", traits = [UnitaryOpInterface, OneTargetTwoParameter]> { let summary = "Apply an R gate to a qubit"; let description = [{ Applies an R gate to a qubit and returns the transformed qubit. @@ -688,7 +676,7 @@ def ROp : QCOOp<"r", traits = [UnitaryOpInterface, OneTargetTwoParameterUnitaryM let hasCanonicalizer = 1; } -def U2Op : QCOOp<"u2", traits = [UnitaryOpInterface, OneTargetTwoParameterUnitaryMatrix<[{ return utils::getMatrixU2(tryGetParameterAsDouble(op, 0).value(), tryGetParameterAsDouble(op, 1).value()); }]>]> { +def U2Op : QCOOp<"u2", traits = [UnitaryOpInterface, OneTargetTwoParameter]> { let summary = "Apply a U2 gate to a qubit"; let description = [{ Applies a U2 gate to a qubit and returns the transformed qubit. @@ -717,7 +705,7 @@ def U2Op : QCOOp<"u2", traits = [UnitaryOpInterface, OneTargetTwoParameterUnitar let hasCanonicalizer = 1; } -def UOp : QCOOp<"u", traits = [UnitaryOpInterface, OneTargetThreeParameterUnitaryMatrix<[{ return utils::getMatrixU(tryGetParameterAsDouble(op, 0).value(), tryGetParameterAsDouble(op, 1).value(), tryGetParameterAsDouble(op, 2).value()); }]>]> { +def UOp : QCOOp<"u", traits = [UnitaryOpInterface, OneTargetThreeParameter]> { let summary = "Apply a U gate to a qubit"; let description = [{ Applies a U gate to a qubit and returns the transformed qubit. @@ -747,7 +735,7 @@ def UOp : QCOOp<"u", traits = [UnitaryOpInterface, OneTargetThreeParameterUnitar let hasCanonicalizer = 1; } -def SWAPOp : QCOOp<"swap", traits = [UnitaryOpInterface, TwoTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixSWAP(); }]>]> { +def SWAPOp : QCOOp<"swap", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> { let summary = "Apply a SWAP gate to two qubits"; let description = [{ Applies a SWAP gate to two qubits and returns the transformed qubits. @@ -771,7 +759,7 @@ def SWAPOp : QCOOp<"swap", traits = [UnitaryOpInterface, TwoTargetZeroParameterU let hasCanonicalizer = 1; } -def iSWAPOp : QCOOp<"iswap", traits = [UnitaryOpInterface, TwoTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixiSWAP(); }]>]> { +def iSWAPOp : QCOOp<"iswap", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> { let summary = "Apply a iSWAP gate to two qubits"; let description = [{ Applies a iSWAP gate to two qubits and returns the transformed qubits. @@ -793,7 +781,7 @@ def iSWAPOp : QCOOp<"iswap", traits = [UnitaryOpInterface, TwoTargetZeroParamete }]; } -def DCXOp : QCOOp<"dcx", traits = [UnitaryOpInterface, TwoTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixDCX(); }]>]> { +def DCXOp : QCOOp<"dcx", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> { let summary = "Apply a DCX gate to two qubits"; let description = [{ Applies a DCX gate to two qubits and returns the transformed qubits. @@ -815,7 +803,7 @@ def DCXOp : QCOOp<"dcx", traits = [UnitaryOpInterface, TwoTargetZeroParameterUni }]; } -def ECROp : QCOOp<"ecr", traits = [UnitaryOpInterface, TwoTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixECR(); }]>]> { +def ECROp : QCOOp<"ecr", traits = [UnitaryOpInterface, TwoTargetZeroParameter]> { let summary = "Apply an ECR gate to two qubits"; let description = [{ Applies an ECR gate to two qubits and returns the transformed qubits. @@ -839,7 +827,7 @@ def ECROp : QCOOp<"ecr", traits = [UnitaryOpInterface, TwoTargetZeroParameterUni let hasCanonicalizer = 1; } -def RXXOp : QCOOp<"rxx", traits = [UnitaryOpInterface, TwoTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRXX(tryGetParameterAsDouble(op, 0).value()); }]>]> { +def RXXOp : QCOOp<"rxx", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let summary = "Apply an RXX gate to two qubits"; let description = [{ Applies an RXX gate to two qubits and returns the transformed qubits. @@ -868,7 +856,7 @@ def RXXOp : QCOOp<"rxx", traits = [UnitaryOpInterface, TwoTargetOneParameterUnit let hasCanonicalizer = 1; } -def RYYOp : QCOOp<"ryy", traits = [UnitaryOpInterface, TwoTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRYY(tryGetParameterAsDouble(op, 0).value()); }]>]> { +def RYYOp : QCOOp<"ryy", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let summary = "Apply an RYY gate to two qubits"; let description = [{ Applies an RYY gate to two qubits and returns the transformed qubits. @@ -897,7 +885,7 @@ def RYYOp : QCOOp<"ryy", traits = [UnitaryOpInterface, TwoTargetOneParameterUnit let hasCanonicalizer = 1; } -def RZXOp : QCOOp<"rzx", traits = [UnitaryOpInterface, TwoTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRZX(tryGetParameterAsDouble(op, 0).value()); }]>]> { +def RZXOp : QCOOp<"rzx", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let summary = "Apply an RZX gate to two qubits"; let description = [{ Applies an RZX gate to two qubits and returns the transformed qubits. @@ -926,7 +914,7 @@ def RZXOp : QCOOp<"rzx", traits = [UnitaryOpInterface, TwoTargetOneParameterUnit let hasCanonicalizer = 1; } -def RZZOp : QCOOp<"rzz", traits = [UnitaryOpInterface, TwoTargetOneParameterUnitaryMatrix<[{ return utils::getMatrixRZZ(tryGetParameterAsDouble(op, 0).value()); }]>]> { +def RZZOp : QCOOp<"rzz", traits = [UnitaryOpInterface, TwoTargetOneParameter]> { let summary = "Apply an RZZ gate to two qubits"; let description = [{ Applies an RZZ gate to two qubits and returns the transformed qubits. @@ -955,7 +943,7 @@ def RZZOp : QCOOp<"rzz", traits = [UnitaryOpInterface, TwoTargetOneParameterUnit let hasCanonicalizer = 1; } -def XXPlusYYOp : QCOOp<"xx_plus_yy", traits = [UnitaryOpInterface, TwoTargetTwoParameterUnitaryMatrix<[{ return utils::getMatrixXXPlusYY(tryGetParameterAsDouble(op, 0).value(), tryGetParameterAsDouble(op, 1).value()); }]>]> { +def XXPlusYYOp : QCOOp<"xx_plus_yy", traits = [UnitaryOpInterface, TwoTargetTwoParameter]> { let summary = "Apply an XX+YY gate to two qubits"; let description = [{ Applies an XX+YY gate to two qubits and returns the transformed qubits. @@ -985,7 +973,7 @@ def XXPlusYYOp : QCOOp<"xx_plus_yy", traits = [UnitaryOpInterface, TwoTargetTwoP let hasCanonicalizer = 1; } -def XXMinusYYOp : QCOOp<"xx_minus_yy", traits = [UnitaryOpInterface, TwoTargetTwoParameterUnitaryMatrix<[{ return utils::getMatrixXXMinusYY(tryGetParameterAsDouble(op, 0).value(), tryGetParameterAsDouble(op, 1).value()); }]>]> { +def XXMinusYYOp : QCOOp<"xx_minus_yy", traits = [UnitaryOpInterface, TwoTargetTwoParameter]> { let summary = "Apply an XX-YY gate to two qubits"; let description = [{ Applies an XX-YY gate to two qubits and returns the transformed qubits. @@ -1015,7 +1003,7 @@ def XXMinusYYOp : QCOOp<"xx_minus_yy", traits = [UnitaryOpInterface, TwoTargetTw let hasCanonicalizer = 1; } -def BarrierOp : QCOOp<"barrier", traits = [UnitaryOpInterface, ZeroTargetZeroParameter]> { +def BarrierOp : QCOOp<"barrier", traits = [UnitaryOpInterface]> { let summary = "Apply a barrier gate to a set of qubits"; let description = [{ Applies a barrier gate to a set of qubits and returns the transformed qubits. @@ -1080,7 +1068,6 @@ def YieldOp : QCOOp<"yield", traits = [Terminator]> { def CtrlOp : QCOOp<"ctrl", traits = [ UnitaryOpInterface, - DynamicTargetZeroParameterUnitaryMatrix<[{ return utils::getMatrixCtrl(op.getNumControls(), getBlockMatrix(op.getNumTargets(), getCtrlBody(op))); }]>, AttrSizedOperandSegments, AttrSizedResultSegments, SameOperandsAndResultType, diff --git a/mlir/include/mlir/Dialect/Utils/MatrixUtils.h b/mlir/include/mlir/Dialect/Utils/MatrixUtils.h deleted file mode 100644 index 16fa93e785..0000000000 --- a/mlir/include/mlir/Dialect/Utils/MatrixUtils.h +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM - * Copyright (c) 2025 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include "mlir/Dialect/QCO/IR/QCODialect.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mlir::utils { - -inline Eigen::Matrix, 1, 1> getMatrixGPhase(double theta) { - using namespace std::complex_literals; - return Eigen::Matrix, 1, 1>{std::exp(1i * theta)}; -} - -inline Eigen::Matrix2cd getMatrixId() { - using namespace std::complex_literals; - return Eigen::Matrix2cd{{1.0 + 0i, 0.0 + 0i}, // row 0 - {0.0 + 0i, 1.0 + 0i}}; // row 1 -} - -inline Eigen::Matrix2cd getMatrixX() { - using namespace std::complex_literals; - return Eigen::Matrix2cd{{0.0 + 0i, 1.0 + 0i}, // row 0 - {1.0 + 0i, 0.0 + 0i}}; // row 1 -} - -inline Eigen::Matrix2cd getMatrixY() { - using namespace std::complex_literals; - return Eigen::Matrix2cd{{0.0 + 0i, 0.0 - 1i}, // row 0 - {0.0 + 1i, 0.0 + 0i}}; // row 1 -} - -inline Eigen::Matrix2cd getMatrixZ() { - using namespace std::complex_literals; - return Eigen::Matrix2cd{{1.0 + 0i, 0.0 + 0i}, // row 0 - {0.0 + 0i, -1.0 + 0i}}; // row 1 -} - -inline Eigen::Matrix2cd getMatrixH() { - using namespace std::complex_literals; - const std::complex m00 = 1.0 / std::sqrt(2) + 0i; - const std::complex m11 = -1.0 / std::sqrt(2) + 0i; - return Eigen::Matrix2cd{{m00, m00}, {m00, m11}}; -} - -inline Eigen::Matrix2cd getMatrixS() { - using namespace std::complex_literals; - return Eigen::Matrix2cd{{1.0 + 0i, 0.0 + 0i}, // row 0 - {0.0 + 0i, 0.0 + 1i}}; // row 1 -} - -inline Eigen::Matrix2cd getMatrixSdg() { - using namespace std::complex_literals; - return Eigen::Matrix2cd{{1.0 + 0i, 0.0 + 0i}, // row 0 - {0.0 + 0i, 0.0 - 1i}}; // row 1 -} - -inline Eigen::Matrix2cd getMatrixT() { - using namespace std::complex_literals; - const std::complex m00 = 1.0 + 0i; - const std::complex m01 = 0.0 + 0i; - const std::complex m11 = std::exp(1i * std::numbers::pi / 4.0); - return Eigen::Matrix2cd{{m00, m01}, {m01, m11}}; -} - -inline Eigen::Matrix2cd getMatrixTdg() { - using namespace std::complex_literals; - const std::complex m00 = 1.0 + 0i; - const std::complex m01 = 0.0 + 0i; - const std::complex m11 = std::exp(-1i * std::numbers::pi / 4.0); - return Eigen::Matrix2cd{{m00, m01}, {m01, m11}}; -} - -inline Eigen::Matrix2cd getMatrixSX() { - using namespace std::complex_literals; - const std::complex m00 = (1.0 + 1i) / 2.0; - const std::complex m01 = (1.0 - 1i) / 2.0; - return Eigen::Matrix2cd{{m00, m01}, {m01, m00}}; -} - -inline Eigen::Matrix2cd getMatrixSXdg() { - using namespace std::complex_literals; - const std::complex m00 = (1.0 - 1i) / 2.0; - const std::complex m01 = (1.0 + 1i) / 2.0; - return Eigen::Matrix2cd{{m00, m01}, {m01, m00}}; -} - -inline Eigen::Matrix2cd getMatrixRX(double theta) { - using namespace std::complex_literals; - const std::complex m00 = std::cos(theta / 2.0) + 0i; - const std::complex m01 = -1i * std::sin(theta / 2.0); - return Eigen::Matrix2cd{{m00, m01}, {m01, m00}}; -} - -inline Eigen::Matrix2cd getMatrixRY(double theta) { - using namespace std::complex_literals; - const std::complex m00 = std::cos(theta / 2.0) + 0i; - const std::complex m01 = -std::sin(theta / 2.0) + 0i; - return Eigen::Matrix2cd{{m00, m01}, {-m01, m00}}; -} - -inline Eigen::Matrix2cd getMatrixRZ(double theta) { - using namespace std::complex_literals; - const std::complex m00 = std::exp(-1i * theta / 2.0); - const std::complex m01 = 0.0 + 0i; - const std::complex m11 = std::exp(1i * theta / 2.0); - return Eigen::Matrix2cd{{m00, m01}, {m01, m11}}; -} - -inline Eigen::Matrix2cd getMatrixP(double theta) { - using namespace std::complex_literals; - const std::complex m00 = 1.0 + 0i; - const std::complex m01 = 0.0 + 0i; - const std::complex m11 = std::exp(1i * theta); - return Eigen::Matrix2cd{{m00, m01}, {m01, m11}}; -} - -inline Eigen::Matrix2cd getMatrixU2(double phi, double lambda) { - using namespace std::complex_literals; - const std::complex m00 = 1.0 / std::sqrt(2) + 0i; - const std::complex m01 = -std::exp(1i * lambda) / std::sqrt(2); - const std::complex m10 = std::exp(1i * phi) / std::sqrt(2); - const std::complex m11 = std::exp(1i * (phi + lambda)) / std::sqrt(2); - return Eigen::Matrix2cd{{m00, m01}, {m10, m11}}; -} - -inline Eigen::Matrix2cd getMatrixU(double theta, double phi, double lambda) { - using namespace std::complex_literals; - const std::complex m00 = std::cos(theta / 2.0) + 0i; - const std::complex m01 = - -std::exp(1i * lambda) * std::sin(theta / 2.0); - const std::complex m10 = std::exp(1i * phi) * std::sin(theta / 2.0); - const std::complex m11 = - std::exp(1i * (phi + lambda)) * std::cos(theta / 2.0); - return Eigen::Matrix2cd{{m00, m01}, {m10, m11}}; -} - -inline Eigen::Matrix2cd getMatrixR(double theta, double phi) { - using namespace std::complex_literals; - const std::complex m00 = std::cos(theta / 2.0) + 0i; - const std::complex m01 = - -1i * std::exp(-1i * phi) * std::sin(theta / 2.0); - const std::complex m10 = - -1i * std::exp(1i * phi) * std::sin(theta / 2.0); - const std::complex m11 = std::cos(theta / 2.0) + 0i; - return Eigen::Matrix2cd{{m00, m01}, {m10, m11}}; -} - -inline Eigen::Matrix4cd getMatrixSWAP() { - using namespace std::complex_literals; - return Eigen::Matrix4cd{{1.0 + 0i, 0.0 + 0i, 0.0 + 0i, 0.0 + 0i}, // row 0 - {0.0 + 0i, 0.0 + 0i, 1.0 + 0i, 0.0 + 0i}, // row 1 - {0.0 + 0i, 1.0 + 0i, 0.0 + 0i, 0.0 + 0i}, // row 2 - {0.0 + 0i, 0.0 + 0i, 0.0 + 0i, 1.0 + 0i}}; // row 3 -} - -inline Eigen::Matrix4cd getMatrixiSWAP() { - using namespace std::complex_literals; - return Eigen::Matrix4cd{{1.0 + 0i, 0.0 + 0i, 0.0 + 0i, 0.0 + 0i}, // row 0 - {0.0 + 0i, 0.0 + 0i, 0.0 + 1i, 0.0 + 0i}, // row 1 - {0.0 + 0i, 0.0 + 1i, 0.0 + 0i, 0.0 + 0i}, // row 2 - {0.0 + 0i, 0.0 + 0i, 0.0 + 0i, 1.0 + 0i}}; // row 3 -} - -inline Eigen::Matrix4cd getMatrixDCX() { - using namespace std::complex_literals; - return Eigen::Matrix4cd{{1.0 + 0i, 0.0 + 0i, 0.0 + 0i, 0.0 + 0i}, // row 0 - {0.0 + 0i, 0.0 + 0i, 1.0 + 0i, 0.0 + 0i}, // row 1 - {0.0 + 0i, 0.0 + 0i, 0.0 + 0i, 1.0 + 0i}, // row 2 - {0.0 + 0i, 1.0 + 0i, 0.0 + 0i, 0.0 + 0i}}; // row 3 -} - -inline Eigen::Matrix4cd getMatrixECR() { - using namespace std::complex_literals; - const std::complex m0 = 0.0 + 0i; - const std::complex m1 = 1.0 / std::sqrt(2) + 0i; - const std::complex mi = 0.0 + 1i / std::sqrt(2); - return Eigen::Matrix4cd{{m0, m0, m1, mi}, // row 0 - {m0, m0, mi, m1}, // row 1 - {m1, -mi, m0, m0}, // row 2 - {-mi, m1, m0, m0}}; // row 3 -} - -inline Eigen::Matrix4cd getMatrixRXX(double theta) { - using namespace std::complex_literals; - const std::complex m0 = 0.0 + 0i; - const std::complex mc = std::cos(theta / 2.0) + 0i; - const std::complex ms = -1i * std::sin(theta / 2.0); - return Eigen::Matrix4cd{{mc, m0, m0, ms}, // row 0 - {m0, mc, ms, m0}, // row 1 - {m0, ms, mc, m0}, // row 2 - {ms, m0, m0, mc}}; // row 3 -} - -inline Eigen::Matrix4cd getMatrixRYY(double theta) { - using namespace std::complex_literals; - const std::complex m0 = 0.0 + 0i; - const std::complex mc = std::cos(theta / 2.0) + 0i; - const std::complex ms = 1i * std::sin(theta / 2.0); - return Eigen::Matrix4cd{{mc, m0, m0, ms}, // row 0 - {m0, mc, -ms, m0}, // row 1 - {m0, -ms, mc, m0}, // row 2 - {ms, m0, m0, mc}}; // row 3 -} - -inline Eigen::Matrix4cd getMatrixRZX(double theta) { - using namespace std::complex_literals; - const std::complex m0 = 0.0 + 0i; - const std::complex mc = std::cos(theta / 2.0) + 0i; - const std::complex ms = -1i * std::sin(theta / 2.0); - return Eigen::Matrix4cd{{mc, -ms, m0, m0}, // row 0 - {-ms, mc, m0, m0}, // row 1 - {m0, m0, mc, ms}, // row 2 - {m0, m0, ms, mc}}; // row 3 -} - -inline Eigen::Matrix4cd getMatrixRZZ(double theta) { - using namespace std::complex_literals; - const std::complex m0 = 0.0 + 0i; - const std::complex mp = std::exp(1i * theta / 2.0); - const std::complex mm = std::exp(-1i * theta / 2.0); - return Eigen::Matrix4cd{{mm, m0, m0, m0}, // row 0 - {m0, mp, m0, m0}, // row 1 - {m0, m0, mp, m0}, // row 2 - {m0, m0, m0, mm}}; // row 3 -} - -inline Eigen::Matrix4cd getMatrixXXPlusYY(double theta, double beta) { - using namespace std::complex_literals; - const std::complex m0 = 0.0 + 0i; - const std::complex m1 = 1.0 + 0i; - const std::complex mc = std::cos(theta / 2.0) + 0i; - const std::complex msp = - -1i * std::sin(theta / 2.0) * std::exp(1i * beta); - const std::complex msm = - -1i * std::sin(theta / 2.0) * std::exp(-1i * beta); - return Eigen::Matrix4cd{{m1, m0, m0, m0}, // row 0 - {m0, mc, msm, m0}, // row 1 - {m0, msp, mc, m0}, // row 2 - {m0, m0, m0, m1}}; // row 3 -} - -inline Eigen::Matrix4cd getMatrixXXMinusYY(double theta, double beta) { - using namespace std::complex_literals; - const std::complex m0 = 0.0 + 0i; - const std::complex m1 = 1.0 + 0i; - const std::complex mc = std::cos(theta / 2.0) + 0i; - const std::complex msp = - std::sin(theta / 2.0) * std::exp(-1i * beta) + 0i; - const std::complex msm = - -std::sin(theta / 2.0) * std::exp(1i * beta) + 0i; - return Eigen::Matrix4cd{{mc, m0, m0, msm}, // row 0 - {m0, m1, m0, m0}, // row 1 - {m0, m0, m1, m0}, // row 2 - {msp, m0, m0, mc}}; // row 3 -} - -inline Eigen::MatrixXcd getMatrixCtrl(size_t numControls, - Eigen::MatrixXcd targetMatrix) { - // Get dimensions of target matrix - const auto targetDim = targetMatrix.cols(); - assert(targetMatrix.cols() == targetMatrix.rows()); - - // Define dimensions and type of output matrix - const auto dim = static_cast((1 << numControls) * targetDim); - - // Allocate output matrix - Eigen::MatrixXcd matrix = Eigen::MatrixXcd::Identity(dim, dim); - - // TODO: apply permutation such that target qubits are last - - // Fill output matrix - for (int64_t i = 0; i < targetDim; ++i) { - for (int64_t j = 0; j < targetDim; ++j) { - matrix(dim - targetDim - j, dim - targetDim - i) = targetMatrix(j, i); - } - } - - // TODO: undo permutation - - return matrix; -} - -} // namespace mlir::utils From 1a8fa76041c2cbcaa80f4045f6bc9273bfc47552 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 21 Jan 2026 12:07:47 +0100 Subject: [PATCH 166/237] fix compilation issues with new matrix implementation --- .../Patterns/GateDecompositionPattern.cpp | 60 ++++++++++++------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 662991b68a..7e6ab2bdd9 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -84,10 +84,14 @@ struct GateDecompositionPattern final if (series.isSingleQubitSeries()) { // only a single-qubit series; // single-qubit euler decomposition is more efficient - const matrix2x2 unitaryMatrix = series.getSingleQubitUnitaryMatrix(); + const auto unitaryMatrix = series.getSingleQubitUnitaryMatrix(); + if (!unitaryMatrix) { + // cannot process decomposition without the matrix of the series + return mlir::failure(); + } for (auto&& eulerBasis : decomposerEulerBases) { auto sequence = decomposition::EulerDecomposition::generateCircuit( - eulerBasis, unitaryMatrix, true, std::nullopt); + eulerBasis, *unitaryMatrix, true, std::nullopt); if (!bestSequence || sequence.complexity() < bestSequence->complexity()) { bestSequence = sequence; @@ -95,9 +99,13 @@ struct GateDecompositionPattern final } } else { // two-qubit series; perform two-qubit basis decomposition - const matrix4x4 unitaryMatrix = series.getUnitaryMatrix(); + const auto unitaryMatrix = series.getUnitaryMatrix(); + if (!unitaryMatrix) { + // cannot process decomposition without the matrix of the series + return mlir::failure(); + } const auto targetDecomposition = - decomposition::TwoQubitWeylDecomposition::create(unitaryMatrix, + decomposition::TwoQubitWeylDecomposition::create(*unitaryMatrix, DEFAULT_FIDELITY); for (const auto& decomposer : basisDecomposers) { @@ -229,37 +237,45 @@ struct GateDecompositionPattern final return result; } - [[nodiscard]] matrix2x2 getSingleQubitUnitaryMatrix() { + [[nodiscard]] std::optional getSingleQubitUnitaryMatrix() { auto unitaryMatrix = decomposition::IDENTITY_GATE; for (auto&& gate : gates) { - // auto gateMatrix = gate.op.getFastUnitaryMatrix(); - auto gateMatrix = gate.op.getUnitaryMatrix(); - unitaryMatrix = gateMatrix * unitaryMatrix; + if (auto gateMatrix = gate.op.getUnitaryMatrix()) { + unitaryMatrix = *gateMatrix * unitaryMatrix; + } else { + return std::nullopt; + } } assert(helpers::isUnitaryMatrix(unitaryMatrix)); return unitaryMatrix; } - [[nodiscard]] matrix4x4 getUnitaryMatrix() { + [[nodiscard]] std::optional getUnitaryMatrix() { matrix4x4 unitaryMatrix = helpers::kroneckerProduct( decomposition::IDENTITY_GATE, decomposition::IDENTITY_GATE); + matrix4x4 gateMatrix; for (auto&& gate : gates) { - auto gateMatrix = gate.op.getUnitaryMatrix(); if (gate.op.isSingleQubit()) { assert(gate.qubitIds.size() == 1); - // TODO: use helpers::kroneckerProduct or Eigen::kroneckerProduct? + auto matrix = gate.op.getUnitaryMatrix(); + if (!matrix) { + return std::nullopt; + } if (gate.qubitIds[0] == 0) { - gateMatrix = Eigen::kroneckerProduct(decomposition::IDENTITY_GATE, - gateMatrix) - .eval(); + gateMatrix = helpers::kroneckerProduct(decomposition::IDENTITY_GATE, + *matrix); } else if (gate.qubitIds[0] == 1) { - gateMatrix = Eigen::kroneckerProduct(gateMatrix, - decomposition::IDENTITY_GATE) - .eval(); + gateMatrix = helpers::kroneckerProduct( + *matrix, decomposition::IDENTITY_GATE); + } + } else if (gate.op.isTwoQubit()) { + if (auto matrix = gate.op.getUnitaryMatrix()) { + gateMatrix = std::move(*matrix); + } else { + return std::nullopt; } } - // auto gateMatrix = gate.op.getFastUnitaryMatrix(); unitaryMatrix = gateMatrix * unitaryMatrix; } @@ -339,13 +355,14 @@ struct GateDecompositionPattern final return false; } // TODO: this only works because parameters are at end of operands; - // use future getInputQubits() instead + // use to-be-implemented getInputQubits() instead auto&& opInQubits = nextGate->getOperands(); // iterator in the operation input of "old" qubit that already has // previous single-qubit gates in this series auto it2 = llvm::find(opInQubits, firstQubitIt != outQubits.end() ? *firstQubitIt : *secondQubitIt); + assert(it2 != opInQubits.end()); // new qubit ID based on position in outQubits const QubitId newInQubitId = std::distance(outQubits.begin(), it); // position in operation input; since there are only two qubits, it must @@ -498,7 +515,8 @@ struct GateDecompositionPattern final } } assert((unitaryMatrix * std::exp(IM * sequence.globalPhase)) - .isApprox(series.getUnitaryMatrix(), SANITY_CHECK_PRECISION)); + .isApprox(series.getUnitaryMatrix().value_or(matrix4x4::Zero()), + SANITY_CHECK_PRECISION)); if (series.isSingleQubitSeries()) { rewriter.replaceAllUsesWith(series.outQubits[0], inQubits[0]); @@ -507,7 +525,7 @@ struct GateDecompositionPattern final } for (auto&& gate : llvm::reverse(series.gates)) { if (auto ctrlOp = llvm::dyn_cast(*gate.op)) { - rewriter.eraseBlock(&ctrlOp.getBody().front()); + rewriter.eraseOp(ctrlOp.getBodyUnitary()); } rewriter.eraseOp(gate.op); } From 0514c5482ea7399eaade5029468b0523fc3761ca Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 11:08:16 +0000 Subject: [PATCH 167/237] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/include/mlir/Passes/CMakeLists.txt | 4 ++-- mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h | 4 ++-- mlir/include/mlir/Passes/Decomposition/EulerBasis.h | 4 ++-- mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h | 4 ++-- mlir/include/mlir/Passes/Decomposition/Gate.h | 4 ++-- mlir/include/mlir/Passes/Decomposition/GateSequence.h | 4 ++-- mlir/include/mlir/Passes/Decomposition/Helpers.h | 4 ++-- mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h | 4 ++-- mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h | 4 ++-- mlir/include/mlir/Passes/Passes.h | 4 ++-- mlir/include/mlir/Passes/Passes.td | 4 ++-- mlir/lib/Passes/CMakeLists.txt | 4 ++-- mlir/lib/Passes/GateDecompositionPass.cpp | 4 ++-- mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp | 4 ++-- mlir/unittests/CMakeLists.txt | 5 +++-- mlir/unittests/decomposition/CMakeLists.txt | 4 ++-- mlir/unittests/decomposition/test_basis_decomposer.cpp | 4 ++-- mlir/unittests/decomposition/test_euler_decomposition.cpp | 4 ++-- mlir/unittests/decomposition/test_weyl_decomposition.cpp | 4 ++-- 19 files changed, 39 insertions(+), 38 deletions(-) diff --git a/mlir/include/mlir/Passes/CMakeLists.txt b/mlir/include/mlir/Passes/CMakeLists.txt index 03e903ad72..9d11c5ca00 100644 --- a/mlir/include/mlir/Passes/CMakeLists.txt +++ b/mlir/include/mlir/Passes/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM -# Copyright (c) 2025 Munich Quantum Software Company GmbH +# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM +# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH # All rights reserved. # # SPDX-License-Identifier: MIT diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index cc4d510a0f..b6500d9365 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM - * Copyright (c) 2025 Munich Quantum Software Company GmbH + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH * All rights reserved. * * SPDX-License-Identifier: MIT diff --git a/mlir/include/mlir/Passes/Decomposition/EulerBasis.h b/mlir/include/mlir/Passes/Decomposition/EulerBasis.h index 887db1a272..ab781534f5 100644 --- a/mlir/include/mlir/Passes/Decomposition/EulerBasis.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerBasis.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM - * Copyright (c) 2025 Munich Quantum Software Company GmbH + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH * All rights reserved. * * SPDX-License-Identifier: MIT diff --git a/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h index 35220464c7..8db67d6571 100644 --- a/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM - * Copyright (c) 2025 Munich Quantum Software Company GmbH + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH * All rights reserved. * * SPDX-License-Identifier: MIT diff --git a/mlir/include/mlir/Passes/Decomposition/Gate.h b/mlir/include/mlir/Passes/Decomposition/Gate.h index f6ca1d3d8d..df0fabde3d 100644 --- a/mlir/include/mlir/Passes/Decomposition/Gate.h +++ b/mlir/include/mlir/Passes/Decomposition/Gate.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM - * Copyright (c) 2025 Munich Quantum Software Company GmbH + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH * All rights reserved. * * SPDX-License-Identifier: MIT diff --git a/mlir/include/mlir/Passes/Decomposition/GateSequence.h b/mlir/include/mlir/Passes/Decomposition/GateSequence.h index fd66133f4e..f7cd6458b7 100644 --- a/mlir/include/mlir/Passes/Decomposition/GateSequence.h +++ b/mlir/include/mlir/Passes/Decomposition/GateSequence.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM - * Copyright (c) 2025 Munich Quantum Software Company GmbH + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH * All rights reserved. * * SPDX-License-Identifier: MIT diff --git a/mlir/include/mlir/Passes/Decomposition/Helpers.h b/mlir/include/mlir/Passes/Decomposition/Helpers.h index 44c4e5a310..d48a48023c 100644 --- a/mlir/include/mlir/Passes/Decomposition/Helpers.h +++ b/mlir/include/mlir/Passes/Decomposition/Helpers.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM - * Copyright (c) 2025 Munich Quantum Software Company GmbH + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH * All rights reserved. * * SPDX-License-Identifier: MIT diff --git a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index 322112990e..87179aed8e 100644 --- a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM - * Copyright (c) 2025 Munich Quantum Software Company GmbH + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH * All rights reserved. * * SPDX-License-Identifier: MIT diff --git a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index ad37ab1376..5350868015 100644 --- a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM - * Copyright (c) 2025 Munich Quantum Software Company GmbH + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH * All rights reserved. * * SPDX-License-Identifier: MIT diff --git a/mlir/include/mlir/Passes/Passes.h b/mlir/include/mlir/Passes/Passes.h index e60409041a..34e5a770ba 100644 --- a/mlir/include/mlir/Passes/Passes.h +++ b/mlir/include/mlir/Passes/Passes.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM - * Copyright (c) 2025 Munich Quantum Software Company GmbH + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH * All rights reserved. * * SPDX-License-Identifier: MIT diff --git a/mlir/include/mlir/Passes/Passes.td b/mlir/include/mlir/Passes/Passes.td index ff071fb838..6c238bcfa2 100644 --- a/mlir/include/mlir/Passes/Passes.td +++ b/mlir/include/mlir/Passes/Passes.td @@ -1,5 +1,5 @@ -// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM -// Copyright (c) 2025 Munich Quantum Software Company GmbH +// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM +// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH // All rights reserved. // // SPDX-License-Identifier: MIT diff --git a/mlir/lib/Passes/CMakeLists.txt b/mlir/lib/Passes/CMakeLists.txt index 8632a3cca7..cfa3558868 100644 --- a/mlir/lib/Passes/CMakeLists.txt +++ b/mlir/lib/Passes/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM -# Copyright (c) 2025 Munich Quantum Software Company GmbH +# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM +# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH # All rights reserved. # # SPDX-License-Identifier: MIT diff --git a/mlir/lib/Passes/GateDecompositionPass.cpp b/mlir/lib/Passes/GateDecompositionPass.cpp index 88198f4347..85f2a29a1e 100644 --- a/mlir/lib/Passes/GateDecompositionPass.cpp +++ b/mlir/lib/Passes/GateDecompositionPass.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM - * Copyright (c) 2025 Munich Quantum Software Company GmbH + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH * All rights reserved. * * SPDX-License-Identifier: MIT diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 7e6ab2bdd9..5cacc17696 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM - * Copyright (c) 2025 Munich Quantum Software Company GmbH + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH * All rights reserved. * * SPDX-License-Identifier: MIT diff --git a/mlir/unittests/CMakeLists.txt b/mlir/unittests/CMakeLists.txt index b31b2b2b88..46a46bd865 100644 --- a/mlir/unittests/CMakeLists.txt +++ b/mlir/unittests/CMakeLists.txt @@ -13,5 +13,6 @@ add_subdirectory(Dialect) add_custom_target(mqt-core-mlir-unittests) add_dependencies( - mqt-core-mlir-unittests mqt-core-mlir-decomposition-test mqt-core-mlir-compiler-pipeline-test mqt-core-mlir-qco-dialect-test - mqt-core-mlir-dialect-qco-ir-modifiers-test mqt-core-mlir-dialect-utils-test) + mqt-core-mlir-unittests mqt-core-mlir-decomposition-test mqt-core-mlir-compiler-pipeline-test + mqt-core-mlir-qco-dialect-test mqt-core-mlir-dialect-qco-ir-modifiers-test + mqt-core-mlir-dialect-utils-test) diff --git a/mlir/unittests/decomposition/CMakeLists.txt b/mlir/unittests/decomposition/CMakeLists.txt index f845b432a8..ca2cb4cd9d 100644 --- a/mlir/unittests/decomposition/CMakeLists.txt +++ b/mlir/unittests/decomposition/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM -# Copyright (c) 2025 Munich Quantum Software Company GmbH +# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM +# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH # All rights reserved. # # SPDX-License-Identifier: MIT diff --git a/mlir/unittests/decomposition/test_basis_decomposer.cpp b/mlir/unittests/decomposition/test_basis_decomposer.cpp index 7f9221f5af..7d92cee7e4 100644 --- a/mlir/unittests/decomposition/test_basis_decomposer.cpp +++ b/mlir/unittests/decomposition/test_basis_decomposer.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM - * Copyright (c) 2025 Munich Quantum Software Company GmbH + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH * All rights reserved. * * SPDX-License-Identifier: MIT diff --git a/mlir/unittests/decomposition/test_euler_decomposition.cpp b/mlir/unittests/decomposition/test_euler_decomposition.cpp index 442bbb9c3c..ee33bf1d6e 100644 --- a/mlir/unittests/decomposition/test_euler_decomposition.cpp +++ b/mlir/unittests/decomposition/test_euler_decomposition.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM - * Copyright (c) 2025 Munich Quantum Software Company GmbH + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH * All rights reserved. * * SPDX-License-Identifier: MIT diff --git a/mlir/unittests/decomposition/test_weyl_decomposition.cpp b/mlir/unittests/decomposition/test_weyl_decomposition.cpp index 9b205abe2b..2978bca2f8 100644 --- a/mlir/unittests/decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/decomposition/test_weyl_decomposition.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM - * Copyright (c) 2025 Munich Quantum Software Company GmbH + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH * All rights reserved. * * SPDX-License-Identifier: MIT From 1bb1f63c6ea1f77c170430e96e8ace6dfb243f3d Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 22 Jan 2026 19:32:35 +0100 Subject: [PATCH 168/237] fix issue with CtrlOp --- .../Passes/Patterns/GateDecompositionPattern.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 5cacc17696..caa91c8547 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -342,10 +342,12 @@ struct GateDecompositionPattern final bool appendTwoQubitGate(UnitaryOpInterface nextGate) { auto&& firstOperand = nextGate.getInputQubit(0); auto&& secondOperand = nextGate.getInputQubit(1); + assert(firstOperand != secondOperand); auto firstQubitIt = // NOLINT(readability-qualified-auto) llvm::find(outQubits, firstOperand); auto secondQubitIt = // NOLINT(readability-qualified-auto) llvm::find(outQubits, secondOperand); + assert(firstQubitIt != secondQubitIt); if (firstQubitIt == outQubits.end() || secondQubitIt == outQubits.end()) { // another qubit is involved, series is finished (except there only // has been one qubit so far) @@ -356,7 +358,17 @@ struct GateDecompositionPattern final } // TODO: this only works because parameters are at end of operands; // use to-be-implemented getInputQubits() instead - auto&& opInQubits = nextGate->getOperands(); + auto getInputQubits = + [](UnitaryOpInterface op) -> llvm::SmallVector { + if (auto&& ctrlOp = llvm::dyn_cast(*op)) { + auto&& range = + llvm::concat(ctrlOp.getTargetsIn(), + ctrlOp.getControlsIn()); + return {range.begin(), range.end()}; + } + return op->getOperands(); + }; + auto&& opInQubits = getInputQubits(nextGate); // iterator in the operation input of "old" qubit that already has // previous single-qubit gates in this series auto it2 = llvm::find(opInQubits, firstQubitIt != outQubits.end() From 03346140dc2156529024d3149e78486fa206c182 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 24 Jan 2026 18:31:59 +0100 Subject: [PATCH 169/237] fixes and improvements (forceApplication+singleQubitOnly) for QCO dialect --- .../mlir/Passes/Decomposition/EulerBasis.h | 36 +++ .../mlir/Passes/Decomposition/Helpers.h | 8 +- .../Passes/Decomposition/UnitaryMatrices.h | 38 +++- .../Patterns/GateDecompositionPattern.cpp | 208 ++++++++++++------ 4 files changed, 212 insertions(+), 78 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/EulerBasis.h b/mlir/include/mlir/Passes/Decomposition/EulerBasis.h index ab781534f5..63f3aa9c3a 100644 --- a/mlir/include/mlir/Passes/Decomposition/EulerBasis.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerBasis.h @@ -10,7 +10,10 @@ #pragma once +#include "ir/operations/OpType.hpp" + #include +#include namespace mlir::qco::decomposition { /** @@ -37,4 +40,37 @@ enum class EulerBasis : std::uint8_t { ZSXX = 10, ZSX = 11, }; + +[[nodiscard]] inline llvm::SmallVector +getGateTypesForEulerBasis(EulerBasis eulerBasis) { + switch (eulerBasis) { + case EulerBasis::ZYZ: + return {qc::RZ, qc::RY}; + case EulerBasis::ZXZ: + return {qc::RZ, qc::RX}; + case EulerBasis::XZX: + return {qc::RX, qc::RZ}; + case EulerBasis::XYX: + return {qc::RX, qc::RY}; + case EulerBasis::U3: + [[fallthrough]]; + case EulerBasis::U321: + [[fallthrough]]; + case EulerBasis::U: + return {qc::U}; + case EulerBasis::RR: + return {qc::R}; + case EulerBasis::ZSXX: + [[fallthrough]]; + case EulerBasis::ZSX: + return {qc::RZ, qc::X}; + case EulerBasis::PSX: + [[fallthrough]]; + case EulerBasis::U1X: + break; + } + throw std::invalid_argument{ + "Unsupported euler basis for translation to gate types"}; +} + } // namespace mlir::qco::decomposition diff --git a/mlir/include/mlir/Passes/Decomposition/Helpers.h b/mlir/include/mlir/Passes/Decomposition/Helpers.h index d48a48023c..c90a27a109 100644 --- a/mlir/include/mlir/Passes/Decomposition/Helpers.h +++ b/mlir/include/mlir/Passes/Decomposition/Helpers.h @@ -27,7 +27,6 @@ #include #include #include -#include #include // TODO: unstable, NOLINT(misc-include-cleaner) namespace mlir::qco { @@ -148,8 +147,11 @@ getParameters(UnitaryOpInterface op) { [[nodiscard]] inline qc::OpType getQcType(UnitaryOpInterface op) { try { - const std::string type = op->getName().stripDialect().str(); - return qc::opTypeFromString(type); + auto type = op.getBaseSymbol(); + if (type == "ctrl") { + type = llvm::cast(op).getBodyUnitary().getBaseSymbol(); + } + return qc::opTypeFromString(type.str()); } catch (const std::invalid_argument& /*exception*/) { return qc::OpType::None; } diff --git a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index 87179aed8e..2d829f4706 100644 --- a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -94,11 +94,40 @@ inline matrix2x2 pMatrix(const fp lambda) { return matrix2x2{{1, 0}, {0, {std::cos(lambda), std::sin(lambda)}}}; } const matrix2x2 IDENTITY_GATE = matrix2x2::Identity(); +const matrix4x4 SWAP_GATE{ + {1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}; const matrix2x2 H_GATE{{1.0 / SQRT2, 1.0 / SQRT2}, {1.0 / SQRT2, -1.0 / SQRT2}}; const matrix2x2 IPZ{{IM, C_ZERO}, {C_ZERO, M_IM}}; const matrix2x2 IPY{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; const matrix2x2 IPX{{C_ZERO, IM}, {IM, C_ZERO}}; +[[nodiscard]] inline matrix4x4 +expandToTwoQubits(const matrix2x2& singleQubitMatrix, QubitId qubitId) { + if (qubitId == 0) { + return helpers::kroneckerProduct(decomposition::IDENTITY_GATE, + singleQubitMatrix); + } + if (qubitId == 1) { + return helpers::kroneckerProduct(singleQubitMatrix, + decomposition::IDENTITY_GATE); + } + throw std::invalid_argument{"Invalid qubit id for single-qubit expansion"}; +} + +[[nodiscard]] inline matrix4x4 +fixTwoQubitMatrixQubitOrder(const matrix4x4& twoQubitMatrix, + const llvm::SmallVector& qubitIds) { + if (qubitIds == llvm::SmallVector{1, 0}) { + // since UnitaryOpInterface::getUnitaryMatrix() does have a static + // qubit order, adjust if we need the other direction of the gate + return decomposition::SWAP_GATE * twoQubitMatrix * decomposition::SWAP_GATE; + } + if (qubitIds == llvm::SmallVector{0, 1}) { + return twoQubitMatrix; + } + throw std::invalid_argument{"Invalid qubit IDs for fixing two-qubit matrix"}; +} + inline matrix2x2 getSingleQubitMatrix(const Gate& gate) { if (gate.type == qc::SX) { return matrix2x2{{qfp{0.5, 0.5}, qfp{0.5, -0.5}}, @@ -136,6 +165,7 @@ inline matrix2x2 getSingleQubitMatrix(const Gate& gate) { qc::toString(gate.type) + ")"}; } +// TODO: remove? only used for verification of circuit inline matrix4x4 getTwoQubitMatrix(const Gate& gate) { using helpers::kroneckerProduct; @@ -143,13 +173,7 @@ inline matrix4x4 getTwoQubitMatrix(const Gate& gate) { return kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); } if (gate.qubitId.size() == 1) { - if (gate.qubitId[0] == 0) { - return kroneckerProduct(IDENTITY_GATE, getSingleQubitMatrix(gate)); - } - if (gate.qubitId[0] == 1) { - return kroneckerProduct(getSingleQubitMatrix(gate), IDENTITY_GATE); - } - throw std::logic_error{"Invalid qubit ID in getTwoQubitMatrix"}; + return expandToTwoQubits(getSingleQubitMatrix(gate), gate.qubitId[0]); } if (gate.qubitId.size() == 2) { if (gate.type == qc::X) { diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index caa91c8547..26007985fc 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -56,14 +56,32 @@ struct GateDecompositionPattern final * Initialize pattern with a set of basis gates and euler bases. * The best combination of (basis gate, euler basis) will be evaluated for * each decomposition. + * + * @param context MLIR context in which the pattern is applied + * @param basisGate Set of two-qubit gates that should be used in the + * decomposition. All two-qubit interactions will be + * represented by one of the gates in this set + * @param eulerBasis Set of euler bases that should be used for the + * decomposition of local single-qubit modifications. For + * each necessary single-qubit operation, the optimal basis + * will be choosen from this set + * @param singleQubitOnly If true, only perform single-qubit decompositions + * and no two-qubit decompositions + * @param forceApplication If true, always apply best decomposition, even if + * it is longer/more complex than the previous + * circuit. To prevent recursion, this will not apply + * a decomposition if the (sub)circuit only contains + * gates available as basis gates or euler bases */ explicit GateDecompositionPattern(mlir::MLIRContext* context, llvm::SmallVector basisGate, - llvm::SmallVector eulerBasis) + llvm::SmallVector eulerBasis, + bool singleQubitOnly, bool forceApplication) : OpInterfaceRewritePattern(context), - decomposerBasisGate{std::move(basisGate)}, - decomposerEulerBases{std::move(eulerBasis)} { - for (auto&& basisGate : decomposerBasisGate) { + decomposerBasisGates{std::move(basisGate)}, + decomposerEulerBases{std::move(eulerBasis)}, + singleQubitOnly{singleQubitOnly}, forceApplication{forceApplication} { + for (auto&& basisGate : decomposerBasisGates) { basisDecomposers.push_back(decomposition::TwoQubitBasisDecomposer::create( basisGate, DEFAULT_FIDELITY)); } @@ -72,9 +90,20 @@ struct GateDecompositionPattern final mlir::LogicalResult matchAndRewrite(UnitaryOpInterface op, mlir::PatternRewriter& rewriter) const override { - auto series = TwoQubitSeries::getTwoQubitSeries(op); + auto collectSeries = [](UnitaryOpInterface op, bool singleQubitOnly) { + if (singleQubitOnly) { + return TwoQubitSeries::getSingleQubitSeries(op); + } + return TwoQubitSeries::getTwoQubitSeries(op); + }; + auto series = collectSeries(op, singleQubitOnly); + + auto&& [singleQubitGates, twoQubitGates] = getDecompositionGates(); + auto containsForeignGates = + !series.containsOnlyGates(singleQubitGates, twoQubitGates); - if (series.gates.size() < 3) { + if (series.gates.empty() || (series.gates.size() < 3 && + (!forceApplication || containsForeignGates))) { // too short return mlir::failure(); } @@ -113,8 +142,11 @@ struct GateDecompositionPattern final targetDecomposition, decomposerEulerBases, DEFAULT_FIDELITY, false, std::nullopt); if (sequence) { + // decomposition successful if (!bestSequence || sequence->complexity() < bestSequence->complexity()) { + // this decomposition is better than any successful decomposition + // before bestSequence = sequence; } } @@ -136,7 +168,8 @@ struct GateDecompositionPattern final llvm::errs() << "\n"; // only accept new sequence if it shortens existing series by more than two // gates; this prevents an oscillation with phase gates - if (bestSequence->complexity() + 2 >= series.complexity) { + if (bestSequence->complexity() + 2 >= series.complexity && + (!forceApplication || !containsForeignGates)) { return mlir::failure(); } @@ -164,12 +197,10 @@ struct GateDecompositionPattern final * Qubits that are the input for the series. * First qubit will always be set, second qubit may be equal to * mlir::Value{} if the series consists of only single-qubit gates. - * - * All */ std::array inQubits{}; /** - * Qubits that are the input for the series. + * Qubits that are the output for the series. * First qubit will always be set, second qubit may be equal to * mlir::Value{} if the series consists of only single-qubit gates. */ @@ -182,38 +213,29 @@ struct GateDecompositionPattern final llvm::SmallVector gates; [[nodiscard]] static TwoQubitSeries - getTwoQubitSeries(UnitaryOpInterface op) { - if (isBarrier(op)) { + getSingleQubitSeries(UnitaryOpInterface op) { + if (isBarrier(op) || !op.isSingleQubit()) { return {}; } TwoQubitSeries result(op); - auto getUser = [](mlir::Value qubit, - auto&& filter) -> std::optional { - if (qubit) { - auto users = qubit.getUsers(); - auto userIt = users.begin(); - // qubit may have more than one use if it is in a ctrl block (one use - // for gate, one use for ctrl); we want to use the ctrl operation - // since it is relevant for the total unitary matrix of the circuit - assert(qubit.hasOneUse() || qubit.hasNUses(2)); - if (!qubit.hasOneUse()) { - // TODO: use wire iterator for proper handling - while (!mlir::dyn_cast(*userIt)) { - ++userIt; - if (userIt == users.end()) { - // TODO: should not happen? - return std::nullopt; - } - } - } - auto user = mlir::dyn_cast(*userIt); - if (user && filter(user)) { - return user; - } + while (auto user = getUser(result.outQubits[0], + &helpers::isSingleQubitOperation)) { + if (!result.appendSingleQubitGate(*user)) { + break; } - return std::nullopt; - }; + } + + assert(result.isSingleQubitSeries()); + return result; + } + + [[nodiscard]] static TwoQubitSeries + getTwoQubitSeries(UnitaryOpInterface op) { + if (isBarrier(op)) { + return {}; + } + TwoQubitSeries result(op); bool foundGate = true; while (foundGate) { @@ -262,16 +284,12 @@ struct GateDecompositionPattern final if (!matrix) { return std::nullopt; } - if (gate.qubitIds[0] == 0) { - gateMatrix = helpers::kroneckerProduct(decomposition::IDENTITY_GATE, - *matrix); - } else if (gate.qubitIds[0] == 1) { - gateMatrix = helpers::kroneckerProduct( - *matrix, decomposition::IDENTITY_GATE); - } + gateMatrix = + decomposition::expandToTwoQubits(*matrix, gate.qubitIds[0]); } else if (gate.op.isTwoQubit()) { if (auto matrix = gate.op.getUnitaryMatrix()) { - gateMatrix = std::move(*matrix); + gateMatrix = decomposition::fixTwoQubitMatrixQubitOrder( + *matrix, gate.qubitIds); } else { return std::nullopt; } @@ -288,6 +306,17 @@ struct GateDecompositionPattern final llvm::is_contained(outQubits, mlir::Value{}); } + [[nodiscard]] bool + containsOnlyGates(const llvm::SetVector& singleQubitGates, + const llvm::SetVector& twoQubitGates) { + return llvm::all_of(gates, [&](auto&& gate) { + auto&& gateType = helpers::getQcType(gate.op); + return (gate.qubitIds.size() == 1 && + singleQubitGates.contains(gateType)) || + (gate.qubitIds.size() == 2 && twoQubitGates.contains(gateType)); + }); + } + private: /** * Initialize empty TwoQubitSeries instance. @@ -340,6 +369,13 @@ struct GateDecompositionPattern final * @return true if series continues, otherwise false */ bool appendTwoQubitGate(UnitaryOpInterface nextGate) { + if (isBarrier(nextGate)) { + // a barrier operation should not be crossed for a decomposition; + // ignore possitility to backtrack (if this is the first two-qubit gate) + // since two single-qubit decompositions are less expensive than one + // two-qubit decomposition + return false; + } auto&& firstOperand = nextGate.getInputQubit(0); auto&& secondOperand = nextGate.getInputQubit(1); assert(firstOperand != secondOperand); @@ -361,9 +397,8 @@ struct GateDecompositionPattern final auto getInputQubits = [](UnitaryOpInterface op) -> llvm::SmallVector { if (auto&& ctrlOp = llvm::dyn_cast(*op)) { - auto&& range = - llvm::concat(ctrlOp.getTargetsIn(), - ctrlOp.getControlsIn()); + auto&& range = llvm::concat(ctrlOp.getTargetsIn(), + ctrlOp.getControlsIn()); return {range.begin(), range.end()}; } return op->getOperands(); @@ -391,10 +426,6 @@ struct GateDecompositionPattern final // possible to collect other single-qubit operations backtrackSingleQubitSeries(newInQubitId); } - if (isBarrier(nextGate)) { - // a barrier operation should not be crossed for a decomposition - return false; - } const QubitId firstQubitId = std::distance(outQubits.begin(), firstQubitIt); const QubitId secondQubitId = @@ -436,6 +467,24 @@ struct GateDecompositionPattern final [[nodiscard]] static bool isBarrier(UnitaryOpInterface op) { return llvm::isa_and_nonnull(*op); } + + /** + * + */ + template + static std::optional getUser(mlir::Value qubit, + Func&& filter) { + if (qubit) { + auto users = qubit.getUsers(); + auto userIt = users.begin(); + assert(qubit.hasOneUse()); + auto user = mlir::dyn_cast(*userIt); + if (user && std::invoke(std::forward(filter), user)) { + return user; + } + } + return std::nullopt; + }; }; template @@ -494,33 +543,43 @@ struct GateDecompositionPattern final matrix4x4 unitaryMatrix = helpers::kroneckerProduct( decomposition::IDENTITY_GATE, decomposition::IDENTITY_GATE); for (auto&& gate : sequence.gates) { - auto gateMatrix = decomposition::getTwoQubitMatrix(gate); - unitaryMatrix = gateMatrix * unitaryMatrix; - // TODO: need to add each basis gate we want to use - if (gate.type == qc::X) { - mlir::SmallVector inCtrlQubits; - if (gate.qubitId.size() > 1) { - // controls come last - inCtrlQubits.push_back(inQubits[gate.qubitId[1]]); - } - auto newGate = createControlledGate( - rewriter, location, inCtrlQubits, inQubits[gate.qubitId[0]]); + if (gate.type == qc::X && gate.qubitId.size() > 1) { + // X gate involving more than one qubit is a CX gate: + // qubit position 0 is target, 1 is control + auto newGate = createControlledGate(rewriter, location, + {inQubits[gate.qubitId[1]]}, + inQubits[gate.qubitId[0]]); + unitaryMatrix = decomposition::fixTwoQubitMatrixQubitOrder( + newGate.getUnitaryMatrix().value(), gate.qubitId) * + unitaryMatrix; updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RX) { assert(gate.qubitId.size() == 1); auto newGate = createGate( rewriter, location, inQubits[gate.qubitId[0]], gate.parameter[0]); + unitaryMatrix = + decomposition::expandToTwoQubits(newGate.getUnitaryMatrix().value(), + gate.qubitId[0]) * + unitaryMatrix; updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RY) { assert(gate.qubitId.size() == 1); auto newGate = createGate( rewriter, location, inQubits[gate.qubitId[0]], gate.parameter[0]); + unitaryMatrix = + decomposition::expandToTwoQubits(newGate.getUnitaryMatrix().value(), + gate.qubitId[0]) * + unitaryMatrix; updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RZ) { assert(gate.qubitId.size() == 1); auto newGate = createGate( rewriter, location, inQubits[gate.qubitId[0]], gate.parameter[0]); + unitaryMatrix = + decomposition::expandToTwoQubits(newGate.getUnitaryMatrix().value(), + gate.qubitId[0]) * + unitaryMatrix; updateInQubits(gate.qubitId, newGate); } else { throw std::runtime_error{"Unknown gate type!"}; @@ -536,17 +595,30 @@ struct GateDecompositionPattern final rewriter.replaceAllUsesWith(series.outQubits, inQubits); } for (auto&& gate : llvm::reverse(series.gates)) { - if (auto ctrlOp = llvm::dyn_cast(*gate.op)) { - rewriter.eraseOp(ctrlOp.getBodyUnitary()); - } rewriter.eraseOp(gate.op); } } + [[nodiscard]] std::array, 2> + getDecompositionGates() const { + llvm::SetVector eulerBasesGates; + llvm::SetVector basisGates; + for (auto&& eulerBasis : decomposerEulerBases) { + eulerBasesGates.insert_range( + decomposition::getGateTypesForEulerBasis(eulerBasis)); + } + for (auto&& basisGate : decomposerBasisGates) { + basisGates.insert(basisGate.type); + } + return {eulerBasesGates, basisGates}; + } + private: - llvm::SmallVector decomposerBasisGate; + llvm::SmallVector decomposerBasisGates; llvm::SmallVector basisDecomposers; llvm::SmallVector decomposerEulerBases; + bool singleQubitOnly; + bool forceApplication; }; /** @@ -562,7 +634,7 @@ void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns) { eulerBases.push_back(GateDecompositionPattern::EulerBasis::XYX); eulerBases.push_back(GateDecompositionPattern::EulerBasis::ZXZ); patterns.add(patterns.getContext(), basisGates, - eulerBases); + eulerBases, false, false); } } // namespace mlir::qco From 9fde078e4afbb24e1992a28a0551b51cf20d12b3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 24 Jan 2026 17:35:08 +0000 Subject: [PATCH 170/237] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 26007985fc..3d5fbbf5bd 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -64,7 +64,7 @@ struct GateDecompositionPattern final * @param eulerBasis Set of euler bases that should be used for the * decomposition of local single-qubit modifications. For * each necessary single-qubit operation, the optimal basis - * will be choosen from this set + * will be chosen from this set * @param singleQubitOnly If true, only perform single-qubit decompositions * and no two-qubit decompositions * @param forceApplication If true, always apply best decomposition, even if From 7b93aec73b693eaad5e7d89b9269abc35f48d860 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 27 Jan 2026 20:26:57 +0100 Subject: [PATCH 171/237] fix nested ops issue and others in pass/pattern --- mlir/lib/Passes/GateDecompositionPass.cpp | 4 ++++ .../Patterns/GateDecompositionPattern.cpp | 20 ++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/mlir/lib/Passes/GateDecompositionPass.cpp b/mlir/lib/Passes/GateDecompositionPass.cpp index 85f2a29a1e..ee90801549 100644 --- a/mlir/lib/Passes/GateDecompositionPass.cpp +++ b/mlir/lib/Passes/GateDecompositionPass.cpp @@ -39,7 +39,11 @@ struct GateDecompositionPass final // Configure greedy driver mlir::GreedyRewriteConfig config; + // start at top of program to maximize collected sub-circuits config.setUseTopDownTraversal(true); + // only optimize existing operations to avoid unnecessary sub-circuit + // collections of already decomposed gates + config.setStrictness(GreedyRewriteStrictness::ExistingOps); // Apply patterns in an iterative and greedy manner. if (mlir::failed( diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 3d5fbbf5bd..baaba63134 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -90,6 +90,16 @@ struct GateDecompositionPattern final mlir::LogicalResult matchAndRewrite(UnitaryOpInterface op, mlir::PatternRewriter& rewriter) const override { + if (op->getParentOfType()) { + // application of pattern might not work on gates inside a control + // modifier because rotation gates need to create new constants which are + // not allowed inside a control body; also, the foreign gate dection does + // not work and e.g. a CNOT will not be recognized as such and thus will + // be further decomposed into a RX gate inside the control body which is + // most likely undesired + return mlir::failure(); + } + auto collectSeries = [](UnitaryOpInterface op, bool singleQubitOnly) { if (singleQubitOnly) { return TwoQubitSeries::getSingleQubitSeries(op); @@ -103,8 +113,8 @@ struct GateDecompositionPattern final !series.containsOnlyGates(singleQubitGates, twoQubitGates); if (series.gates.empty() || (series.gates.size() < 3 && - (!forceApplication || containsForeignGates))) { - // too short + !(forceApplication && containsForeignGates))) { + // empty or too short and only contains valid gates anyway return mlir::failure(); } @@ -169,7 +179,7 @@ struct GateDecompositionPattern final // only accept new sequence if it shortens existing series by more than two // gates; this prevents an oscillation with phase gates if (bestSequence->complexity() + 2 >= series.complexity && - (!forceApplication || !containsForeignGates)) { + !(forceApplication && containsForeignGates)) { return mlir::failure(); } @@ -245,6 +255,10 @@ struct GateDecompositionPattern final while (auto user = getUser(result.outQubits[i], &helpers::isSingleQubitOperation)) { foundGate = result.appendSingleQubitGate(*user); + if (!foundGate) { + // result.outQubits was not updated, prevent endless loop + break; + } } } From 7348caf724bdee5dafd1ff24e56d9169808282f2 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 30 Jan 2026 12:41:41 +0100 Subject: [PATCH 172/237] set GPhase complexity to 0 and fix complexity calculation for backtracking --- mlir/include/mlir/Passes/Decomposition/Helpers.h | 2 +- mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mlir/include/mlir/Passes/Decomposition/Helpers.h b/mlir/include/mlir/Passes/Decomposition/Helpers.h index c90a27a109..eecea033da 100644 --- a/mlir/include/mlir/Passes/Decomposition/Helpers.h +++ b/mlir/include/mlir/Passes/Decomposition/Helpers.h @@ -217,7 +217,7 @@ template return (numOfQubits - 1) * multiQubitFactor; } if (type == qc::GPhase) { - return 2; + return 0; } return 1; } diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index baaba63134..26dd3ebe4b 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -464,6 +464,7 @@ struct GateDecompositionPattern final auto prependSingleQubitGate = [&](UnitaryOpInterface op) { inQubits[qubitId] = op.getInputQubit(0); gates.insert(gates.begin(), {.op = op, .qubitIds = {qubitId}}); + complexity += helpers::getComplexity(helpers::getQcType(op), 1); // outQubits do not need to be updated because the final out qubit is // already fixed }; From c46f15970399ac5932089cebe4a1a77d2476ba57 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 31 Jan 2026 22:43:04 +0100 Subject: [PATCH 173/237] thesis evaluation: add statistics and disable canonicalizations --- mlir/include/mlir/Passes/Passes.h | 15 ++- mlir/lib/Compiler/CompilerPipeline.cpp | 88 ++++++------- mlir/lib/Passes/GateDecompositionPass.cpp | 83 +++++++++++- .../Patterns/GateDecompositionPattern.cpp | 123 +++++++++++++++--- 4 files changed, 248 insertions(+), 61 deletions(-) diff --git a/mlir/include/mlir/Passes/Passes.h b/mlir/include/mlir/Passes/Passes.h index 34e5a770ba..ec21ae3523 100644 --- a/mlir/include/mlir/Passes/Passes.h +++ b/mlir/include/mlir/Passes/Passes.h @@ -27,7 +27,20 @@ namespace mlir::qco { #define GEN_PASS_DECL #include "mlir/Passes/Passes.h.inc" // IWYU pragma: export -void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns); +void populateGateDecompositionPatterns( + mlir::RewritePatternSet& patterns, + llvm::Statistic& twoQubitCreationTime, + llvm::Statistic& numberOfTwoQubitCreations, + llvm::Statistic& successfulSingleQubitDecompositions, + llvm::Statistic& totalSingleQubitDecompositions, + llvm::Statistic& successfulTwoQubitDecompositions, + llvm::Statistic& totalTwoQubitDecompositions, + llvm::Statistic& totalCircuitCollections, + llvm::Statistic& totalTouchedGates, + llvm::Statistic& subCircuitComplexityChange, + llvm::Statistic& timeInCircuitCollection, + llvm::Statistic& timeInSingleQubitDecomposition, + llvm::Statistic& timeInTwoQubitDecomposition); //===----------------------------------------------------------------------===// // Registration diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index 4d0edddac5..ae08267253 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -124,17 +124,17 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, } // Stage 2: QC canonicalization - addCleanupPasses(pm); - if (pm.run(module).failed()) { - return failure(); - } - if (record != nullptr && config_.recordIntermediates) { - record->afterInitialCanon = captureIR(module); - if (config_.printIRAfterAllStages) { - prettyPrintStage(module, "Initial QC Canonicalization", ++currentStage, - totalStages); - } - } + // addCleanupPasses(pm); + // if (pm.run(module).failed()) { + // return failure(); + // } + // if (record != nullptr && config_.recordIntermediates) { + // record->afterInitialCanon = captureIR(module); + // if (config_.printIRAfterAllStages) { + // prettyPrintStage(module, "Initial QC Canonicalization", ++currentStage, + // totalStages); + // } + // } pm.clear(); // Stage 3: QC-to-QCO conversion @@ -152,17 +152,17 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, pm.clear(); // Stage 4: QCO canonicalization - addCleanupPasses(pm); - if (failed(pm.run(module))) { - return failure(); - } - if (record != nullptr && config_.recordIntermediates) { - record->afterQCOCanon = captureIR(module); - if (config_.printIRAfterAllStages) { - prettyPrintStage(module, "Initial QCO Canonicalization", ++currentStage, - totalStages); - } - } + // addCleanupPasses(pm); + // if (failed(pm.run(module))) { + // return failure(); + // } + // if (record != nullptr && config_.recordIntermediates) { + // record->afterQCOCanon = captureIR(module); + // if (config_.printIRAfterAllStages) { + // prettyPrintStage(module, "Initial QCO Canonicalization", ++currentStage, + // totalStages); + // } + // } pm.clear(); // Stage 5: Optimization passes @@ -181,17 +181,17 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, pm.clear(); // Stage 6: QCO canonicalization - addCleanupPasses(pm); - if (failed(pm.run(module))) { - return failure(); - } - if (record != nullptr && config_.recordIntermediates) { - record->afterOptimizationCanon = captureIR(module); - if (config_.printIRAfterAllStages) { - prettyPrintStage(module, "Final QCO Canonicalization", ++currentStage, - totalStages); - } - } + // addCleanupPasses(pm); + // if (failed(pm.run(module))) { + // return failure(); + // } + // if (record != nullptr && config_.recordIntermediates) { + // record->afterOptimizationCanon = captureIR(module); + // if (config_.printIRAfterAllStages) { + // prettyPrintStage(module, "Final QCO Canonicalization", ++currentStage, + // totalStages); + // } + // } pm.clear(); // Stage 7: QCO-to-QC conversion @@ -209,17 +209,17 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, pm.clear(); // Stage 8: QC canonicalization - addCleanupPasses(pm); - if (failed(pm.run(module))) { - return failure(); - } - if (record != nullptr && config_.recordIntermediates) { - record->afterQCCanon = captureIR(module); - if (config_.printIRAfterAllStages) { - prettyPrintStage(module, "Final QC Canonicalization", ++currentStage, - totalStages); - } - } + // addCleanupPasses(pm); + // if (failed(pm.run(module))) { + // return failure(); + // } + // if (record != nullptr && config_.recordIntermediates) { + // record->afterQCCanon = captureIR(module); + // if (config_.printIRAfterAllStages) { + // prettyPrintStage(module, "Final QC Canonicalization", ++currentStage, + // totalStages); + // } + // } pm.clear(); // Stage 9: QC-to-QIR conversion (optional) diff --git a/mlir/lib/Passes/GateDecompositionPass.cpp b/mlir/lib/Passes/GateDecompositionPass.cpp index ee90801549..5ac5ded2d4 100644 --- a/mlir/lib/Passes/GateDecompositionPass.cpp +++ b/mlir/lib/Passes/GateDecompositionPass.cpp @@ -28,6 +28,46 @@ namespace mlir::qco { struct GateDecompositionPass final : impl::GateDecompositionPassBase { + GateDecompositionPass() = default; + GateDecompositionPass(const GateDecompositionPass& other) + : impl::GateDecompositionPassBase{other}, + twoQubitCreationTime{this, "twoQubitCreationTime", + "Creation time of basis decomposers"}, + numberOfTwoQubitCreations{ + this, "numberOfTwoQubitCreations", + "Number of times basis decomposers are created"}, + successfulSingleQubitDecompositions{ + this, "successfulSingleQubitDecompositions", + "Number of times a single-qubit decomposition was applied"}, + totalSingleQubitDecompositions{this, "totalSingleQubitDecompositions", + "Number of times (only) a single-qubit " + "decomposition was calculated"}, + successfulTwoQubitDecompositions{ + this, "successfulTwoQubitDecompositions", + "Number of times a two-qubit decomposition was applied"}, + totalTwoQubitDecompositions{ + this, "totalTwoQubitDecompositions", + "Number of times a two-qubit decomposition was calculated"}, + totalCircuitCollections{this, "totalCircuitCollections", + "Number of times a sub-circuit was collected"}, + totalTouchedGates{ + this, "totalTouchedGates", + "Number of gates that were looked at (in sub-circuit collection)"}, + subCircuitComplexityChange{ + this, "subCircuitComplexityChange", + "Increase or decrease of complexity in sub-circuit"}, + timeInCircuitCollection{this, "timeInCircuitCollection", + "Time spent in circuit collection (µs)"}, + timeInSingleQubitDecomposition{ + this, "timeInSingleQubitDecomposition", + "Time spent in single-qubit decomposition (µs)"}, + timeInTwoQubitDecomposition{ + this, "timeInTwoQubitDecomposition", + "Time spent in single-qubit decomposition (µs)"} {} + GateDecompositionPass(GateDecompositionPass&& other) = delete; + GateDecompositionPass& operator=(const GateDecompositionPass& other) = delete; + GateDecompositionPass& operator=(GateDecompositionPass&& other) = delete; + void runOnOperation() override { // Get the current operation being operated on. auto op = getOperation(); @@ -35,7 +75,13 @@ struct GateDecompositionPass final // Define the set of patterns to use. mlir::RewritePatternSet patterns(ctx); - populateGateDecompositionPatterns(patterns); + populateGateDecompositionPatterns( + patterns, twoQubitCreationTime, numberOfTwoQubitCreations, + successfulSingleQubitDecompositions, totalSingleQubitDecompositions, + successfulTwoQubitDecompositions, totalTwoQubitDecompositions, + totalCircuitCollections, totalTouchedGates, subCircuitComplexityChange, + timeInCircuitCollection, timeInSingleQubitDecomposition, + timeInTwoQubitDecomposition); // Configure greedy driver mlir::GreedyRewriteConfig config; @@ -51,6 +97,41 @@ struct GateDecompositionPass final signalPassFailure(); } } + + Statistic twoQubitCreationTime{this, "twoQubitCreationTime", + "Creation time of basis decomposers"}; + Statistic numberOfTwoQubitCreations{ + this, "numberOfTwoQubitCreations", + "Number of times basis decomposers are created"}; + Statistic successfulSingleQubitDecompositions{ + this, "successfulSingleQubitDecompositions", + "Number of times a single-qubit decomposition was applied"}; + Statistic totalSingleQubitDecompositions{ + this, "totalSingleQubitDecompositions", + "Number of times (only) a single-qubit decomposition was calculated"}; + Statistic successfulTwoQubitDecompositions{ + this, "successfulTwoQubitDecompositions", + "Number of times a two-qubit decomposition was applied"}; + Statistic totalTwoQubitDecompositions{ + this, "totalTwoQubitDecompositions", + "Number of times a two-qubit decomposition was calculated"}; + Statistic totalCircuitCollections{ + this, "totalCircuitCollections", + "Number of times a sub-circuit was collected"}; + Statistic totalTouchedGates{ + this, "totalTouchedGates", + "Number of gates that were looked at (in sub-circuit collection)"}; + Statistic subCircuitComplexityChange{ + this, "subCircuitComplexityChange", + "Increase or decrease of complexity in sub-circuit"}; + Statistic timeInCircuitCollection{this, "timeInCircuitCollection", + "Time spent in circuit collection (µs)"}; + Statistic timeInSingleQubitDecomposition{ + this, "timeInSingleQubitDecomposition", + "Time spent in single-qubit decomposition (µs)"}; + Statistic timeInTwoQubitDecomposition{ + this, "timeInTwoQubitDecomposition", + "Time spent in single-qubit decomposition (µs)"}; }; } // namespace mlir::qco diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 26dd3ebe4b..35da3c26b1 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -73,18 +73,49 @@ struct GateDecompositionPattern final * a decomposition if the (sub)circuit only contains * gates available as basis gates or euler bases */ - explicit GateDecompositionPattern(mlir::MLIRContext* context, - llvm::SmallVector basisGate, - llvm::SmallVector eulerBasis, - bool singleQubitOnly, bool forceApplication) + explicit GateDecompositionPattern( + mlir::MLIRContext* context, llvm::SmallVector basisGate, + llvm::SmallVector eulerBasis, bool singleQubitOnly, + bool forceApplication, llvm::Statistic& twoQubitCreationTime, + llvm::Statistic& numberOfTwoQubitCreations, + llvm::Statistic& successfulSingleQubitDecompositions, + llvm::Statistic& totalSingleQubitDecompositions, + llvm::Statistic& successfulTwoQubitDecompositions, + llvm::Statistic& totalTwoQubitDecompositions, + llvm::Statistic& totalCircuitCollections, + llvm::Statistic& totalTouchedGates, + llvm::Statistic& subCircuitComplexityChange, + llvm::Statistic& timeInCircuitCollection, + llvm::Statistic& timeInSingleQubitDecomposition, + llvm::Statistic& timeInTwoQubitDecomposition) : OpInterfaceRewritePattern(context), decomposerBasisGates{std::move(basisGate)}, decomposerEulerBases{std::move(eulerBasis)}, - singleQubitOnly{singleQubitOnly}, forceApplication{forceApplication} { + singleQubitOnly{singleQubitOnly}, forceApplication{forceApplication}, + twoQubitCreationTime{twoQubitCreationTime}, + numberOfTwoQubitCreations{numberOfTwoQubitCreations}, + successfulSingleQubitDecompositions{ + successfulSingleQubitDecompositions}, + totalSingleQubitDecompositions{totalSingleQubitDecompositions}, + successfulTwoQubitDecompositions{successfulTwoQubitDecompositions}, + totalTwoQubitDecompositions{totalTwoQubitDecompositions}, + totalCircuitCollections{totalCircuitCollections}, + totalTouchedGates{totalTouchedGates}, + subCircuitComplexityChange{subCircuitComplexityChange}, + timeInCircuitCollection{timeInCircuitCollection}, + timeInSingleQubitDecomposition{timeInSingleQubitDecomposition}, + timeInTwoQubitDecomposition{timeInTwoQubitDecomposition} { + ++numberOfTwoQubitCreations; + auto startTime = std::chrono::steady_clock::now(); for (auto&& basisGate : decomposerBasisGates) { basisDecomposers.push_back(decomposition::TwoQubitBasisDecomposer::create( basisGate, DEFAULT_FIDELITY)); } + auto endTime = std::chrono::steady_clock::now(); + twoQubitCreationTime += + std::chrono::duration_cast(endTime - + startTime) + .count(); } mlir::LogicalResult @@ -100,13 +131,23 @@ struct GateDecompositionPattern final return mlir::failure(); } - auto collectSeries = [](UnitaryOpInterface op, bool singleQubitOnly) { + auto collectSeries = [this](UnitaryOpInterface op, bool singleQubitOnly) { + ++totalCircuitCollections; if (singleQubitOnly) { return TwoQubitSeries::getSingleQubitSeries(op); } return TwoQubitSeries::getTwoQubitSeries(op); }; + auto startTime = std::chrono::steady_clock::now(); auto series = collectSeries(op, singleQubitOnly); + auto endTime = std::chrono::steady_clock::now(); + timeInCircuitCollection += + std::chrono::duration_cast(endTime - + startTime) + .count(); + // not really accurate since it neglects the "past the series" gates that + // terminated the series + totalTouchedGates += series.gates.size(); auto&& [singleQubitGates, twoQubitGates] = getDecompositionGates(); auto containsForeignGates = @@ -128,6 +169,10 @@ struct GateDecompositionPattern final // cannot process decomposition without the matrix of the series return mlir::failure(); } + // only count the multiple decompositions as "one" since the number of + // euler bases is constant + ++totalSingleQubitDecompositions; + startTime = std::chrono::steady_clock::now(); for (auto&& eulerBasis : decomposerEulerBases) { auto sequence = decomposition::EulerDecomposition::generateCircuit( eulerBasis, *unitaryMatrix, true, std::nullopt); @@ -136,6 +181,11 @@ struct GateDecompositionPattern final bestSequence = sequence; } } + endTime = std::chrono::steady_clock::now(); + timeInSingleQubitDecomposition += + std::chrono::duration_cast(endTime - + startTime) + .count(); } else { // two-qubit series; perform two-qubit basis decomposition const auto unitaryMatrix = series.getUnitaryMatrix(); @@ -147,9 +197,13 @@ struct GateDecompositionPattern final decomposition::TwoQubitWeylDecomposition::create(*unitaryMatrix, DEFAULT_FIDELITY); + // only count the multiple decompositions as "one" since the number of + // euler bases is constant + ++totalTwoQubitDecompositions; + startTime = std::chrono::steady_clock::now(); for (const auto& decomposer : basisDecomposers) { auto sequence = decomposer.twoQubitDecompose( - targetDecomposition, decomposerEulerBases, DEFAULT_FIDELITY, false, + targetDecomposition, decomposerEulerBases, DEFAULT_FIDELITY, true, std::nullopt); if (sequence) { // decomposition successful @@ -161,11 +215,11 @@ struct GateDecompositionPattern final } } } - } - - llvm::errs() << "Found series (" << series.complexity << "): "; - for (auto&& gate : series.gates) { - llvm::errs() << gate.op->getName().stripDialect().str() << ", "; + endTime = std::chrono::steady_clock::now(); + timeInTwoQubitDecomposition += + std::chrono::duration_cast(endTime - + startTime) + .count(); } if (!bestSequence) { @@ -183,6 +237,14 @@ struct GateDecompositionPattern final return mlir::failure(); } + if (series.isSingleQubitSeries()) { + ++successfulSingleQubitDecompositions; + } else { + ++successfulTwoQubitDecompositions; + } + subCircuitComplexityChange += + series.complexity - bestSequence->complexity(); + applySeries(rewriter, series, *bestSequence); return mlir::success(); @@ -634,13 +696,38 @@ struct GateDecompositionPattern final llvm::SmallVector decomposerEulerBases; bool singleQubitOnly; bool forceApplication; + + llvm::Statistic& twoQubitCreationTime; + llvm::Statistic& numberOfTwoQubitCreations; + llvm::Statistic& successfulSingleQubitDecompositions; + llvm::Statistic& totalSingleQubitDecompositions; + llvm::Statistic& successfulTwoQubitDecompositions; + llvm::Statistic& totalTwoQubitDecompositions; + llvm::Statistic& totalCircuitCollections; + llvm::Statistic& totalTouchedGates; + llvm::Statistic& subCircuitComplexityChange; + llvm::Statistic& timeInCircuitCollection; + llvm::Statistic& timeInSingleQubitDecomposition; + llvm::Statistic& timeInTwoQubitDecomposition; }; /** * @brief Populates the given pattern set with patterns for gate * decomposition. */ -void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns) { +void populateGateDecompositionPatterns( + mlir::RewritePatternSet& patterns, llvm::Statistic& twoQubitCreationTime, + llvm::Statistic& numberOfTwoQubitCreations, + llvm::Statistic& successfulSingleQubitDecompositions, + llvm::Statistic& totalSingleQubitDecompositions, + llvm::Statistic& successfulTwoQubitDecompositions, + llvm::Statistic& totalTwoQubitDecompositions, + llvm::Statistic& totalCircuitCollections, + llvm::Statistic& totalTouchedGates, + llvm::Statistic& subCircuitComplexityChange, + llvm::Statistic& timeInCircuitCollection, + llvm::Statistic& timeInSingleQubitDecomposition, + llvm::Statistic& timeInTwoQubitDecomposition) { llvm::SmallVector basisGates; llvm::SmallVector eulerBases; basisGates.push_back({.type = qc::X, .parameter = {}, .qubitId = {0, 1}}); @@ -648,8 +735,14 @@ void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns) { eulerBases.push_back(GateDecompositionPattern::EulerBasis::ZYZ); eulerBases.push_back(GateDecompositionPattern::EulerBasis::XYX); eulerBases.push_back(GateDecompositionPattern::EulerBasis::ZXZ); - patterns.add(patterns.getContext(), basisGates, - eulerBases, false, false); + patterns.add( + patterns.getContext(), basisGates, eulerBases, false, true, + twoQubitCreationTime, numberOfTwoQubitCreations, + successfulSingleQubitDecompositions, totalSingleQubitDecompositions, + successfulTwoQubitDecompositions, totalTwoQubitDecompositions, + totalCircuitCollections, totalTouchedGates, subCircuitComplexityChange, + timeInCircuitCollection, timeInSingleQubitDecomposition, + timeInTwoQubitDecomposition); } } // namespace mlir::qco From a79324cc049d189cf817a5ccadf723e2c5e72c15 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 31 Jan 2026 22:57:53 +0100 Subject: [PATCH 174/237] evaluation scripts --- thesis-evaluation/__init__.py | 0 thesis-evaluation/evaluate.py | 34 +++++ thesis-evaluation/qiskit_run.py | 159 +++++++++++++++++++++ thesis-evaluation/run.sh | 33 +++++ thesis-evaluation/total_evaluate.py | 208 ++++++++++++++++++++++++++++ 5 files changed, 434 insertions(+) create mode 100644 thesis-evaluation/__init__.py create mode 100644 thesis-evaluation/evaluate.py create mode 100644 thesis-evaluation/qiskit_run.py create mode 100755 thesis-evaluation/run.sh create mode 100644 thesis-evaluation/total_evaluate.py diff --git a/thesis-evaluation/__init__.py b/thesis-evaluation/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/thesis-evaluation/evaluate.py b/thesis-evaluation/evaluate.py new file mode 100644 index 0000000000..dda4b4fc5b --- /dev/null +++ b/thesis-evaluation/evaluate.py @@ -0,0 +1,34 @@ +import matplotlib.pyplot as plt +import glob +import os + +INPUT_DIR = "./tmp-output" + +def read_stats(file_name) -> dict[str, int]: + result = {} + + with open(file_name, "r") as f: + for line in f.readlines(): + line = line.lstrip() + line.removeprefix("(S)") + line = line.lstrip() + elements = line.split(" ") + elements = list(filter(lambda x: x != " " and len(x) > 0, elements)) + + metric = elements[2] + value = int(elements[1]) + result[metric] = value if value < 2**31 else value - 2**32 + + return result + +def evaluate(): + all_stats = {} + for file in glob.glob(f"{INPUT_DIR}/*.statistic"): + print(f"Processing {file}...") + name = os.path.basename(file).removesuffix(".statistic") + name = name.removesuffix(".qasm") + name = name.removesuffix(".mlir") + all_stats[name] = read_stats(file) + + print(all_stats) + return all_stats diff --git a/thesis-evaluation/qiskit_run.py b/thesis-evaluation/qiskit_run.py new file mode 100644 index 0000000000..cbd8376dc2 --- /dev/null +++ b/thesis-evaluation/qiskit_run.py @@ -0,0 +1,159 @@ +import numpy as np +import os +import glob +import time +from qiskit import QuantumCircuit +from qiskit.circuit.gate import Gate +from qiskit import qasm3 +from qiskit import qasm2 +from qiskit.circuit.library.standard_gates.x import CXGate +from qiskit.circuit.library.standard_gates.swap import SwapGate +from qiskit.synthesis import ( + TwoQubitBasisDecomposer, + TwoQubitWeylDecomposition, + OneQubitEulerDecomposer, +) +from qiskit.quantum_info import Operator + +np.set_printoptions(linewidth=np.inf) + +MQT_BENCH_DIR = "../../mqt-bench/generated_benchmarks/v3_qasm3" +MQT_BENCH_PATTERN = "*.qasm" +is_qasm3 = True + + +def split_by_barriers(qc: QuantumCircuit) -> list[QuantumCircuit]: + subcircuits = [] + + # Start with an empty circuit that has the same registers + current = QuantumCircuit(*qc.qregs, *qc.cregs) + + for instr in qc.data: + if instr.name == "barrier" or instr.name == "measure": + # Finish current subcircuit if it has content + if current.data: + subcircuits.append(current) + # Start a fresh one + current = QuantumCircuit(*qc.qregs, *qc.cregs) + else: + current.append(instr.operation, instr.qubits, instr.clbits) + + # Append the last chunk if non-empty + if current.data: + subcircuits.append(current) + + return subcircuits + + +def circuit_complexity(qc: QuantumCircuit) -> int: + num_one_qubit_gates = qc.size( + lambda instr: len(instr.qubits) == 1 and instr.name != "barrier" + ) + num_two_qubit_gates = qc.size( + lambda instr: len(instr.qubits) == 2 and instr.name != "barrier" + ) + num_other_gates = qc.size( + lambda instr: len(instr.qubits) != 1 and len(instr.qubits) != 2 + ) + + return num_one_qubit_gates + 10 * num_two_qubit_gates + + +def evaluate(): + stats = {} + + otherCX = QuantumCircuit(2) + otherCX.cx(1, 0) + otherCXGate = otherCX.to_gate() + oneQubitDec = OneQubitEulerDecomposer("ZYZ") + start_time = time.perf_counter_ns() + dec = TwoQubitBasisDecomposer(CXGate(), euler_basis="ZYZ") + dec2 = TwoQubitBasisDecomposer(otherCXGate, euler_basis="ZYZ") + end_time = time.perf_counter_ns() + creation_time_us = (end_time - start_time) / 1000 + print(f"Python Basis Decomposition Creation: {creation_time_us}µs") + + print(f"Processing '{MQT_BENCH_DIR}/{MQT_BENCH_PATTERN}'...") + for file in glob.glob(f"{MQT_BENCH_DIR}/{MQT_BENCH_PATTERN}"): + name = os.path.basename(file).removesuffix(".qasm") + print(f"Decomposing {name} ({file})") + with open(file, "r") as f: + content = f.read() + try: + if is_qasm3: + content = "\n".join( + filter(lambda line: not "measure " in line, content.splitlines()) + ) + qc: QuantumCircuit = qasm3.loads(content) + else: + qc: QuantumCircuit = qasm2.loads(content) + except Exception as e: + print(f"FAILED: {name} ({e})") + continue + + # respect barriers + subcircuits = split_by_barriers(qc) + + decomposition_times_1q = [] + decomposition_times_2q = [] + complexity_changes = [] + num_two_qubit_decompositions = 0 + num_single_qubit_decompositions = 0 + for subcircuit in subcircuits: + m = Operator(subcircuit) + if len(qc.qubits) == 1: + start_time = time.perf_counter_ns() + decomposed_circuit = oneQubitDec(m) + decomposed_circuit2 = None + end_time = time.perf_counter_ns() + decomposition_time = (end_time - start_time) / 1000 + decomposition_times_1q.append(decomposition_time) + + num_single_qubit_decompositions += 1 + elif len(qc.qubits) == 2: + start_time = time.perf_counter_ns() + decomposed_circuit = dec(m) + decomposed_circuit2 = dec2(m) + end_time = time.perf_counter_ns() + decomposition_time = (end_time - start_time) / 1000 + decomposition_times_2q.append(decomposition_time) + + num_two_qubit_decompositions += 1 + else: + raise RuntimeError("Invalid circuit size!") + + before_complexity = circuit_complexity(subcircuit) + after_complexity = circuit_complexity(decomposed_circuit) + + if decomposed_circuit2: + after_complexity2 = circuit_complexity(decomposed_circuit2) + if after_complexity2 < after_complexity: + print(f"Choose alternative decomposition ({after_complexity2} vs {after_complexity})!") + decomposed_circuit = decomposed_circuit2 + after_complexity = after_complexity2 + + print(subcircuit) + print(f"{before_complexity} -> {after_complexity}") + print(decomposed_circuit) + + print(Operator(subcircuit).data) + print("vs") + print(Operator(decomposed_circuit).data) + + complexity_changes.append(before_complexity - after_complexity) + + stats[name] = { + "timeInSingleQubitDecomposition": sum(decomposition_times_1q), + "timeInTwoQubitDecomposition": sum(decomposition_times_2q), + "subCircuitComplexityChange": sum(complexity_changes), + "totalTwoQubitDecompositions": num_two_qubit_decompositions, + "totalSingleQubitDecompositions": num_single_qubit_decompositions, + } + + print() + print(stats) + print(f"Total benchmarks: {len(stats)}") + return stats + +if __name__ == "__main__": + evaluate() diff --git a/thesis-evaluation/run.sh b/thesis-evaluation/run.sh new file mode 100755 index 0000000000..43f070b123 --- /dev/null +++ b/thesis-evaluation/run.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +OUTPUT_DIR="tmp-output" + +MQT_CORE_ROOT_DIR=.. +MQT_BENCH_BENCHMARK_DIR="../../mqt-bench/generated_benchmarks/v3_qasm3" +MQT_BENCH_PATTERN="*.qasm" + +mkdir -p "${OUTPUT_DIR}/broken" + +benchmark_count="$(eza -1 ${MQT_BENCH_BENCHMARK_DIR}/${MQT_BENCH_PATTERN} | wc -l)" +i=0 +success=0 + +for benchmark_path in ${MQT_BENCH_BENCHMARK_DIR}/${MQT_BENCH_PATTERN}; do + i=$((i + 1)) + benchmark="$(basename "${benchmark_path}")" + echo "${i}/${benchmark_count}: ${benchmark}" + result="$("${MQT_CORE_ROOT_DIR}/build/mlir/tools/mqt-cc/mqt-cc" --mlir-timing --mlir-statistics "${benchmark_path}" 2>&1)" + if [ "${?}" -eq 0 ]; then + echo "${result}" | rg 'Total Execution Time' -A 8 >"${OUTPUT_DIR}/${benchmark}.timing" + echo "${result}" | rg '\(S\) ' >"${OUTPUT_DIR}/${benchmark}.statistic" + echo "${result}" | rg 'module \{' -A 99999999999999 >"${OUTPUT_DIR}/${benchmark}.mlir" + echo "${result}" >"${OUTPUT_DIR}/${benchmark}.all" + echo "SUCCESS" + success=$((success + 1)) + else + echo "${result}" >"${OUTPUT_DIR}/broken/${benchmark}.error" + echo "FAILED" + fi +done + +echo "COMPLETED: ${success}/${i}" diff --git a/thesis-evaluation/total_evaluate.py b/thesis-evaluation/total_evaluate.py new file mode 100644 index 0000000000..a137f1e036 --- /dev/null +++ b/thesis-evaluation/total_evaluate.py @@ -0,0 +1,208 @@ +import evaluate +import qiskit_run + +import matplotlib.pyplot as plt +from matplotlib.ticker import MaxNLocator +import math +import json +import os +import numpy as np +import subprocess + +OUT_DIR = "./figures" +if not os.path.exists(OUT_DIR): + os.makedirs(OUT_DIR, exist_ok=True) + +CACHE_FILE = "./evaluate_cache.json" +if os.path.exists(CACHE_FILE): + with open(CACHE_FILE, "r") as f: + cache = json.load(f) + mqt_results = cache["mqt"] + qiskit_results = cache["qiskit"] +else: + ITERATIONS = 20 + all_mqt_results = [] + all_qiskit_results = [] + for _ in range(ITERATIONS): + # evaluate.evalue() will only process statistics files; need to re-run mqt-cc using run.sh + subprocess.run(["./run.sh"]).check_returncode() + all_mqt_results.append(evaluate.evaluate()) + all_qiskit_results.append(qiskit_run.evaluate()) + + def average_results( + all_results: list[dict[str, dict[str, int | float]]], + ) -> dict[str, dict[str, int | float]]: + result = {} + for r in all_results: + for benchmark_name, measurements in r.items(): + if not benchmark_name in result: + result[benchmark_name] = {} + for metric_name, value in measurements.items(): + if not metric_name in result[benchmark_name]: + result[benchmark_name][metric_name] = 0.0 + result[benchmark_name][metric_name] += value / ITERATIONS + return result + + # [benchmark_name -> [metric_name -> value]] + mqt_results = average_results(all_mqt_results) + qiskit_results = average_results(all_qiskit_results) + + with open(CACHE_FILE, "w") as f: + cache = { + "mqt": mqt_results, + "qiskit": qiskit_results, + } + json.dump(cache, f) + +print("In MQT, but not Qiskit: ", mqt_results.keys() - qiskit_results.keys()) +print("In Qiskit, but not MQT: ", qiskit_results.keys() - mqt_results.keys()) + + +x: dict[str, list[str]] = {} +y1: dict[str, list[int | float]] = {} +y2: dict[str, list[int | float]] = {} + + +def define_division_metric( + new_metric: str, old_metric: str, divisor_metric: str, benchmark_name: str +): + y1[new_metric] = y1.get(new_metric, []) + [ + m[old_metric] / m[divisor_metric] if m[divisor_metric] > 0 else float("nan") + ] + y2[new_metric] = y2.get(new_metric, []) + [ + q[old_metric] / q[divisor_metric] if q[divisor_metric] > 0 else float("nan") + ] + x[new_metric] = x.get(new_metric, []) + [benchmark_name] + + +for name in sorted(mqt_results.keys() & qiskit_results.keys()): + m = mqt_results[name] + q = qiskit_results[name] + + for metric in m.keys() | q.keys(): + if metric in m: + y1[metric] = y1.get(metric, []) + [m[metric]] + if metric in q: + y2[metric] = y2.get(metric, []) + [q[metric]] + + x[metric] = x.get(metric, []) + [name] + + define_division_metric( + "timePerSingleQubitDecomposition", + "timeInSingleQubitDecomposition", + "totalSingleQubitDecompositions", + name, + ) + define_division_metric( + "timePerTwoQubitDecomposition", + "timeInTwoQubitDecomposition", + "totalTwoQubitDecompositions", + name, + ) + +names = [] +titles = { + "subCircuitComplexityChange": "Complexity Change after Decomposition", + "successfulSingleQubitDecompositions": "Number of Successful Single-Qubit Decompositions", + "successfulTwoQubitDecompositions": "Number of Successful Two-Qubit Decompositions", + "timeInCircuitCollection": "Sub-Circuit Collection Time [µs]", + "timeInSingleQubitDecomposition": "Total Time for Single-Qubit Decompositions [µs]", + "timeInTwoQubitDecomposition": "Total Time for Two-Qubit Decompositions [µs]", + "totalCircuitCollections": "Number of Sub-Circuit Collections", + "totalSingleQubitDecompositions": "Total Number of Single-Qubit Decompositions", + "totalTouchedGates": "Total Number of Gates in Collected Sub-Circuits", + "totalTwoQubitDecompositions": "Total Number of Two-Qubit Decompositions", + "timePerTwoQubitDecomposition": "Time / Two-Qubit Decomposition [µs]", + "timePerSingleQubitDecomposition": "Time / Single-Qubit Decomposition [µs]", +} +legend_positions = { + "totalTouchedGates": "upper left", + "timeInCircuitCollection": "upper left", + "timePerSingleQubitDecomposition": "upper left", + "timePerTwoQubitDecomposition": "lower right", + "totalTouchedGates": "upper left", + "twoQubitCreationTime": "lower right", +} +for metric in x.keys(): + plt.title(titles.get(metric, metric)) + # x_values = x[metric] # use for benchmark names on x axis + x_values = [str(i) for i in range(len(x[metric]))] + + DEFAULT_POINT_SIZE = 100 + scale1 = [] + scale2 = [] + num_erased_y = 0 + for i in range(len(y1[metric])): + if metric in y1: + y1i = y1[metric][i - num_erased_y] + else: + y1i = None + if metric in y2: + y2i = y2[metric][i - num_erased_y] + else: + y2i = None + if (not y1i and not y2i) or ( + y1i and y2i and math.isnan(y1i) and math.isnan(y2i) + ): + x_values.pop(i - num_erased_y) + if metric in y1: + y1[metric].pop(i - num_erased_y) + if metric in y2: + y2[metric].pop(i - num_erased_y) + num_erased_y += 1 + elif not y1i or math.isnan(y1i): + scale1.append(0) + scale2.append(DEFAULT_POINT_SIZE) + elif not y2i or math.isnan(y2i): + scale1.append(DEFAULT_POINT_SIZE) + scale2.append(0) + else: + scale1.append(DEFAULT_POINT_SIZE) + scale2.append(DEFAULT_POINT_SIZE) + + ymin = float("inf") + if metric in y1: + mqt_scatter = plt.scatter( + x_values, y1[metric], color="blue", s=scale1, alpha=0.4 + ) + mqt_scatter.set_label("MQT") + ymin = min(ymin, min(y1[metric])) + if metric in y2: + qiskit_scatter = plt.scatter( + x_values, y2[metric], color="red", s=scale2, alpha=0.4 + ) + qiskit_scatter.set_label("Qiskit") + ymin = min(ymin, min(y2[metric])) + + # plt.xticks(x_values) # does not work for strings + if ymin > 0: + # let matplotlib handle non-positive values automatically + plt.ylim(bottom=0) + + yint = [] + locs, labels = plt.yticks() + for each in locs: + yint.append(int(each)) + plt.yticks(yint) + + names = x[metric] + leg = plt.legend(loc=legend_positions.get(metric, "best")) + for handle in leg.legend_handles: + handle.set_sizes([DEFAULT_POINT_SIZE]) + plt.savefig( + f"{OUT_DIR}/{metric}.pdf", format="pdf", bbox_inches="tight", pad_inches=0 + ) + # plt.show() + plt.clf() + +for i, name in enumerate(names): + if "_indep_1_none_O0" in name: + print( + i, " & \\code{", name.removesuffix("_indep_1_none_O0"), "} & 1\\\\", sep="" + ) + elif "_indep_2_none_O0" in name: + print( + i, " & \\code{", name.removesuffix("_indep_2_none_O0"), "} & 2\\\\", sep="" + ) + else: + print("UNKNOWN") From 90c46f47be72c56eb26afb22b44d96fa60e0d4d4 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 31 Jan 2026 22:58:52 +0100 Subject: [PATCH 175/237] evaluation results --- thesis-evaluation/evaluate_cache.json | 1 + .../figures/numberOfTwoQubitCreations.pdf | Bin 0 -> 16543 bytes .../figures/subCircuitComplexityChange.pdf | Bin 0 -> 20717 bytes .../successfulSingleQubitDecompositions.pdf | Bin 0 -> 14784 bytes .../successfulTwoQubitDecompositions.pdf | Bin 0 -> 14819 bytes .../figures/timeInCircuitCollection.pdf | Bin 0 -> 18607 bytes .../figures/timeInSingleQubitDecomposition.pdf | Bin 0 -> 19016 bytes .../figures/timeInTwoQubitDecomposition.pdf | Bin 0 -> 19331 bytes .../figures/timePerSingleQubitDecomposition.pdf | Bin 0 -> 17911 bytes .../figures/timePerTwoQubitDecomposition.pdf | Bin 0 -> 18220 bytes .../figures/totalCircuitCollections.pdf | Bin 0 -> 17475 bytes .../figures/totalSingleQubitDecompositions.pdf | Bin 0 -> 17461 bytes thesis-evaluation/figures/totalTouchedGates.pdf | Bin 0 -> 18909 bytes .../figures/totalTwoQubitDecompositions.pdf | Bin 0 -> 16986 bytes .../figures/twoQubitCreationTime.pdf | Bin 0 -> 16818 bytes 15 files changed, 1 insertion(+) create mode 100644 thesis-evaluation/evaluate_cache.json create mode 100644 thesis-evaluation/figures/numberOfTwoQubitCreations.pdf create mode 100644 thesis-evaluation/figures/subCircuitComplexityChange.pdf create mode 100644 thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf create mode 100644 thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf create mode 100644 thesis-evaluation/figures/timeInCircuitCollection.pdf create mode 100644 thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf create mode 100644 thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf create mode 100644 thesis-evaluation/figures/timePerSingleQubitDecomposition.pdf create mode 100644 thesis-evaluation/figures/timePerTwoQubitDecomposition.pdf create mode 100644 thesis-evaluation/figures/totalCircuitCollections.pdf create mode 100644 thesis-evaluation/figures/totalSingleQubitDecompositions.pdf create mode 100644 thesis-evaluation/figures/totalTouchedGates.pdf create mode 100644 thesis-evaluation/figures/totalTwoQubitDecompositions.pdf create mode 100644 thesis-evaluation/figures/twoQubitCreationTime.pdf diff --git a/thesis-evaluation/evaluate_cache.json b/thesis-evaluation/evaluate_cache.json new file mode 100644 index 0000000000..a2f862a4c7 --- /dev/null +++ b/thesis-evaluation/evaluate_cache.json @@ -0,0 +1 @@ +{"mqt": {"modular_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 14.749999999999998, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 824.45}, "bmw_quark_cardinality_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 39.24999999999999, "timeInSingleQubitDecomposition": 8.300000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 13.999999999999995, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 11.999999999999996, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 826.35}, "draper_qft_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 22.65, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 743.4500000000002, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 830.4000000000001}, "vqe_two_local_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 285.1, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12939.300000000001, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 818.9999999999999}, "wstate_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -8.000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 30.35, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1399.1, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 817.8500000000001}, "bmw_quark_copula_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 147.5, "timeInSingleQubitDecomposition": 50.04999999999999, "timeInTwoQubitDecomposition": 853.7, "totalCircuitCollections": 32.999999999999986, "totalSingleQubitDecompositions": 8.000000000000002, "totalTouchedGates": 50.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 832.75}, "qaoa_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 12.249999999999998, "timeInSingleQubitDecomposition": 7.049999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 800.1499999999999}, "qftentangled_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -13.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 33.75, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1916.6499999999999, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 814.8}, "wstate_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 8.450000000000001, "timeInSingleQubitDecomposition": 6.349999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 802.3000000000001}, "ghz_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 19.449999999999996, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 833.8499999999999, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 813.5499999999998}, "qpeinexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -16.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 23.65, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1366.4500000000003, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 819.9}, "vqe_real_amp_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 14.899999999999999, "timeInSingleQubitDecomposition": 5.999999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 836.9499999999999}, "vqe_su2_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 5.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 21.650000000000006, "timeInSingleQubitDecomposition": 8.1, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 808.6999999999999}, "qft_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 8.200000000000003, "timeInSingleQubitDecomposition": 6.249999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 820.2}, "bmw_quark_cardinality_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -55.99999999999998, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 2.999999999999999, "timeInCircuitCollection": 155.45, "timeInSingleQubitDecomposition": 26.850000000000005, "timeInTwoQubitDecomposition": 4141.299999999999, "totalCircuitCollections": 47.999999999999986, "totalSingleQubitDecompositions": 4.000000000000001, "totalTouchedGates": 50.999999999999986, "totalTwoQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 812.3000000000001}, "bv_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 6.549999999999999, "timeInSingleQubitDecomposition": 6.149999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 809.5}, "ghz_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 8.400000000000004, "timeInSingleQubitDecomposition": 6.599999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 838.4499999999999}, "vqe_su2_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 635.25, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 22975.800000000003, "totalCircuitCollections": 20.0, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 198.00000000000006, "totalTwoQubitDecompositions": 15.0, "twoQubitCreationTime": 813.6}, "qnn_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 41.550000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1040.3999999999999, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 13.000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 817.3499999999999}, "qpeexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 24.65, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 798.7, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 815.2500000000001}, "qnn_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 18.5, "timeInSingleQubitDecomposition": 8.300000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 817.25}, "vqe_real_amp_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 289.8, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12900.799999999997, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 805.2500000000001}, "qftentangled_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 11.3, "timeInSingleQubitDecomposition": 3.199999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 823.65}, "bv_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 2.0000000000000004, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 13.700000000000001, "timeInSingleQubitDecomposition": 8.500000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 2.0000000000000004, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 822.6999999999999}, "randomcircuit_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 11.650000000000002, "timeInSingleQubitDecomposition": 8.150000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 814.4499999999999}, "qft_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -23.999999999999993, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 25.000000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1917.25, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 803.8499999999999}, "qaoa_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -6.999999999999997, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 26.550000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1506.6999999999996, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 840.45}}, "qiskit": {"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 297.44634999999994, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1533.8495500000001, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1417.1694999999997, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1409.6549000000002, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 7895.523400000001, "subCircuitComplexityChange": -75.0, "totalTwoQubitDecompositions": 5.999999999999998, "totalSingleQubitDecompositions": 0.0}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 3926.8486, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 0.0}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1489.9131000000002, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 184.4495, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1618.4104499999999, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1447.7477500000002, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1390.3970000000002, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1328.5080999999998, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1378.2878, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1421.9007000000001, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 180.74625, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1491.0009499999999, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 179.93175, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 173.43564999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1344.1407499999998, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1351.71685, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1467.64165, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1486.3598000000002, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 184.27100000000002, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 496.8003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 2.999999999999999}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 162.93785000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 165.6142, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 151.68349999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1552.0785000000003, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1442.9755000000005, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 174.4021, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 170.12144999999995, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1405.2790999999997, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}}} \ No newline at end of file diff --git a/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf b/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf new file mode 100644 index 0000000000000000000000000000000000000000..728e1e2b04d0351ec3deb5ffd379b40f8996c060 GIT binary patch literal 16543 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 literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/subCircuitComplexityChange.pdf b/thesis-evaluation/figures/subCircuitComplexityChange.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8b865a697253ea74ede452130d30ba91a818278d GIT binary patch literal 20717 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 V?0RluAcAmyeq0t7nH#dW{}1@Qbx!~Q literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf b/thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf new file mode 100644 index 0000000000000000000000000000000000000000..510bdabccce6d3b49a71cee47466a80c913de37e GIT binary patch literal 14784 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 literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf b/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf new file mode 100644 index 0000000000000000000000000000000000000000..61f53b4ccba12b64cbd57252a2a9874ae5c7fe80 GIT binary patch literal 14819 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 Watt7mmkA_V0gY!67uPY>W%xgV|5FP9 literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/timeInCircuitCollection.pdf b/thesis-evaluation/figures/timeInCircuitCollection.pdf new file mode 100644 index 0000000000000000000000000000000000000000..77f3a8b073bd2de058487838fb5d2238671adac8 GIT binary patch literal 18607 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 b0TZLIpS6$Q$ut24VpI_jJG+9GBIy4CRw0LL literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf b/thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5f29d6ae986036e123488ad3b14193d0477af468 GIT binary patch literal 19016 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 DqO9>w literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf b/thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf new file mode 100644 index 0000000000000000000000000000000000000000..60a4f414b29bd7c43891c238c7ca7b4009a0c578 GIT binary patch literal 19331 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%0DHK>Fx<}`svl&1+T)jfv@r&+ZVQ*tf$bo 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 N!pOL|6t$Je{tw^}V;cYf literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/timePerSingleQubitDecomposition.pdf b/thesis-evaluation/figures/timePerSingleQubitDecomposition.pdf new file mode 100644 index 0000000000000000000000000000000000000000..60a02c1f5bbef2e89281e16df39ec84e738327f3 GIT binary patch literal 17911 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%0DR$2Ei?tlkIteHPi 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 MNx8V>uFI4DKdbEwd;kCd literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/totalCircuitCollections.pdf b/thesis-evaluation/figures/totalCircuitCollections.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1a5bf20d5f720c3a5dfb7dbcc3c5ec9e6b38ee12 GIT binary patch literal 17475 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 literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/totalSingleQubitDecompositions.pdf b/thesis-evaluation/figures/totalSingleQubitDecompositions.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7fe4e671674e433be79be673cf26b1cf254ab7fa GIT binary patch literal 17461 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 literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/totalTouchedGates.pdf b/thesis-evaluation/figures/totalTouchedGates.pdf new file mode 100644 index 0000000000000000000000000000000000000000..bd00fdf79a0de7cc69e718290eec975a08483214 GIT binary patch literal 18909 zcmb`v1z1$g_W%si-6aA`OK-3XNJw`sAtAZ6l(eWwhlF$}Dj*;rsf2V$qmmLLf`F8? zQiAZ^MSWj?%Kv+PzUNyUXXf0AGv}N+=giEV%c(7|C;$-@Cgd!72YOOV2mygXu2znO z5)vRGy=!hZAR#$RPfI6PJCKmJrJaoj2nrNHf~2GfZCtF;1x0_Upy=x234)yz5Hi%V zah6ltwebX* z0Nu(f0;t&ddV+*hoBVil0-FX%x28~_?`H1_T`0CwNe7ed;2xO%yxfbpR5 z2m05rv39VOb@c^$1OtCCQJ5e^OcVlzgP>v}f?{A%Q4u)Mk&qlPM4%lHkl1%bWn5fb zfgA)~=~uF#AOEDIx{ZsSr#%Srqi;nACt&0tAw?&E9P&0OS8E&e06aY1Z7iJ$eKV3T ztMze;xJ*u&>-26~6>{qI<*stz;wod@VN6qqYggBrC?8;cQM$LjpzWn5QG3a>(Is=W zbibfW4ml%-d~@&C_2XMpjn|Jyg`_5=j+b{jj<(kp?oBnmm~ur_-wbbRa`rzsbE~PQ zX0^}d;D}4#d#aF3<@j6RF_ZE5y(g6%{ga1%#|@K>jSc(M(f+ujLN3*DK9|0tq}FSE znGjb+w)k_x8uY{#JFMA!EHAq7Q6%)NVfiQf+&Mn};(su)Q@Z-<@du3g%w?^q>5Eez z)?M*F?2HO^xVlu+dvBI-Z75%V1-aJFWYd>$r9?hP=Z(+>==xxN+f#)@gl)k!BAZn0 ztf8GXT3DG9oy71MrocgcJ9Mr>QYEICLgO6|jus=lSyiyxmRMmYP3}v)yxO}*9)gIQ zz5z)UQAw9Kzi2C}r)O0SY*b8`#OM?1^*oScirA)Ac2(LD<$u$=F86t=0rIuU=5?&$JQ%B`6^=@rmEKD+2cG$g z;8@X2L2W6{RGLeU*Uy(Izf*04u~ydydiSVY#|;}yOVQ%_wv zLp{j=-}=S~r(kj{-)DL`*etmu!;Jur(Tv)p(816osd*W}>-oN<584jp+m_<_Mx-zii_mLA9+2dko`JGU-NAu!7!AjTnceq@c$! zx9z!+6Nds_QoGeS{zxj1$*~&8_MZ( zOAPez+%l1HHpWcXV-eUZ$zawSN_S4*sIqFMts&v-N`L*-p^>ZXRBRr4SDiszO z2P|sqs3n~Bn%KK$@lg59TjUHXp3P7d>Ad&gILuz`CZDJ2(3T_yM@AAY_F^`F5w?-4 zh2d#gE5rE5S^5Nti^dgkDoA|mn=&kD~IIFfoUD8n&v(4~q0m3a3(1 zE-*Y*311mM#Nw`Vw4b~5R>&3)yCuYIKIB6nPwou6662k;OL88qJTK%PCv5jow@6f6 z64B283?AfhY?J3g?pbd!*yE`Yb#i2|i(poQw0Lol4*7kNFRjo5hI}T8A2o?V&!kHH zdj~%m(pdXhiTD=GEnTsWymJRDGM8u)>~*j{q}AqDOD%e7Z&^7iy&+R@*HZ^QXeDRO z60F7Is?r^9nbr&o#3}4iRR0jg&vX7d7Dj1qP)~t)SF@D@cTs#HahP$-FqDd3$IkxE z0}`!G?Kx+?EeK3k*r%~D)DGjUyVXnM4EnLMe%>Mya)S4OK@?H+UKVNy-C{ZUN!5_KM>dKkahIE<+^LEernt6}aXw|x>6 zQ6Cf`L3R!PunE)lxHXzA%o zn}hU4{1pk6nASE$Er~^rGu&kKdOIW9J*C8D5g?Uk=ebPMddWPu(;~BIU+bxe-RUa?HYF-J%Olg1*Z}Cy_ z^Trju(sZco^AGuSL!G?hpxe^akL}U&o0tmYJNOSx_2L(UqEgMuS(_1=>l^*@k0gRt zn4@v_0tvqD@TiaabQcO!eCAPQps%sOtSZVHNi#52QHjySxGTtcOSG%`_E4_go5H-5 zIO7-Cih8&NPr9q2Bnm|(_WqySsNvG`MO$PTLA2^=XNeI)6+Kt5=90@J3euA?%7}>h zYp&SS7~tYt2OMDY6zaY#e$A72VG^6qUljsh#5E}mj8s>s0ewuXWTy}E zC482zA|G2O&)hPWh8#v<=mvj-1^qNO)3q z^f&Lk*KWt>VCiDw+UcI~yO5So@%+@QQ4Mp;*Arv}LY^-=KHTLNlg3o< zHzTWLN*r8r49V+qb1!|$F~>X;lwwIDwxR6Pj$;~AHf>7pf19FpLOw}12?}#8^veSti=r z@vvpC6BQr6#qhV`pLHyNKFBz#I>>QS`x`1yv10-?5DEu{PZjt zN#V=JTuHjLm-Wlee)X=Tc!8dJ3_9h?TA0BFnk>HNyS*ddtfI|9hLHZbT7ykz!qh0H$ z_qZU%8j-IBS6QfTyAWXL-Tgo_Nq8InjEghlsj>~*(`ykD{9?Wd?Z7lX7pxj9oZI!$ zHo(EpdeTf50cgkwMf?FkLwc#nGDhNI-_&KpSwki5<>;F+f=YjoU@IVy(>2}L-zKOX zd*Gf*n$%j>odFX;n)EwSlZD^3N30@xW_W8+qjOm?1z*#&Y#dS25;jJbn({8FXeZ}r zNG_rSLtP^zFgFZkuW&z-!D87;^^IrAsJkye|9w;U8?BGzzYtbRctUU#!fK(gv8Vlo zMI_q)W1`drnxHM&Q4V)^2Lqv6m9H z>Zgf)B6SXHQ#z-qmKPo3nn{8GKo51+D6rcaf>Nzw!nfrne!fzcWn_|`6;HF~(RSrx z&h?_9K;p1q?AW3 zncIIH#ogdTQRu?>FfHil4c$`lYFvKcOk~`$VISgpN#4gj z(xYE;!oZW=r!}jNC9ThRoP4=#7<{9R+0sVBO5K}_KJgO1@O~V1w3>%JwDabgd_(P; z$u(tZ9T6v@yXRw(I`?u7dXoa0VXTj9!m+Up{TKz^)1t3+Jq=Km*OZZG=IQb^ri5;K zY={(P@Tv=F^4SK95hzTyXb8MgQ9gTt^W}gTLFC$WHiw_OEV3eh@VtDcO;$$HDv0kM zh3>Nw*0gh$ZsQJe&#Is=*a$dR(+V3EUxYVi6Lo7;Q*PVvhP0Z2W8~yj@@X~_AG)^l z)W{?*xlumzi#Yo@O+AN7i|b+dqqbp99RduN=ah!x8BD4;NXPs>yv88^`MLdN?!Eoh>WMph zuD7t>d?E@nlPK(-MSR$gU8q}keb~^@uum4VGBHu=`n6|%;@114>j#Mk)&~z)SIu3H zk9KxG7PDvh?9yZE2vJzD+L${Q2re;yT4&$r!(Y*0Q`h3KVnC#`y;OV%r`uYl~ zLpI|r8d}+wxhvEfQ#pfEjb|(<`1fyPV}Z6fF{l~Dd(2}-zulfAI!}w4*!O_}C%#k% zTkq4!NeRuqT@65o@e`lZ2nl)2^` zepyAv@*3|K=e6zuh2@7D?g!vr6uw5%&gM9XmaaD9uJ^{_{3$@Dp$Hoq*l&zoqVf%Dl_)*6?i1{x}~ zU$A+)&vu{*#kSkvO{Yz8Qa$wb$I)9~Yc_^DZyB`D`yV2GxO-TfzNvc-lVMaS4Jeu- z%<4EA9zHe?i(H&LPTSO|x>*xs!?=yCG9%nPD(rqfJW*E>)wuZ0!h_vh^DyaGPSEJ;*C@$oy5Y+bZfS=@>`a zMY1aD%Y|z(rg_yWvt{2t?#UZZjo-bPwG+Qsb!0fc`egf3U*qxOrM`_emal$qh5e!Z zq1dC_vszgA=&1MLVYTaV<;|lP;biMm-?ok(PF!j_n(cdd>tKj`@#)S1>RY!@lm9_) zpIgVOXU9FQM{9`KzL6I0PNh#SeqFwoBAePQkS)@gY(RfMLVgj z?L5^L32%07M#gE%yko9{WjD^nI99o|H70q?Nn7Ul5RZ1TXjd+I>s{o9KW&7#qSmw1 z(jrRZ*B39US7dRh)SAqU2kkn)f1~s{_SQ?5mufrpD>MSv7wa1QWE7kB6o>OmxtU6b z7Zrui$ncsBk0!>By1EbEiC=W?I2tcaioN8eUAs7HB0I9~PhwIu<-#+A< zFtvh$EeGlvsO1;K5cgQSFc`8RPmN+S>zbeGGIPuR&inn?&(g8}d%*v(7Yx@?alwG#*DcT-N#~YvucsF-XMH=a4AT+9!WtXn zSZ`6}kZOxtJAdg$&$4juhPcsOd!OouxrPYB9Ntz!2fRLa0~qlG#`H6(uuUUdsdQR# zT5XNCtKM_M!7>pJmz8>#z(W#?1T|9hS5c3RO01MWO(Gi_N4FmG_9%@^@>gq)535dQ z+pVZv_BPXUb}*;+q87o4CuI?@$J=L{=NG&X@3M1`CpLWc^A{5Q=s0GhRdiR%t%4x2GjirTMf-IgI+Juc;BqF!wwXIHyCv(_e0YgVN_ zDjPogLeY%iUf_E0NXFjT@|lZo6?2j=>Y0G3AUYhLIwEIxl|||a2mEt_E*0(?ex~r@ zdu4LLyS!<)rD8sw_lc~;+}OEsW#du#flbMZ_a1VdJ)l4-5mr2Thgh@FoTi-@Ue3aG zm568Dg~3XBAzLcX&fXp3vF&oo?{wxhfD(!nM&CZrQ#2;qLcE3sB+iCFjkUasxUvt2 zxl`llsIImOii^`)Q4ukQSFj{Yl^@>t^lI;(Lve20RT(zn_S`AE`pyO4V~k|2%Uq|> z@0245G4OwrPcpWbq#2Aax31oT|3UZ6fu25D_t9wB4ZjvLldk6kw8Sir-i9V)xdxSU zdsVbRFaGFZ<|h|@)>+R`@N=0iPiRk0@)->_XB$3Q;_=g0O_aL!T)Nis=3HR@Fy0iFI(hD z?{O@oI}R&cKX!H=tR|pkK_$NB)MfjaTa}xlTGO2x>vb8b;ok|pV$G1i*XE?+l(^ep zOi4eKF*9v(d8r7)D7{RsZ;4@BK9s+6E}>^Ks3K9K#kL4n+2#6cr=SW)*^auP2(@t; zij114%CcFhLv!U@%v0>N@&eDTvL%nBH@2la$JCO$>f^6W;a>F)8D5H^wQguu@Kf{^ zf7``%?!|N0alCyJGM)OTVxdRk7#kOY9EVq5w|mEqhD>?f3MQg2|7!gxf0=Y=D*f&= zqrG4bn|hOoXa+C*7Y#c%T%uBYWtR84t}`$%ea0i5Mh%`qNAzjeZ_I@WL;hR-;7IYo zdouW+nsgV>JTQ9Sdo80oXV2ppaYmemzfAVb;2FGh-$>R%osvCHEwCB)hEIb;%)`#Q zIO7ey9G2$@=h<(D&$tLv6!}BPI~!Rz=X@Ox8P7=4IkZNrE0S_xU2Pg!=<`eD37-lO zcGhI!R3Xd8sWW*ID?|iU_Cvl%xPMqyf@46JMc{$b;9!4jkT<_S2D{urh2qG+i^H)* zwObHG;n0CvDB>i8p3~Z%X)mqbG@gfSKP{B*K(w55;)Z>oR=}R2OX3i@N~gipKJe1e zqpn{YIoDVho|tI1o0^#4%*UG1Zrloy(=wX3ek?DFEXcZ4oXe?omi+U3$)NZ&k)|ktAih+wQR}beEDDTE!82bo zp71=Q;XIT}mMEV&d;D`mWMaDE%HP7hbQS}7g}V6^ zOwi}SzhMFc|AiKiOi**df)LB?N`(rNH-8;}@HyvvZQA==6Wp9jx6h5!DmbRpsvGGt z#|bBf+*;ajE5E-b?$l>%J8hhu8u>A4p`XTTE&Im!OGQ&7?)K#n+Y8l;I%0Hq2#vyr zPxx~u1${|JxgMD8P+Y9<&tx8k3lfNUUV3E1BaA9MxHWkFFd91TsBY?nd~%&d!c1O& zaS=}`k`0wgw=gS1u%y~*Zyb^Aqktu|wpGz{onUR^y<^u?yn?jX;ykuxpFEL*E_C6n z%w1dg8y_EyU6JkEE92w*qC6G`bLU>O@0cI%nu$AlAA_xSr^KH1^K_}ph@eHi?ga%a zYfbkehbVo`U2W>2nimbkE zKdW&4tk`=&B;3=+`k`OeU80w95hWBxo_wiZ(nCpW$PZ_oOoFJlRg1XqX9_P;^lbPf zUAf^!rBzx!r}v80_*F60+1q7KzB0qz92Au#QQRpWtUCLJj885IS|oDLdI(aTWMR!0t>{<1XUrozHZ8p*~Z`e55x= z&q&?EP&+ckJ%xDaR~3FI9t`^5#6$aZ|M({(YxUginc+LfqsSg=d}+?A=bTn3_A^3| zJ+X*)dkOGIT>f_Mmk#;0LX}#Jr&Xu}Q$Lt_2lX;`*jyxqvDBv;yw2%JB<@l`NJ>yZ z$2iF^fa`F*#%$k4HnM(r#*TcYm@+g<`DKt7yGOTbYAJ})`N>z0gCZj5gSS+g-q)8^ zK2k+2q!cyV%+e&b8{$gp)Tlvj#w|N#ac9+^e;Di}kqd0|Nr#iuZV-d8(60li` zd#{B`f%WMpq?;Z;u0o9M->OYW*Nod>?H;0HO7~4?XD_quoG`Wq*-q=Kw>RxnkWm%x zPBXJ5MG;ws#k-9Z&AL2@r91Xc?lZ(&N9^4;G9w>rRUf%TYbQ8?gL24d6YF+~Hl-?$ z@hX0-Yp4XyA`bDadXWV3(98MK9Sfab6piP85VgKD!S5~RPx^@n5yMKeu*d5cgb2=F zxwd}hth>yy>Es@>Lfs-Kh>?JHM8|3^<70?A&(l5|c zrrOuwT}l}W%Y}HF{2c9v zMy*piYoB(ZIF7E-xkRXQUkEC2WSy1U&MZq~$gd>n701QeaZokUOxjtUvsf5-idn*- zC!cbCr8A=7b$)cce4CV(#@F+}A)7&mi!OutVN&$)4Nn~20^zNst$zJy{X~Y5*C=XiTd0XyWTnn3O-~qkQIs&%Jb&#O$dWwbre5*MziV6T z$aBK@l~=txezx>S4VjHP(h|k;wl=8L{TLpt)S=xuG7<0onFXN{lTsvcg1 zEoM9a$y3-Viv|enFI*RmJ2IqL5PYolYoQONLDIf`2KrQS1H*!oV8Tlo2j~}Nku<7dTA@c>TWKAO&`?4rDB(tti8kIL}it#^6_h`gp34M+Y%fdw6 zRDssy&nL4|X%c&#BQ?%xeSXF2#fx8lJhKhj3SUk?n;-EOkrUXO&cC_&)!rc0B6L7)+tAlM zR4T0?-Fq!T*6)LcINM8R!W^i$0AJ~IV@^oGR(=~@U5QPnj?7Mx@*Ga5rYRn$A}JJJmw z+y%Pd;gGp;-+J4xmyt5$;}EZ+&+{&a^4c+}9amibwE@)qT_V&0yTmD2p5k)D{{l-G z0tUF;;Bq}M6rlq~2%oW{3Z<44%*6lcbW0byKhNy(umB?H^Fa(8dQ#{qy`SP{i~NPN z0)tj-gSE?qF)71n0f*lf_r&3EEQqX;6{J%pZtKB(MjTMVnGqCW7mx;WCT|DP%9p1{ zGRQM!V8-`Ot72hrPSAFrg3l?g=ihAZoQh8hxq%>^IzQgaGXp7ZglrF5l5sI=>`1sz z>Gm|I4KDiMHdP^B%^e$rFTeXBh&|wNP9jm&22yj3AbD+#@J`MD7NW|YN6EiHeGPoG zd+U{T)sl~xfjA=$wZi$dZvDmbGf%{I+tRN@I(|ag%sn3x$8BePZ7Nn^ef3eam!21| zn|fdJw$9|fw^7nfB~ly0u_BLiPSZWkFX*`y?-j3+?nZ*NxOJ7@$=hO83hg<;v`=_d=kB>op4Haq_NsR(hr&@B5dJXGZoL zj<1G^_f;~KoLisqNgMI*G`C8i@xY9F$CJNsMbLPZ;llewSm#{cLD=gWi`2Sc_4WOW zBYj_wu$Xylzn((MQ`|WS0`?cAj1#BCVkMTlu9?NY$Bre{XA9*)?7E`HoRG~!hM?d& zs-WSwL6M6=;Z>ZIIP1mOcBsQ8gbSQ<4%;ihAZOM-33;=o-67d?(;nf$M^sG3WJ~Io z^oSvo`~uDU)$BRgF}F+{%^nx}A69fo2};~8uK`7t?o$5b$Z&A z$ZM)h<#+ja-G~s47h&FfeH~0c{n&mTHHh0NtjVPthQXf(;-hGl{Xmy4pDztc< zX2xKW$ME@+J#UU0JOX!!(V{M?!fl@F<{J$B*g2I&vT24jg)3X8c8(g z)sXKm)pYh{%Dmr$N!aoWChSgpJ?a2-4BT{ zeI;@B#4-qHE;j-7=U9wZf!sUE=!sj>siN43RW)#rOWf}~G>c-Cda)@-9iKot4?i~i zw&g)ms(Xh!-o>?X#w??N6pP|q!rgjyQ^RsWN(u+Kj7DFxu|Pmwwb>D41$!;A^iE{k z)w{^ptfnh&xqU((xpl|>B$4nbE|TcqOw8yy^uDV6^*jq~f36)Vout1x$2VNjvub$|K7+jmy|E+j+>(>tsm}R*GiAK4G4b zJ!^XkYNt3m5X4{XN-M;PQ3Bo@wnsw{xANtn-J;&gmaoSJcq2;LOJ{`7)zWJ8_)*(S zEtM-~)%IUmmziar+G%a0^_Zjj-g+qI5)41`Q zF8LU@n8U~=T)jiB<;zJM8o{LrkgxQ5_2R+NDtl%M?~Eax@+ryiaSn{m(Z8)w< zNK{ZHwfF-07#68|!w6LUXr!$==B0G#htel6L;I8`x-LZY#zj3(5D2|4&p!uglV}TC zf3nHUykGRP`x#z`(6|p7-_CG|4eM+>!&^(5PD`3O!!j?{!t1`eIWG!0#%80ArP@6N zumx@?_6L~HJuHo#-cO))*iCaDD)B2*7ca~yJLjb3Va8={Y*%xExeu> zz2`lu`kauy4oxl1#!IkItYS0-1y;tFS{$g;tq3M4&2@Ej@A@$u9c-;_iu)g7@_XFy zIE5{zIAJ1xA$~9zLKkp@z-K7)^1@Ep_vC*W$j?p`mvT-}jtB*IwsOj`u(iUqHUdFF zwP>d-?JgD8DcGFil)->gk^epx1lFfGz*$2d&vWVA4XxLo3k#zff%Pd>1C2V^VwOk9 zXi%+jm~$fc#heMtE@!#y`#Y<5Un1LgX`fN|36v`JQB(^MV(U;z8m0@;unqTpS-g<% zrus1O5`Hx`V|}m+MzG@^9&;`Xq`e^BQD+53Vg+%Gc{p6fk96mXyp>b`V2ZmqC+mx( zQF`xGqFQc0lZNIZW_wS&N~WpGer9@_s(eD~r<>)nU&5LbB&3ue-2p3zNat)Et_9{? z&X;Y}7s@)%)Z5;DqrYg;${fxUeTnEJR z+Q(UVX=}u)PNL}xx3!d4KKKLnM~t*R(gSii;ZmVfAB2VvZIsPc>6P%-qMZmQy6iIH~Kd=1%{~51_!*LmMam)V4BB5 zp3?Fu4krZmH_G^q8Ywm_;BaPS$2NDeEL?>yg?b#X2Hba{LP>_|1=a**NeW)Ux=+Kx z%22ggU62y1U?ipIhcqDTo(?bS=w*GE$-fb#!W+h_9hG)AUcknsI$#1p^7Lu4Zf!EL-k?`CV6x^hSA-e*TO-pav*5S%iaQ+z%cU~c|GV8vVj zQ%0I?pGwgr4*U-u?cM>=zc=E ziVRcEuQyNmB0G%cdAHd90m5KcJ!&&dzG7M`nJ&{^bh(X_icbBFCA%Hz#dN4( zg@>dFXhG31)iTb-;4F+XRyb6dHMH;mtna6}9FYGt2)b=34Kdcc6W<#Jd>5^MSvGBKa^ zqpp-+x?o1_yU~}9!V4)C0v-X+tLlY{6PhD!BZ6BT(w<}9mB5r69mv;_qhszL&ec6% z_6}R>cC`!HB&sqnwTg8i5@dCrfnVlTfdUn+b02pOya{ln->0rlcIoDJq|gWAVj75^ z8(kdTNS4&%8t|ln4nKkS(i2cAuu1I4=Dnf%WGp-gr5x^{D00NA7yky<#K54s+Iz_Lq8%Ik6 zFI`I)542Kr_wbanw{!<83#nWFNJHUpLLqb=c^eOuyMvpjt2+qt<5H}ymzC$qrCW3# zfRWI&bOx@j%KqN;_xiHmud50`z)%QCKp0qhLqvdWF9;McFThPzbQc1|hkBYvC`|kzdpCCdZc?Vlt8{ir(`rfPw z2z}P&;bw^fuJi(uVt>ue-o^za46MZbY}{QzLar_V>YhG8%G2K621wfiH*SId9lU|G z2Y{83hmE(53!#vWgPpx6NXW$jsDJHtJQ|4twwXP`X*1rP+FtPs#Ezzv`aFW`+V;HIt6 zB?oIydtl6HXYkicyC-b;d8GF*n}1={3040Ot^g!=vb6I6=<-+mk?}yGFC>dWfvsM2 z59o{l6upxzXX&Q=ohV>JA;X_Z;JPz#5!(SJ<6`Gz10XJ>>uKX`fKF&w`kssk7?3dZ z*SpX^om~?U0u28DBM|<7Z3hN;Q^J4*3Il6BQJ5$Q0Yeaq0a`~y6bym_H>+X5n@Q*Z z2k=BcN0)~oAm3w{2)g|LTOj=T90rs{2Xq@h0~`hc3f%OD69NH*fFnR~IM4_%r9=RM77+yy00U`sjD8NhxrYEQnu`GS(QS%}iUIL= z7&<-~3`~R&9ix#z4+a!hwN*Zv!X`^m~#9)`n0K(USld5j%;+0KV5WoQ-EFrp2I8f(= zC_j-y$0zcG?nxM(0i#KTMjJgebPR+OzM#AN9thz-c=k_I-xC0W-E`=V`i7){Ih0g!e3K}UenDFG;j|RjL zVxXS^EePLb04)dq8%}5q$OT#^ehDY?fR>4$;Y3z`$OKv*ehp~6PVzt-XxaD~&?83x z`UwG?U7g4PS`K~-Vxj;~&~gEonqR|DnfMu_i30GQ@FYg3e+ehN{{9?Y=AVF;3p7{J z@xQO6{<<)c15C$>E(FvrKt!}w1XM2s?F<1aP0-1kdO%7C1aS5QrV9eLKTlG6Ab`n0 ziV#rB&>wcp3Is6xO6UE;Ln7zRlqU*wB-q|9C+gSpEE4 zSq}KV2HM#=4|qjxPOhF#4pty<;4>cp;`|_cPfs_G3qnH9|2z|Pb+_Xo6aw5MYcCY= zy^de2xmnwStSnJifNp*^0Teyyz`@l;9{rh<^YRy7qY`?)opn$jV zI}8C>pxCfDh211s(nSheu#B2wF@01`|f>yx(AA;NQzZz-ZU_ zf6GC@!hg~pi~#>ZXRyeh@&XnGj+1|HPfYB0oFR}?WD)`gj$nVQ3lW0@8WH{bXS~2f z`a{-$)sEQj^n*ar7W}umFfdx_|8IMMJMss9FgW0K{ay}%_!B1jhl~Rf2`o~6s|$F% zzw-kkED9X_|5i=}?H&9B^K`d#aI$ef`3#e;gP#qcBS1n(S68%pJFy`uF1D_K7j}{X abft%8(%aw=#k68=9T5zdnU literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/totalTwoQubitDecompositions.pdf b/thesis-evaluation/figures/totalTwoQubitDecompositions.pdf new file mode 100644 index 0000000000000000000000000000000000000000..106bb3f534e44e8d81bf77557ec457efa6ca2169 GIT binary patch literal 16986 zcmb`v1z45K^8gHjl!T?EN{4iZgdiv>phyTJ9g+%y2#6v` z2#O#eit@b&^#XmNgb@?~MT=agStRT_P6b3Nk-x*&nqs$ov;{pnq-vhc^y;Z+if{@92Yd?0r0Zy={U0 z;P40FtJ~W-Tg!O(0}vs=Ke&*vpfC~v7eazyXdyu~L|7PTngi4Twg{}_144gCR2qZv z0G^<@NxzZ>_xdLt)$B2jSSJwl2W$mrH(=)=u!0*v4mo>U4?BC@27IvI_SWuX{<-UB z>WNIf6c0wKmv3Css%Vu=@XIA3Ny57mv2b$1%EL}R!LB=l`QWRVpXMo6N0rz5-YO6V z)YRfmN@Aj$0sXZx&4>Pb)ss}?Q%a1imEn@HEr#j1jkzDzk2($ zUqEwabBE5=)KsI^VQ-J(fGNPd*_C3hwSDnT@52I(x%~XgMDp(ox2IO<_Dx>zIJQ=n z1T+mg8P+17beNdVES7gg3)xEY!)c+DPop@~?gZ=~B!V*~!E<0u$rGXwV#6yz5eIve zmJioL$1+EHJT!vu_$_-|J6|07+BQ2mdSf@fxWB5&j@$5Rt%13DisjuCoEargpDxm* zTC!;HR`ND!Jx~fve2{VcPA4TcbHzsQCu>N)p;?x z)vv=@^-IOdogK`iUEIdAQC`i-(|y$=nzv>q=gs3U_1pWhz01~`d2U;VPOb|_HX7K_ z8V_q!(l_R+lzK@k+??<;16@ppR0t=2?Y*`JH(xzG+L=CpA6e(|)gv z*~l|qM%W_b=ibchI`Q z9yC3C;e(%UDdo~!nPx8e0te-(-MWUKmv{cm`^J>vCM?l{!-QJQjW zDfbDH#wJcH`>vWRzS;YBt~7uzRW~B{458=64ay45?FXlPs!VVA+@Y+Pc%hvwTmBSp zdEVDWCxvL@^14oe$Q-v=nGC0%y6Gyh3gy@a_ejvg+cXkwc?+s8D7cJ;X<3Qqy>_yg z&rf~I-Czhk$eK-P#3>OKBbQcB7b*YKW8+$d$`mw-<60$YXUWJF`sD$*# zpPUS)7a{Ty$hb0NSH@V?wtjVY_T=cS9!b9T@N zlO}|gNR^w?d6%>mE4q5Wke)(LX57GDp zct4cmsgcw1cdO1mf|K3WJ?Ecz7lS{-B_EoJktRu?5^C?g7x0O--)=+L)qZ9Q6;k`C zs_B)+q8(K$xoxL24NRI}qVZgbiU|HoJZd8ft|tAyDrx*%B!V&`Xv>R^q0jF*toa4l zR6Rp1y?Jz*$dFJYhq&}Y?kIbU_rMUN=N}kw@zQjf_;{RRq$!C_GM%@fD~?KWr3mt) zq&{IVQu~aMvbLxz;stt2-oL&?kSg0+u_R3KsZw}_FD;Y2N{C(o&qC*=ROZJ6g{6CT zg1&Fj3FWlY4!O}X;EBsg2{OFh@Cna#-7FDSE<~Bayl$20JF4qUH(zPz3rqIL#?id7 zIp58tFwE!FEgiD0q}#n`1Tzh<>5a`jzQg4h-eu7?(3$eNLPnM%hB&4?txv*KNLxFL zPTDtGrfT?FU7z-_EGp!V<+Xt(y&L2G)}@Mpo@8YNbQAuVz?h9kC5T;#p)8$}!4*8K zJyt8UR+QmIg|>6Z^K#B_lXJ#PpO`@BQAYg)+?AsAg=yw<_9Vg1 ziBX3-+J@EK5D{C|bed|iwl1l5@0Li@iwf=MXdg`>o=vR?d=Z>mrnB z8bY~-uk*4{7^!LU`W|7s)3aU0E0;5*ub=hf(Bk&uB%bz6TG8Tt_D)_y@ySJ$;nLIX zDn)k$H&bH$<@)xJt?sL0{Y2Ph+5@9LyVHBu6F#0FAP=?eNJ56LS>&Z?$Ha;I6uoy9 zlYAvxZ`;}8s6p;Y@W#^sbTLgr4bZT0XSt&8*lXZPmR#7N^}N98R<<)pO1(%`i+|0V zPvoJ!hLVdm%h?_QA0rd0z@o5);Hys+1r56OS&W~4?a$~)GKIYHd2`ZfCZeooJ%$l& z^i;Y?wq*3xHKsh9+EbbL$X<2x^t7JgzLl|moZPL-^n-PCJAGZj-jbwLWPy(2)fk6R z>rk2Nyy2^s*(uu5aXxR(H59#6hEI&=W)*{LeXE+@oO@Po8g|W9Lk7BIDl{{ZODK7# z65q@l#6)y&p(ah0vj5&fsYSUkVTT@5Xr*t+tm{I4N;^GwkKhuov+dT?+NxT2W{-(D zW}#u-CoB!ZMcDK81|l~S;0N=>ebKtBO3=q@5H&RtgnSZHrLG7UlDMC+B{MG|_xwb@ zpvbm%fY7%hT~_->kfrY`$25g<}?oRpOgLcoDxkx3aM#9ik)7Bu=<<3sdO~RYEIJSGFh+=%bJ70%qmfzjtv`Fy3 zZBCi>iHFWn)OwyH^i>5j^n);{K>b5pOeWlK#i6f@;DW{rjcos!r0D*|te7Zx54T@l)**x zi!odK$q6o_>9)nP$kh$vX%<~}GMMF;lv^50q~fv#cS<9(Jt9hzwGE$lt-M{~$dh>~ zPj(@2u?H=ruXyo0d z4bUrmQhGDn^LSYyW9vf9Sn86*ZcOTzuKr8&3ld(NmKwQPbjteoL%5`MQukcu8flkx z6)#;Fn!a5)BD~R`O0VHt@D)-bcxX1-0kiQ4FdK=XC0Z;R+I>$irLqWn5ir+>y)j{G zxK@XV(_o_6_$t5N!PUqvgUTH1x`ntPIaI1sBDY%O@yW21sp6GhJ~c!+;Y)G**SWMSRH^A*ZQ z#hTbZ`1h5ltT<;KgJ=%Z@gLWnEvn%=&Lxli99qpBcBax-%{PmVkrtH3_VqbmhF9cA zQseSe8o8lsgxaNPpj7}B&pZ$UVa4)pwedJ-w1$IL=s|Qpfa^D(nIhQzOo3?sC7f+G{(3@3c zZQ)YwI*pNs`dS)`YC9J_te?cr+zyHrskR=CpQ7}Zi<0WIr#};*B$mY_661yRD7vA= zo3cvRZAJ=NDNLoqR#BQK8RU&ZNU*tKqdhT(UK&!l$t5Z=2FI<~uUqk&tQzENLzI)g z`1f5BTQKaE&A-gBD|B^ji{%-;M>TD-P)y4_lxpi8Z63tv>xWg^<*fnmEmjQ}=ce2X z8Jb0W7j>4s__Ky421Bo3e%sjjxEDJ%Hn-Q^yx85qoy~jkL&KP5hUWgQ0n5x&4<*0u zbg%3=%gX2y#swD(QQKX#TgtahZSP$%C!Nzy+Rd*Y6H5Mi_keqkaWISI`iQF3wS zRnx`=_qy##={nNr#~9RUy@0dFFr#`}jrCU&?{mx$hM^D?z)*$98?K38Blk}v%0Qb0 zN*#N6jDl$8JxeKt&DJ3-5jNfzoWgp7wN~88@weNX#F-Lf6|=2I4TABd=cjpST;w`B z*bnw^XsKbSMBLBNookIRx_%Q?HvX#iX@K{YjU=%fpbzeJ%<1e!CIb(9_1F4~q`x@a z#Uy{)=~(Q1&T3dIo11m3%p9dTKmF#$pxeDWcQ<(GQcc4cE{BD7HE7#0BFFn~u`&6{7MuwZE4Nwz#~zU2yr^ z>f85ok{7>x{!%apwrnxY*Bs5+H6gD%pI1n)b+G^yFXWVyhP`Kcm#G-MUjDJukB>ck z{B!983i)39$KLcT^jDs3N_-fT#8}GS^bL5LW$=MwjCz1#7@rmOl@HD9II65e33NRR zH9ScELJXUweOFpR&hep6g1yK1jz#`Xq#xgqgDG(Ug>_pKr#a`D7>}IZ53cTe2K7U` zAwf6$`CX>hBV(IdO}HVa$8M+r9mhjVi}P0Y8~O!=3N zZxXzZ6}yDI@?l|`s@0$7$_H(54Px?a4#vpy+Le9OBf~f33llSx@#~*d*}*3-cD#ty zY%tE$GLN0UGQj=*^5@S(o4o~dm$x>17iSv={2~U+Zp{oE?r**uTa4+V35;excsj+>IuDDfD@>TX^wL;&{gDEDvSSs#-MINm&Svk^sI*WlU;T5K zeti-&dv3Ox)9QSXm5|;((OLPuu4}3x-Gv+X9Bugriw(P1d(LvO2&O_g9Ur}K(uD@T zU-=exaf#Q@DC#QB8%K6p$_3?xwkD}&f1_waSdJ>Ju4Xnvz7!)zt=y)vxkO5{?J-J7WK;D?4a$Vac! zgSXNH^q=x^N<~m@HsxZ!jV~tk)PH_0n+-&#s6EV>i)Llt%b8&*}S2V zivzd(lds7tJ&vr@Tk=%9HJF62n~f4gA>n@3yo!~N(lCr)93f{@sc+upOwC5C`W&nC zahX4<#d&8Iug#ek(e`0^c;q3aclDZ!H3V>!vjmVt`NjdZaCorL_cC0 zKSD)^0T5hJ1OdcUe#VauBPX~{Q;_*TeYl{}@6i(!0tlJ>N9aU6shwJujYRIfK`m!+ zu#|Yeo%g4Q2d+}7W_HY0yJeywN!n5HPRIHFu(4W%rw4tUD@irSM-ug8scl{DIsW7) z@NZcs+X~L*s1O-_awL(YQY*ohbB3KD!)e1 zeuRq9kiXb#!T?1ZghckeT06-r-D6+$^r`y4O-2RH{|{rRWK|3nIgi@5zJBQPZLFR;>9#mGTPrkCisoBEtnmwmCMz9d7XsXsHp*9@k9xfkYY$C$#`<)-YGx;t7$ zb9yXy{-u@KN+q6gc8%=F3gfn11b^>B%A?uPx>T`the{$POyICvXq}5pPjhIr>XbBf zZqpqlnZnGmh1yM)ISvLnfkvAm@q@UHZK>X8s_A_#$*yxmUVh=@EAb3=58C7d6#PY> z_i-_GHhN4E?@>@`x7CL22N>1x*bqm>Y9wACchji| zWEHcJ(_daDR*Ye{%{;aATAFl4rNhZ2I{kt?f%Lo0x<`Se@1|e4^xYvY%NiEtb*OoZ z)e*#;ieh8w+brE!yz$IcX5>>1ALmD*8-_0)qZijNfQ7^}T| zNHNrMj?X0G4Z+wkTEz_-*|nH-T2@#Pco`Pd02AF1nacLmY{Zy#+IPaOO<#(h9}u;W zy-=?snO9i;{tWtspbipiZ&wgdm~&zZlhUq`3z9(rQ-MF6a%hnG99Yp1E4%<}W?oe~$VdMJ#uQ51aO0`Pm8)L90~G`H#Uf z*V7$>0(6wAAz?w4{Z50&P@}2Mj;DHZR9WN`3=vs1qRPmP;NAK9)?JiW;?}885f|nT z_$V(;9+S9&r~1vD=m_HBt~7op9vt@HzagZyGNubc-u#7rzYTBVT?=>j^{AbLNu5V@ zBvPF9jhr^N9LK=WPw42GPbtV7T$dfa4fpvqBNRKTUMkatWWKWS3mrJyV=qYwXKl$e z7%uKfCGV3*iHlLgo^hTOfix5OK67{;)5`YhE{D#LLdMu6&BrI_2_Na!&#i>gxL17k z`BHhp{mXM&4Zpxu+D)p^OG&G4h1`WLJOyEH5-!nlnLLg! zW7^w!Mk&{t)*a{d*__O0Z@Jr+Dya01J)}@qPa)ZpBXz7tykq!wW#mEYerlA*KdbBjFtI-nSUGB0jiMI0SyOGMPt`)qLOJvm~-Kh)d!8*;%CV7{AY0_s;FP)~zbiBeiXLqni{4L6a z{Rj%;?#X_mARPS{)=ykV71J$sX#K7~*|;`xQAdBa<%@@_tWMeamKbOc6&wIcx8Gp+Kf3g($v;V$@HbgS_Q@(bK39T?!u0{ zc*K>Qux0v4T6bK=U8(EXsuZ5wTAE%}Qi9_-+K2SBjy6nIOONm1-)7X4%LrWSjV>E5 zjcbwXlF(HD%#)!eV9!YJk<%?^5B>C@7L8(X9Z zW@0mDkrmzzqZzhRMOzhSzE*rWZR|^Zo4KiR7#qTxKH;fW_x5t%w&pkNw8@Zfi#JJ; z)S|ldMl;B+xxtU!aH#IdA88!_aj~2O8MPw-SVt2kc2N6xgWvPqr6`D zUzlR`L}^L_C<(#)ixCA~DVN>e|Xz2>`vg zME_Ze%_6nsmTb1{(!t~-$_O+eX?03!MwZbDDrh(>zxbzQHXos{XD=5`H5&({^iLgR zEwSrrf6^%xw2?87K08uKy&+!cN@HC6-~*cfMz&8YF>6F4A-oXZ_L4HJsq{@c8?6Sp zFB7FnaYv)8P3OGHW3l)d%oha*d#-SbL_3+8)4|c8x5w_B=1)VRXko^~1nU(26ed=a zWoNZxL;2DsNU=o*UFCx>@4lJv6Ot9!d|7(w!)GUhOsj~;s@q2XmJt$JW!Zl3Qe*;N zsf)4?vXB+SL_yLb_fC|?51n>B1O4t$5k8ogx{=)deVGUXktr`SA zO(X;0z&a2eI;^H&)n>;q$}#8SCl9<- zA;9CDX6QcxpCjB@VfbIzwm~#aFofjzXuj?~M|dCmt`}3R|A7JIF%e5ez;!m^u>0** zudE*3$I`ynef_j2mxdI$)>Y5Ae1FfVH>yu#B!@;$=GtM4?+7VtJ z;x8QRBT1EU(m?Q(8b9&ivBw#nWbFCv=|p%D;LlPqGylO$@66O>o3v zFCV#gp5|7#R`rf@HwL81t*h9*4w~8^=8nIVUcu*?=eF}$6+%h)RfaZeAy(=wc}UNY z?#Bh1wb!rxmO`vNE>bH^o$O0yJIx&GmuGe`Ke6}Vz$;R8?nA6yMw?o-@j%NfI?tUkN^S&2SJ4Z-2sKc;kXZ3ejN4SXtMu|s=S6F zAi59~QonBzCWJb6*Sn3M!-oTO;#{niv>hDyVbX7r1s#Fw5k3kOK;u8*$tQ`@5U`QU z25J;?eBvOG7;%8{qINxOpSkI@jTwQ$nrTDFpNGaQhep+N&K`eXMd)a|zkSaZ{fqUY1@D{hhWBJWw=K6H+RUP_ z#f0=fVkQ|{)t%Wxi5A_z-~X++ccyakGT)lMwX?D60BJ*Bvx5}rj4t|lg}5XunaIu& zdp*K-!=Qg5;^JCO@q|$F0rQA6Mr{G>WsVH7sy7clD(&sCKW32EIgiXv;RXAqaT@Es z5DCiYqP!Tnh_OR)o|gxw)^P5Ql6|-o^M-DB_G!jlSwyB2`tk|*`SP>OW2&ee&-1kR zug`SCKU2742@IlGYE40-#a5H`An#tP)8y^hw6W~uD(XZBx4jb!EMm_}bbgSfOHQGD zgFG<$y6Hnvt((Z5jPYolx5zD{B%o$W$!Xy*H>wq+p>{?}tBeFBFP2bv?9FBwIiXO*74#Y#Ys*~ zBloPh88c}%F9zNzOO#K}A8GCt@-YK)!c0*sA#d%5_0!f>!uai*I$Q6l((>0T=Of?Z zACo!ba0F^cm{}P7FU}+CTK(<-_r5ffAt7zcOwBCBv0`_=UXgu7nv&_HI$yY%W}5NH zewPF7(SLtYHcBJic@r}_R9*Y+2Kh0+11-IiRh+ddgJw@KwFqVKRHtx!rt7_grLw?q z;~V}Pub++ahOCIg+8ypbKtn3ThbfaZ8hff&$3)Z55yae2+L9?Dd&@G;S(L~(ou!kf zA+UHlvtT(eC;?Br`$hcBA`6M(A;IR_=JP74hG7H_U9Jts1`Jjw=La}J&7=r0OgZiTYIwy&hT7|;< zqpr*RDk*a93@OZ7w``+Dd6d+YY*Uo#S;9DSjP9CeW$T^4tMjVPxx3@xl*!XRxp>d` z{g@RZy?xD9v$PHMu<8`(=hJ#EqG55$pDg5OOrY!EYRjftN30?T zcgZG&d$%amLWSP`DIka=d<-A}_TQ>ZKu6WP8$>?sLuP&*{)u=&Yz2GY|G`0hN~*j9 zrPW91Vq}a;kTIy?#za>`{Ge3CtLlouh!LgfK9T5wq}aw3frvmk{sm~4SXb!#iVrL- zdzFLzcZqwzQx~ZCcE-c)*~-vcM7NIsp+Jzr zXt3o%L3P5*y%YxL-7NR9+W|FdqUFUkOm3P!7F?Dlj!hygBgFkXx1Uk#G#{gjXo3&> zyy6)%P^e?@Ln*nKxp^dfHBuRkz?dmqp$7UPki1)=_nI6n#7YL58st_8M(V zarj7&hNecVA^a^Hnx3@G#>BAQnJ(LgbehIeS6eqHfbrXx&37L}FMq@5_X+Yjf-Oh5 zT0(yzesDNS7jXR`$7pWej65QC$v+sv&v7CtgGo^Kge}C~#;w@O!G_4r7z72~$2p=| zIkW^vU~`0b1qZ^^|7q`#z?u9w5Ro3?ZImk6&>Y?>FOO{n&g8TY=+z)ruX(}7PgGmS zIj2iH7fe}qIcsHJB(CQS>U8fi+@%{4s8$@IZV(_N)TR|T$_CT3kB@v@7Af^qDF`tn zX`nmX5~hq7=JJV{r34PrT9WE%wt?vogr0lm*mUP*R!)!-P6lrvb{y6w-OX}1S5{{H~+>K!%=lwkl;=_+1m&_<~Ia7N38s- z`}Om)UcjIFPZrB3s!|dH7gM{rMG0-atV>?7l?b1M_24`Vt*v;3UPx1Dp}3$cK^{FT z8)N;4hO&$Vd1DE^038FW{+CgeJp*hr*ZDU>mCr}AX~kxpNfxljGz3qhDDK=z*DXn? ziM;_y+^KV;XR$0YW%o^y4C^`%o_T$rxvylR`9)D|>y=`BDCrTqIl@7M13LXbcZ1YH zV*s=F_AA*XP%vit{S-(kdH*3r*%Tdt7gi@t)I`s3ASy!d-WqnVF8w);(82}#h%VCp z3YvADNTt#i1GcB{vtiW@NJqjxosy1);svk%P|41cTPJ$=>T9bP`r3ZcKP{P`5EaD&Xg>4_kHZgxlg0D z*S|fB6M86ee#O+p*!GFU+4Zy@e%MNY(MRmoL~qy$#QUQCrKjXH2>bYy9^$I!=*XgM zZ3F)X*;I-lXfN9qWqv0ksmf#O0!19D7qF=}u2fIe)j) zqA7%4<&&X-^@n?{las8>>%l!oFy|`Co40pIS zXPe0vwQw*=@`#v-B324(GJid;0K{+DvY1@q)Hw%-hRz+2y39A}ZnPk!2U51xLKgac z2>8hI%qxp@FmaUsx>f>EY*62{;RoF)Mgp~MzZeywgoJkOZ4k6an(ypd;0{IR;JNM(C`SC6Dsr zOuyc#K^mV981z`ZLjROlQ5#jS83~PDCTnXOh}}GK&tS_q_2Wo`=xpNsItNSfqPCbd zlI|LO!{D#?dqY$^_AJI0GS73`(nf8Dtg`E%L<>*e(_8Z{zj}A}8lNZq$Nk5$@79ms z;}DxWV%tZ!v%-Jj%_0F$I~YPP7pVD)dj8=QF&o*lhuzAawsW(o7CQ4%iz3tsH$%9k zx3eukQ!Dre^7Gotg9qvl%H(xNZmw6T6LP~7wtbmObH)NjO?cc>8D4l^!pmh|{}Vyv z5q={K`WMk_q&AuoI2o?((tndW7YFv&EaDmZ_HZcfu>niFS!7)rTlS_-YAM^Iu0%k# zVD9~y$w3#yQbwJCPjF*>3%Dw!Eyf`_tlc@Q5kE%^Uv~0wskZDXmeKJNU7ng5LW!#l z7^+#@dm)+iY)dg98y-e}>7g=tS_bzK?qcLa;EePlbA7f?x1=ZIbO;f?fiTnL^5jOk zxF*+QEIn+z0y%J+lvbWyY%k&FQ`)yCh$k?b@gC|*7lIbiuMiD9JX)`TV|E29X?GX8 zJZeAQB{KZ{4ZlE9FXK=80HpXgy8r!@f0$YWma+D+|It#{)|NBile2fRHt^N8#`xeA zgSQV>*2&r%m<(34{?P{R#DNn&khLOb?_=xj?1}a820?%1;QSLYz`+2ln1;1GkSQbc z`_kX%|3^Fl2SVK%>+O7)LL&%);9O?l-#;yLGB6G~Eg-W8$d|#H9w6;T9s{Jm;PQU{ z%{ThlMFy60c5tu zz`npU@N>HWe9!{q^?(hX?XXV3mT~UkuPH)@T=;p1^Dmcw;nN}C{tu=A{C2Z;^a057 zSL~7YvBjl7p2!}RqYq%OHhkU_-`(DV9KX~@fQr{Z@g5Q^b z0Xqh!A#qp%nEw=mpX~Zs11tmLHQ%XpxB@~5ire-f`ZziGXMNl#+@21F2@ojU^FOQL zkb*$S4&VJ)0F6R}eiob{e3t>79QH~c?Ab`n0iwscCupeg39t1G_@a=a!20Jt^IHe8y0q4*Pz|GxCUnY=ilKh2Re^jC}gPwp;QRvH)twzUQ# zAvn_}4g0@`2?8b&7zhFC!=dp1b0}b9|6LLBe-48P0yg__MPXpYUo6ir-qOFj`9(P^YyZ+izaoU$YXtksV{z!)I`8aROd>BBi{xIYl)M;{c4!inB*eQ=z(|K0~E z#ozmofU*0d5AeeN=mV7F|Mme-D1bY-zkhInK;XbPYQOa%gm6E|-^Rfpf2;>6{XcMl z3;zKZ3P+*;*ar~S`yCf30*zz%|H4HG0fzneJ~VI(_gfzlpz0re@IPQckSLrg`d=7G zp+D#h0d)EwxS$Y#&->e4l#tLL>j^;s1N7TCApr0neL}!3`)}ifA%E^o7?8t1#=!x{ z==ZruoD#Hnio7^l>Kn~QU@{XBoLf1LK9?%gWu#SfZPQ4vk8fA=w2jG+)J^;$q Y2W#z(Jya)9q%aCa#>pwKsX+Gs0KG?DHUIzs literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/twoQubitCreationTime.pdf b/thesis-evaluation/figures/twoQubitCreationTime.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8a23f856d054034ffb82362a0db34e714864d2e1 GIT binary patch literal 16818 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 DfULLk literal 0 HcmV?d00001 From 3a8b02a3eca3dc07a00d0dd82f3aaf19ecf7bd1b Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 2 Feb 2026 14:45:19 +0100 Subject: [PATCH 176/237] evaluation script fixes and improvements --- thesis-evaluation/qiskit_run.py | 6 +++++- thesis-evaluation/total_evaluate.py | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/thesis-evaluation/qiskit_run.py b/thesis-evaluation/qiskit_run.py index cbd8376dc2..a82339dc73 100644 --- a/thesis-evaluation/qiskit_run.py +++ b/thesis-evaluation/qiskit_run.py @@ -128,7 +128,9 @@ def evaluate(): if decomposed_circuit2: after_complexity2 = circuit_complexity(decomposed_circuit2) if after_complexity2 < after_complexity: - print(f"Choose alternative decomposition ({after_complexity2} vs {after_complexity})!") + print( + f"Choose alternative decomposition ({after_complexity2} vs {after_complexity})!" + ) decomposed_circuit = decomposed_circuit2 after_complexity = after_complexity2 @@ -148,6 +150,7 @@ def evaluate(): "subCircuitComplexityChange": sum(complexity_changes), "totalTwoQubitDecompositions": num_two_qubit_decompositions, "totalSingleQubitDecompositions": num_single_qubit_decompositions, + "twoQubitCreationTime": creation_time_us, } print() @@ -155,5 +158,6 @@ def evaluate(): print(f"Total benchmarks: {len(stats)}") return stats + if __name__ == "__main__": evaluate() diff --git a/thesis-evaluation/total_evaluate.py b/thesis-evaluation/total_evaluate.py index a137f1e036..840baa9333 100644 --- a/thesis-evaluation/total_evaluate.py +++ b/thesis-evaluation/total_evaluate.py @@ -102,7 +102,7 @@ def define_division_metric( names = [] titles = { - "subCircuitComplexityChange": "Complexity Change after Decomposition", + "subCircuitComplexityChange": "Complexity Improvement after Decomposition", "successfulSingleQubitDecompositions": "Number of Successful Single-Qubit Decompositions", "successfulTwoQubitDecompositions": "Number of Successful Two-Qubit Decompositions", "timeInCircuitCollection": "Sub-Circuit Collection Time [µs]", @@ -114,6 +114,7 @@ def define_division_metric( "totalTwoQubitDecompositions": "Total Number of Two-Qubit Decompositions", "timePerTwoQubitDecomposition": "Time / Two-Qubit Decomposition [µs]", "timePerSingleQubitDecomposition": "Time / Single-Qubit Decomposition [µs]", + "twoQubitCreationTime": "Time for Creation of Two-Qubit Basis Decomposers [µs]" } legend_positions = { "totalTouchedGates": "upper left", @@ -123,11 +124,20 @@ def define_division_metric( "totalTouchedGates": "upper left", "twoQubitCreationTime": "lower right", } +# modifications = { +# "subCircuitComplexityChange": lambda value: -1.0 * value, +# } for metric in x.keys(): plt.title(titles.get(metric, metric)) # x_values = x[metric] # use for benchmark names on x axis x_values = [str(i) for i in range(len(x[metric]))] + # if metric in modifications: + # for y in y1[metric]: + # y = modifications[metric](y) + # for y in y2[metric]: + # y = modifications[metric](y) + DEFAULT_POINT_SIZE = 100 scale1 = [] scale2 = [] @@ -206,3 +216,7 @@ def define_division_metric( ) else: print("UNKNOWN") + +for metric in x.keys(): + print(f"Average MQT {metric}:", np.average(y1[metric]) if metric in y1 else "-") + print(f"Average Qiskit {metric}:", np.average(y2[metric]) if metric in y2 else "-") From b4af378f093b69e4091dff808c6eaaccee6a90e4 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 2 Feb 2026 16:21:18 +0100 Subject: [PATCH 177/237] update evaluation results --- thesis-evaluation/evaluate_cache.json | 2 +- .../figures/numberOfTwoQubitCreations.pdf | Bin 16543 -> 16543 bytes .../figures/subCircuitComplexityChange.pdf | Bin 20717 -> 20391 bytes .../successfulSingleQubitDecompositions.pdf | Bin 14784 -> 14784 bytes .../successfulTwoQubitDecompositions.pdf | Bin 14819 -> 14819 bytes .../figures/timeInCircuitCollection.pdf | Bin 18607 -> 18600 bytes .../timeInSingleQubitDecomposition.pdf | Bin 19016 -> 18967 bytes .../figures/timeInTwoQubitDecomposition.pdf | Bin 19331 -> 19312 bytes .../timePerSingleQubitDecomposition.pdf | Bin 17911 -> 18108 bytes .../figures/timePerTwoQubitDecomposition.pdf | Bin 18220 -> 18237 bytes .../figures/totalCircuitCollections.pdf | Bin 17475 -> 17475 bytes .../totalSingleQubitDecompositions.pdf | Bin 17461 -> 17461 bytes .../figures/totalTouchedGates.pdf | Bin 18909 -> 18909 bytes .../figures/totalTwoQubitDecompositions.pdf | Bin 16986 -> 16986 bytes .../figures/twoQubitCreationTime.pdf | Bin 16818 -> 22875 bytes 15 files changed, 1 insertion(+), 1 deletion(-) diff --git a/thesis-evaluation/evaluate_cache.json b/thesis-evaluation/evaluate_cache.json index a2f862a4c7..a340bb90f0 100644 --- a/thesis-evaluation/evaluate_cache.json +++ b/thesis-evaluation/evaluate_cache.json @@ -1 +1 @@ -{"mqt": {"modular_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 14.749999999999998, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 824.45}, "bmw_quark_cardinality_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 39.24999999999999, "timeInSingleQubitDecomposition": 8.300000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 13.999999999999995, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 11.999999999999996, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 826.35}, "draper_qft_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 22.65, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 743.4500000000002, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 830.4000000000001}, "vqe_two_local_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 285.1, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12939.300000000001, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 818.9999999999999}, "wstate_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -8.000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 30.35, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1399.1, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 817.8500000000001}, "bmw_quark_copula_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 147.5, "timeInSingleQubitDecomposition": 50.04999999999999, "timeInTwoQubitDecomposition": 853.7, "totalCircuitCollections": 32.999999999999986, "totalSingleQubitDecompositions": 8.000000000000002, "totalTouchedGates": 50.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 832.75}, "qaoa_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 12.249999999999998, "timeInSingleQubitDecomposition": 7.049999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 800.1499999999999}, "qftentangled_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -13.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 33.75, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1916.6499999999999, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 814.8}, "wstate_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 8.450000000000001, "timeInSingleQubitDecomposition": 6.349999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 802.3000000000001}, "ghz_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 19.449999999999996, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 833.8499999999999, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 813.5499999999998}, "qpeinexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -16.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 23.65, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1366.4500000000003, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 819.9}, "vqe_real_amp_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 14.899999999999999, "timeInSingleQubitDecomposition": 5.999999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 836.9499999999999}, "vqe_su2_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 5.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 21.650000000000006, "timeInSingleQubitDecomposition": 8.1, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 808.6999999999999}, "qft_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 8.200000000000003, "timeInSingleQubitDecomposition": 6.249999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 820.2}, "bmw_quark_cardinality_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -55.99999999999998, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 2.999999999999999, "timeInCircuitCollection": 155.45, "timeInSingleQubitDecomposition": 26.850000000000005, "timeInTwoQubitDecomposition": 4141.299999999999, "totalCircuitCollections": 47.999999999999986, "totalSingleQubitDecompositions": 4.000000000000001, "totalTouchedGates": 50.999999999999986, "totalTwoQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 812.3000000000001}, "bv_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 6.549999999999999, "timeInSingleQubitDecomposition": 6.149999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 809.5}, "ghz_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 8.400000000000004, "timeInSingleQubitDecomposition": 6.599999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 838.4499999999999}, "vqe_su2_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 635.25, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 22975.800000000003, "totalCircuitCollections": 20.0, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 198.00000000000006, "totalTwoQubitDecompositions": 15.0, "twoQubitCreationTime": 813.6}, "qnn_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 41.550000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1040.3999999999999, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 13.000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 817.3499999999999}, "qpeexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 24.65, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 798.7, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 815.2500000000001}, "qnn_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 18.5, "timeInSingleQubitDecomposition": 8.300000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 817.25}, "vqe_real_amp_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 289.8, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12900.799999999997, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 805.2500000000001}, "qftentangled_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 11.3, "timeInSingleQubitDecomposition": 3.199999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 823.65}, "bv_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 2.0000000000000004, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 13.700000000000001, "timeInSingleQubitDecomposition": 8.500000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 2.0000000000000004, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 822.6999999999999}, "randomcircuit_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 11.650000000000002, "timeInSingleQubitDecomposition": 8.150000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 814.4499999999999}, "qft_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -23.999999999999993, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 25.000000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1917.25, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 803.8499999999999}, "qaoa_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -6.999999999999997, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 26.550000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1506.6999999999996, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 840.45}}, "qiskit": {"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 297.44634999999994, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1533.8495500000001, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1417.1694999999997, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1409.6549000000002, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 7895.523400000001, "subCircuitComplexityChange": -75.0, "totalTwoQubitDecompositions": 5.999999999999998, "totalSingleQubitDecompositions": 0.0}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 3926.8486, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 0.0}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1489.9131000000002, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 184.4495, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1618.4104499999999, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1447.7477500000002, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1390.3970000000002, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1328.5080999999998, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1378.2878, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1421.9007000000001, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 180.74625, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1491.0009499999999, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 179.93175, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 173.43564999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1344.1407499999998, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1351.71685, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1467.64165, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1486.3598000000002, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 184.27100000000002, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 496.8003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 2.999999999999999}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 162.93785000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 165.6142, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 151.68349999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1552.0785000000003, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1442.9755000000005, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 174.4021, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 170.12144999999995, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1405.2790999999997, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0}}} \ No newline at end of file +{"mqt": {"modular_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 14.85, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 817.4499999999999}, "bmw_quark_cardinality_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 38.44999999999999, "timeInSingleQubitDecomposition": 8.300000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 13.999999999999995, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 11.999999999999996, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 819.3500000000001}, "draper_qft_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 22.1, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 721.3000000000001, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 817.45}, "vqe_two_local_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 280.54999999999995, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12609.250000000002, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 775.8}, "wstate_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -8.000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 30.249999999999993, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1362.4, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 805.25}, "bmw_quark_copula_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 148.05, "timeInSingleQubitDecomposition": 48.9, "timeInTwoQubitDecomposition": 806.5500000000001, "totalCircuitCollections": 32.999999999999986, "totalSingleQubitDecompositions": 8.000000000000002, "totalTouchedGates": 50.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 795.3}, "qaoa_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 11.849999999999996, "timeInSingleQubitDecomposition": 6.9499999999999975, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 788.3999999999999}, "qftentangled_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -13.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 33.99999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1859.0000000000007, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 815.0}, "wstate_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.900000000000002, "timeInSingleQubitDecomposition": 6.099999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 788.65}, "ghz_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 19.699999999999992, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 808.9000000000001, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 819.1}, "qpeinexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -16.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 23.749999999999993, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1349.45, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 789.5}, "vqe_real_amp_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 14.699999999999998, "timeInSingleQubitDecomposition": 6.149999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 811.55}, "vqe_su2_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 5.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 20.85, "timeInSingleQubitDecomposition": 9.149999999999997, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 785.35}, "qft_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 9.350000000000001, "timeInSingleQubitDecomposition": 6.649999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 807.7499999999999}, "bmw_quark_cardinality_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -55.99999999999998, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 2.999999999999999, "timeInCircuitCollection": 154.14999999999998, "timeInSingleQubitDecomposition": 26.8, "timeInTwoQubitDecomposition": 4105.85, "totalCircuitCollections": 47.999999999999986, "totalSingleQubitDecompositions": 4.000000000000001, "totalTouchedGates": 50.999999999999986, "totalTwoQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 778.8}, "bv_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 5.999999999999998, "timeInSingleQubitDecomposition": 5.999999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 787.6999999999999}, "ghz_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 9.3, "timeInSingleQubitDecomposition": 6.399999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 784.1500000000001}, "vqe_su2_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 617.9, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 22416.35, "totalCircuitCollections": 20.0, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 198.00000000000006, "totalTwoQubitDecompositions": 15.0, "twoQubitCreationTime": 790.5999999999999}, "qnn_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 40.300000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1006.8, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 13.000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 805.25}, "qpeexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 23.249999999999996, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 792.6499999999999, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 790.5999999999999}, "qnn_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 17.75, "timeInSingleQubitDecomposition": 8.3, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 784.45}, "vqe_real_amp_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 278.65000000000003, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12623.799999999997, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 811.5999999999999}, "qftentangled_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 10.15, "timeInSingleQubitDecomposition": 3.999999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 786.0500000000001}, "bv_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 2.0000000000000004, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 13.200000000000003, "timeInSingleQubitDecomposition": 8.300000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 2.0000000000000004, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 790.9500000000002}, "randomcircuit_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 11.200000000000001, "timeInSingleQubitDecomposition": 7.700000000000001, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 794.95}, "qft_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -23.999999999999993, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 24.799999999999997, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1866.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 795.6999999999999}, "qaoa_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -6.999999999999997, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 25.950000000000006, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1508.2, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 783.8}}, "qiskit": {"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 209.6128, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1419.5159500000004, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1343.9643499999995, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1351.9793, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 7665.386049999999, "subCircuitComplexityChange": -75.0, "totalTwoQubitDecompositions": 5.999999999999998, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 3760.08035, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1407.0931999999998, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 169.70975, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1500.8784500000002, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1378.24565, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1358.2719, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1285.2263500000001, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1341.5559, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1375.4873999999998, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 164.0675, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1406.1616999999999, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 161.11284999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 162.2279, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1225.2042, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1288.63685, "subCircuitComplexityChange": 15.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1383.5497999999998, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1403.6088000000002, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 164.67900000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 482.5216, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 1830.4915999999998}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 161.5916, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 160.09644999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 149.87760000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1410.09395, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1356.1121, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 162.5001, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 161.91225000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1320.5394499999998, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}}} \ No newline at end of file diff --git a/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf b/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf index 728e1e2b04d0351ec3deb5ffd379b40f8996c060..dd136a0642f3b41fd38ca1b7cebe9f3fa644338b 100644 GIT binary patch delta 24 gcmbQ=$T+`|al>pIP9p;&Lo-85WAn{RY<@EX0B3jzGXMYp delta 24 gcmbQ=$T+`|al>pIPD5iuBO_B21Jlh*Y<@EX0B1u8D*ylh diff --git a/thesis-evaluation/figures/subCircuitComplexityChange.pdf b/thesis-evaluation/figures/subCircuitComplexityChange.pdf index 8b865a697253ea74ede452130d30ba91a818278d..3a9f799e997edf0adc569c41ef92b4aba8e2a476 100644 GIT binary patch delta 9133 zcmZu$bwHF|v!@j#q(N#)r8iiXr9oL*=@RMg76g3gTylveq(Qoo5~M>BL_kmhX$5Hn z3FCsV_kQ22_xWp{-_Due%$%9ob9T4#aUbX6)~4VB>Iuw>9vxq6N|!hCEFJ>P#i{DP zxNN1|o1u%L;1av%1&vfq$=`Rrb5v9;Dctw*B;qj(!V;n+&}H!flqa;588xT<2-k88TKkI(DjKRz$^4hzdHeB4?}W7|C#TuM(r_Vf36k?!IL#0b`O4`#0B%Dk4j zS>t#gs9Kz6+p9hK2;|)UkZTe^2xrSXSY4-ix6u~xJiSRr_)eWeY9^hLfn#je@hh{U z!hF-T=u_rx7on3CRcwcakviTw($OV}E`N%9RoAUVooHXnOl`${Pli za&O}Nn$}|x**pCS^wN_2$C5N=D}jOH=7|byD!{htOUE=hUQ4d#raryhY!y$__WQ`@ z;^rM7thGwb0C-zNkimL&jj#=RN@(uje192G1{r7xBqTeQO#KY zF0OV6SA+`-7TZH#YN#E0JTd zitdn@&oM`433B5&Rgqk<49c(YHN3glOu8wN!w{0alQ3OLeyk|*F+SFs$Wsju-ym)A0 zf{FEk7fvnAx6wCJU4|oW_sD+bQtS22-O9JCO%~Hb>0-=2&(n74@!!4N^h5yJizU^T z{UYqd5gDBXM*fMUq}un@P~<*(n`yNVHcMD2Uq$IlJy|q<;1azm-IW;TTt8^tIOt~v?=Obo-a-jON_k-bN8yF!_d15K|u@y*$xYBgEtg1mz9 z$5B=FGn6(mu(1!j;kgfPPn9PShpbcLjeLJR5MIWQ*SaL>!clO8>-a6IIqEf}08*sx z8_OR3sXT2GX|azrXfnHfoyo*ct#?B{y)3T|duQ8jH|m{%5+!SNf>~k{;4m}9AU0st zcP+8rJGfH9xZAyYaAlj^lH+Y8zQ^?uJ6BrHAe8L-3vSZQl2(vrL!Ry0ODRfQ6V9p? zvmUedo0Og(%e)`1#eDXqUr35vW$kA`TcH=7N4RcD8JKHZw~i?(j!TVG*sBLc*uX^f zTg<=U)W6c3Bcdw1XCHX?7)Xqvfm|+|n`ae3y-x1;O^fJ&kIQ-pkd?{Rm8a55ws6f~ zzYK1+$e%xr!LiIXvoCwyU}dbcJVeWv8hp1__Um?RpmtsRmnk&HG1a zE0p8c!we>n6e%;@+U{j}b0J%B3u%0AALTrSFMKzWzNV50+P!UpqnuJ|%V?Y_)7p#z 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 W1S6rCM;a&+C>%n<&8=_;Me;w=d)=-8 delta 9502 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 diff --git a/thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf b/thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf index 510bdabccce6d3b49a71cee47466a80c913de37e..6697fffda0a24b495cc41764789a9a4b8c302c22 100644 GIT binary patch delta 22 dcmX?5e4uzkgDIzxfsvt^p{23K=1x;{764-E2T=e3 delta 22 dcmX?5e4uzkgDIz>v7wQXsfmHf=1x;{764+Y2S)$^ diff --git a/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf b/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf index 61f53b4ccba12b64cbd57252a2a9874ae5c7fe80..253f8d968aa9cccc492307be6cee1357d4147fb3 100644 GIT binary patch delta 22 dcmaD{{J3~SzbU7Yfsvt^p{23K<{75mEC6Z?2gv{c delta 22 dcmaD{{J3~SzbU7ov7wQXsfmH<<{75mEC6ZH2fzRT diff --git a/thesis-evaluation/figures/timeInCircuitCollection.pdf b/thesis-evaluation/figures/timeInCircuitCollection.pdf index 77f3a8b073bd2de058487838fb5d2238671adac8..c2edf7e5253fe8df00665b97ba46ac6caddac76a 100644 GIT binary patch delta 6944 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 RH&s$1gb;IcE2yK0{||}YJ#PR2 delta 6943 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 diff --git a/thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf b/thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf index 5f29d6ae986036e123488ad3b14193d0477af468..6f95867774fa13ccbf20fa735f264fefb15e3fe8 100644 GIT binary patch delta 6265 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

h|c^P!oNga1(C!@xKmQuK26P)nf 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

=Z}Tmsnx%tg zUB1Vvd5}ccOsQK^?e%qY3;?;NWT)>=$yVQ;vXnM!1WJASWkVmB666#y(GRPab9{lTXXYlyD#i+-_eU6K&J7pj;Xw_fSBl&K`P<=cn3Z` ziI?rNbmUr813|5__6PE+)%$gHDPMST-5!%#rQaep3nPhC18$Mg}qP3TDA z1SCxY$$~ppmcG9UIMFGWEebxw2Os)LCCmyG^zs1Ya2*tQrgq|G-a`x8+oea$?6fH- z#21`_V9m?IfgsEUWY!S-vzXr9()Jh9QZAkhise8%Jxa!T+{0c>VsQkQ4B2)}xpM$b z3F^0q$<~Tj(HzKabEi^MrL^M9R0pWVPC5Y4 z!%3qxS#bsI^XTV{VqXB>Jpz}H7_I959_17Xb!92yg}D<5a=O)uWhrPB^+#tIweq^g zyXt@TV?eFD@I9<+IQ`9}MrT2)nwrlablZ_vki1-;u# zsvzWd&74qxMo#yz8(TPoVK5gPAZQq^zu8;V5H0yc&So);!7;;wZJYp06?>(b({1tZ ziNGG-cqaOjnP#e<87$hwvrqj%`Sgu?7S|n2fXd>P6xI5{Oj%MEzh&gB&X`!s+5FYW z3`@BgKgm1Fy)(y2{IkX;msDjhYT+0r(?^~MJ4q5Rvrg1&CG70498SC7NfSx_vWQ#Z zl6mSByI)3JC4Od&r~rc$@YkpdaKQ^&=NU;d=4JYsZqzF#8eEf$37ePX^EenAPc^2d?^{-8N;(rAw=ewYTCL*y(YklDaJ zXV9IVfOr}~A2imaKQ@e!f3A;mPYW~|wRiSxNS|M2I>L2{FP)RLU>6xcm^+!>%x4o) zS6)hLQo+BtMp*7*J^>cE=o@j<@4sVk#`&*j+&)f$%_iSwE{uIh$2Wan(Grs-KBK8F za)Zg3+dTue->IJ?k{+(HYh5+Q7QmB>)=X!c=H4z;@i(6@%wSG8;d58N_NtC|e9tz! zw)@CuHVMklVk7X1dlKV9gwCIL=@Ei`d_|Na;(c$2iPog<bKT$lL7u?0e8qiM#uX?q%5FS`)!)um>C_ zcQSfb7agJgtiyS3rOMCzNi+3Hzwq3a{gAv--LaT0W>O*%p?rC25nj~OaPewkC2for zICtGi3miV{nA3>==xph$Qg!cYos*(D>m9*Su+PMJ^csfUP%5Xx;q$4BgNL+S1>(v! z>($B13^TkN&z|VTaqwLGq`2qt)u-(t=BvsmP67V({j2VkI8%vN4L+-$D%G`54~AI6 zMH!lZ1TRitE^FPJZB2cTkJ`?!&vy5|^Y4gX{Bk^Bx_)&*)v)R0GEp@2b@0pe)mF_V zUQ&{_J=z>|N5=en6V!TwLMir&(3*KidOyl1&YQ2M_M_-JSAr~A+*mFn5hf&_U!I|r zmV1MCYqn=JfFva7JlT@RXFf1 zdd_f_H8pv@@^p^lw?9h5_05aWrg0;jQ(TH&WnaY<+3a&LO6&_Yb%0`$ewG(WDG4hg zo&%%dzitzN z_(hnqw>yIN;dUgB0hquq5%Tt7#_XKMDVEpXJJQF=Y{%9p(?Gz%N zGsuSFekZhn`_)U4fhVSv*6|W%J6M&lBr8YG8NJX6cWUr9F}OU~gr~MKo6anlu1>g; z>4(J+cu3HwzyAfXoJ?(?aS^rb;+@*)69qv{gZg6oKi~gCKbE?NP7WOz2#=KgCC$Uw zI-VVBQn=M8o>JNZMKFHV=x2YIDEsd9X&ku{2Gy+IBM2ATS6Liilg6S9=PDvVi}6SYEOa|M`HxVFE%oF(LR33?V3R6NBC4 zLm&j>(iD(*f(W6w2?Y)eOz`hzFvv|RAtdxh7(#*&$lq(>5ai!>V3-j2CI0J*`3fC~N52lF4n!3dZT;%_Gi zB;tk>q@dspJ0$EzV!%iw9QhX?1Pl@UuOh$wgTWB60Q`oWU_r=#4gY5+bVCIQ7>2mv z%ArYT{hbjc>;|C_MBs)pa3m5M2USrZ5)vSO L@IX#op7{R&2K{y2 delta 7112 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 delta 6396 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 diff --git a/thesis-evaluation/figures/timePerTwoQubitDecomposition.pdf b/thesis-evaluation/figures/timePerTwoQubitDecomposition.pdf index 657d119dfd7a7615118b65cdcc67d9763bc19f45..f49f80f9181bbbb68ac36741117c032696990edf 100644 GIT binary patch delta 7100 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 W3$bMEp%RQpOq`gTTTx4i`2PVLc&_09 delta 7076 zcmZvAby!qg_ckaYE!`m{NX;-X%pf2jC5?!54jqyb5{Hm3k(L%DWRNZ?VMvJqL>NFy z8l+pokLP*+_`R=u`;T*-v(CNOz1O|gUT0sYB@P=Ihh33~4fMa|>~U>A(H%>G<%-S4 zbFe8pC_tELj3CUm&9=C*MA)yDq&rgA|14!6-9cw(cJG3|II6xZ2w&wB9&MVvyqf-V zd9gn2JPWj3eq(js`DL0D(0aDzd*N5#czwP2e6}SS(Aw0vrBZ+C9bGo({we*>@AfO| zS1n!>3;~zeK)m{&3-t7sP`#IFdmnoD^vW}OA)EbbslVkJI=r)-{<}ZX$<$ACNhv9M zMGj!vUmLLDQ!K^cQRE0a+D z$DpHNATODvx}m^1b#a~&e|DnY5SN?P@JR+4J@WU|ExaeVR~SYE3U~Q}*$Im9%DU%% zQxEi>Frz#4wncOC8JZ_vowqFe!qJuGz>Ic`d!qX)kGI>xjj=o8 zWPSFw!5#Y(y>?K+jQ12`eS^i)7im?AB^9pF0ne$$a0Bngz$hj?G)9RHU`p z0b0J=mFn7=9xI`>eHw=;ZZt`t5gmqDq35T8AsH%EhID&dc_@>g!sN|v3~}`+e#*yp z{<{a?uC_r&h%Cu?(`X_x*pE~E?mnC;f!iMC!wV@(B{ugATvTT)ia!tuNVgTBh?skN z7n<9r^xz;=3y$utYfPrzcB6QVQti1O0Is<`uBO+>I3~A^Kn=efN$BwK5@_nC#`MIE z82j!c2HgDfz0W4fHG^sjDdSrPItK;GQLO%ePzOIyB!Z>O2INUdM91J=o=c_u%yle5TS1& zwyAHI`P*WLaWTuR!MfF*PM9hwIZQi-LyCGmh%SRa)W8dIQYXbgi)1qVaO`Q+$+rmQ?i75 z%R7f^NZPCmywjRbVB<{*i#kY;FQGUGanuzyqz>G#xoX2aRc!e5))Y1XZ)gMw6$c#L zs9X9dBcG@ek$ISHJlP4xL{dATaf0>VEJ}eS=$4|+A}1yGHHgZUy%oar+h01XrYQ5; z+tj5yl=XZIw<*lvg2z8I?`Y?zII~osvCFmdqo1`rB1Fh%i9_E}haE=-*RhOY^}mf{ z_h90{hv2RU96DW4mv{m)M09WjZa zEa>goi0H$92vssb)1><*8$3-byK;0sah+@zpeu3JMz4hpSCWqr;EM}(y)-NqtYFI~7Vd#ku10TLrLd8(A4L>t>y0QuxOah4K!PIaM2hisNp`>k` zXeJ_bu*C*-JJR2FFPV!`9s}zpC3P!#9jAt|Ar_`ZFd^>m6_0}Wx|qCe`7r$7DpGoV z9wK25+}uR4$(0U?5H{C$j}dY)oK%StMR$l{L&hW)d0+HM&FfaGC4b-bZ7d4Z#(A%e zhH=7JNj+#u0m}rK{@93B)Mvy{a;2Mop!_Wn=zub9P^8zYY@SAwZ%8%v5EI9x>}*!r zJyj69hG0vba8O)7)*_!rCzsF0WW;kO?fN_R>vGW`p|jqO9_MK@;NrIPU@o6MYRmRP%Kg z$O?1f2UvBOiL1gOzyiij;*@2%rJuzBzb3ym{0H$9fB6Dt0^lK`U+-WQf|`h@spqZt zv(qS{kYCJ$4~ytE9EG>E}!?AXfJM%Tjq7L06`->e}qBk#LObukOJ1=1hrd zU_1jQZ3DYQbr?fmUc9G-Vt;)k!*NKHw`>0V6pM#xMvpHeTOnmiQQv7l)7vF_4;!yx z-8^ZNs>Tp+%_I*c(5H=7sA*4oB@a?zRrY()7e)zM0ApGdjEx!2NaF}EtAw*nY|=U-Ofhg6>PdJ(c^|A2t8Q>W*IX7mBR?T{Ca z^<&(=OIJ8QhDr?`HPRzr_iv10FVix zWr#}%E-0zdVqijin$PTKCd5Il7nepc+t46G^YI?iY_w_+a5`gY9Y<%sUeD->kuF?4 zDBg}c&j`LpCTrHhz+}@627fm|uXwViwj6LK!%w>)o9SjwflX%K*>xx>A>1mmkm>=4 zheT+Vf4bNDZTMpr9UXu11)Ohy0`_xBj?^A=Crd#>j!I2o`zAuT07|9&O+w08q)nc_ znQ9h$ZY{ombbSGe;QB+>PJcJ#1r=VUA@opzPPYsOX5YK^Rjrs3-(eEemCXpQh8TR& zs!47!nWhIe7BA=>mPtsWoA?RM9txlo9~%DH#;Qgs6k&?vERXHH##g53{8tIUH66P&`7sq)S_R zBziMCV^-8=KB2KyOX|#wXuQQdUBO{(RugRX%N7Ra9?V(AB}#Wa;73-!&Tr6a((qbtq&`DQ)S&3^3r9JPQI0R$uVzuJ@WQ@WlG-e;Q5r^*gx5zE!#BE9nqOY-|l( zc=#a8C);7>?DIOVzpUv)y`p|Uq;<=3Lhf1@AR~4Ufq8b%CG^q#8;b$s=Us?|j6mB>f&W7>Z2LjVwEdXf58e*9C$CuL?%LJ0d( z%(P0}*j(3#I)P?B_04=f_N~Fx%hpjHtPH*YA?DKEFTWlOPq%9Zpg&2e=$=>+eEzC)B{PJOFAXhafGG?_0iw-r;JPwV-lI zf94>OkzqxhW{P>}X_x5TogH1kKL~4J`%Y)*H>?9_CK5dL&+?!){^!spzWLzLE|I6$ zhEikzrh1iIJVD;tp=eZ9lpSl=4nB#_OHr@EntmI{?ZJe=zyD*r;*6OW<}BOuF_E!9 zE|S`>`oIKF5#l(EiaM+;yd=Gk{j<2;;yX&cF;Pd4uU+|vCobo)3lp}Tqyba7_Cq!~)~Kkd4HKrQG=B(7iukCwym6&_W{7$LMv+5K<4{f%H_*NA z7w|4MMeuwQtj-Cqsq9h%xlptN-3i0et5$($)Z+VZC5{~B_(-=>h-!}Wy2Or7>^+bo z@I}p3d*9(DBkS+=Um~7I=ZL~_X3tI1o&)Q|6ShpO)5t#a#&5OaReY5z`HlLCx9i#+ zwtPmHVjc^pBuSxce}Nr;kiBP0iCPG*HWpRX_4_FA8YXMWcU(+hG*;SY&P~joP2TyE z-q=`ME48@D2Jt{C((iYAFAUc72uV+8wn)d!|Eef^(8L@a5XzMa{IkyM*j@0S%z$Y_C4d{&Zo& z8K3ate9hp~%x@px(HxWz(#hw(qDPfZF%YShDWEJ{c(mK@H%7^v%a9wE+c~~y4{p-) z&ii668=F*e4&rm^6f~gOE=rpb(4sdy)NcJgZd~FWHFt|~{U8pF=y&oG(Z^OD zt^`(8HKurLrgbx}HE|7-5_kWVXPX&vijHC<5p@Lb^2mt-PzY{LCkd|Ll zU6(@zYEJcoP&$DY8w>gRArgKf0UU^`mFku!K`7p^XYr-)#94ro0SFb|3Qqu;6V1Gf z!XoDz?=rhRpGb5sXp{3xdUk68{|4r@5QI3fk7unRN~l#)m^CdgQTc9y(f23E9qc&!pj4J6F{J*RiBGZ_i%rt-&dCk* zIQQ9L@5QTrCae{orATS2jGuHqSo@<4+-D@TZb-;?o#eTg{%lCI;~;gA`Ol>Fb(LOW zpH@pDGLT@x0M0Asc|MOME+6(b^7rxq5U=Py=K?Y{|8Ke;5x~p5)`eS&mHx!lzZHHx zu-t1_z_oIH+F}Og#|SF6Xp0J8a1+G&H4lkN4(gG*vl2uaEKbfAv@X;og5^nZ(aY(k zccKD#yoq~GkKdH~hH(|2i3SbC%Kapw$f-0R*Z0fU$T;o3cz0;sZ8pUy^CM-XR=2@c zWo4|ok{?zmMGg#%hEU{~RxO0arcN=AN8a@>C!1od)_6ekUM_paW{~IvHLT({Ji9xU z=H=(~bP`k)gGuOc9JKMRKE`X37?mjuO0+qqx7N>cohO#YjRMAI%^IV!BXbq#_+=MR z$m(N-9Gb0IZ|GKR$KBAFK+k@x89tAOe^1UdbW?ml)Z}l?T6q;{bH!e9OMA2HY?BE~ zjhJ;#4ZqR|V(T#($5+}7GIzlR8DC2A(XqArS!zqE3Kec2c{9~Y4)LxM;f3?!Fb& zIiM>CK3)bxVs)JSvJaefE;D)%9~=mot1)JsJo^FsO*nblS`@uA`Pi(5A$rDQ~Y2Hi=oG1 zKxolFLReg{VOPg~EAiMm;$BJ7wY-|}zS3@e-;h)As6KuUa^2B+eAavXUW1HA9GKZ+ zkx;zd%=PxPkMHnoF61F;p6m5ng5fpLCbDIuNX?fcTizG4a5pk0SFjYTpU)9U;_}+S zct5ApE{`x%`mNb1uOgl~>}uyc>rZ_$q5aE^fvK^(iFfwf`v(j@L1B92?Pqu-rw7yJ z^=&ge9cORLYm}P9JTV;hR`#J1HG9nm>pjyG*Z;w;LhD_pd)i`7O#Lq6r*rD&&V=e7 zIkWz8IbV&hC#w&VlV=E)Zu|2GAeLF+vXM0jX-u3AW^0ynnZZ{%Pu;THj1~Ra(j_L6 zWdbepiW>d=F6Y*Ki}g=j!fsyL_&K};TDB!OWtlbI{G8zVxM-g!q__*!6*S^A*1dWr zLfHYd%x=@;Pdci@L(%DTcq24yNJpuly9e%fMd#6a-MhB0zNPJMa}kz&1s)KJK2jW6 zY&i?2F5EM0a+aw~qc&L3rm_f#vgnO>-_aXka42+^u1sV5RzHCgnwFC<{qso|Vbx{Q z+&G6-kn97psB1bcOMa?#x{NC|9vW@AHktQ4ofk~(5(w)*Ws=F2+AAydjEEi=FwK~vJ#fdtzz`U(vU-7ObFkR6 zm!kn8^wb-O{Z7=-UFym*+g58(G(@BSwHVD{f_!V!^sKvEqTZp-YDhW{ zbKMgd3rsYA4r(yGaQ#uGN|Lc_6*3_v->s%-TQTH=^j8M zQl)QMc$DV^`83t7@w2NI_)I1h+fYe!32PWQRW8k)JXt-Tt=8+20Y4%Z0Dm%M{$6z9 z`0T8xg;Co2m~2rwro4tQ%)uL4aPci5`DE@%>F=k_R;4c^UgQksh6j*LvmV!D$7%Oo z)O}ely?DN}+H?uH9!0gWn)-CH<^*i2+*+QDOwg5VB9Cu9*lDthToe!@RYH_6*qQ$+ z-lcvmL~*d)cCmc}38Bg{8Opop!|TPl=t$8G>QYZU-q z#s@2y_{Qw4>2tudG)!Cu4*LQ#63Tr&sARk~xmd$pfzfx3sxp!+3b(I9=mex45iaPetbRmesO# zz?tt9lW7$%GxaId=}seBtKLk;7qaj8GdzbtmaFLlPDLzyOY-)>?z%gnMLi;-u_4ch zBnJ1s;Zar-@pYYYZ%}Kd1jY>y)=U%`3L@J#68(T6Q;Wvu{^A=V`qNocWMlp>X@YSb zyvd*H$+MUUb8f$W;_%91#!Q*a82dwN-hF~gI;gJxD*uY@i`4i91^)I&=ucNiC`IFZ z(*T0#HdWKs&t_VB2QUhZJX((+TXv6Vij6D zRov;^6;9e^pWS^+UzmgCC5kCFr zHVjXGFVUEu`Iea2HMtWn0T==n78Vr&i6-vLssX}aHqbwV?FAd*zc*kgROJ6-6NB8q z!9-yE3c^9~8#uU#@J*a34EmQ^Ah;O#Mj&t)Ao@2D3<7~~ zlmZAM^7mvg2nzl0NdAcf41&RLlmZwG|9gR8Fht}(DFOa3CtxsC^lu5l!XW71DY-6W z(2Ye4i{2y_78CwE?_gmV_}Rt80BbP_0RR91 delta 24 gcmdnm!ML@9al=G=PD5iuBO_B21C!0O>}Rt80BZ0D_W%F@ diff --git a/thesis-evaluation/figures/totalTouchedGates.pdf b/thesis-evaluation/figures/totalTouchedGates.pdf index bd00fdf79a0de7cc69e718290eec975a08483214..43c452932e1bda5e9a7891993fa95a67e7516fe3 100644 GIT binary patch delta 24 fcmcaRnepyq#tn(CoJIylhGvGA#^#%|T#H!%a6Sj> delta 24 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 delta 7514 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

|DzY(`(~5;?{;SIVAV_bYdv z9HPVpNNUblco)_W=r4YWmX(y$g&b(R}B0&Fcm4>Ft1t#xR$Qs?+SK^HONb`*en`Vwf4 z@uCwVH$KPL08zxaqmVGStJQe}*cWN5mR6?!H&-{2S{kBqLJcH-k zcMUoIPAbD`K3_iJYsaCYtcJ#arJdosPE%i?VYg8Yx0|&n_hdqq)S=F_0teR}8@8Jy zx|$6>^7~&-aZ<&dnN@&dd|B@#0NHJiaA1-xSu8kni=sECc_No1GT6PSRpWEU%NxHu z*i^3Vm>3Ib^(r)r8Cec8e7Yz5V)vS1P5N-hslZ9E(n--?B5XEDyYC^W8#_?G6jHZCC3YIn1~4&SrW)V1H7~_3FXFy?9$XtKpGcM(Kc= zbfXoiiJx&69T_weeU>xVY|jXy3dE1WzzdDg*eduxeDafcz?Z|?h%`s-op(&;kW zzWt9@X0Ep!+3Q>$zhWQMj8T*x+kaJuJNyfGx_)STYk${EdXTJpw`-I5tMDph?sT(` zm#;3KB>SWOEzVJ|&eI)B=cB$05$F2XLzz@?VW(I715J2l?sF|dqRw?{2C}(QaVomH zSZ``n>TJM{R|!Xva*xzJ&;PZn9hd`wb*)PssBNaQcBYLaio#hLyx%`0T~7}e5VlhN zOl~Ok%#INDVRE+zHk7(tv~!+*8)vvy??)$)B!N+AeOl$^w$Y_K_x8E*f|}Zl(fb87 z&h{>Yj5VAmc{<&wEu;OKVDW4-0ro0!W+SoB^dh8ihcW4~rN{CehOeS7HnAn!Y$g09 zn#Zi_E5omqwopz(2{LQv3Xjuoc*tiKKG66ue4<&!_LW} zEz|jEUQb`t(I~ph zkOJ)n;J-+K@Ols-f;oZ)fDe@f;RBKBcxovSJ_i+smw~Vlo}tmi11Jb@C^L;WfHM;m zWP<>BFoK2fLXIAQcR+&h>+*4UY7_|HsW^d;MX}(ORD$uWXb@pYB>;eLK!fmgYOVMq zX%Hb?{SE-HAOj+BT!I4dVzMB-jph=*Ne+ZJ)w+v+Cl4a*Y4MTbC3Ij8V2Cr;^}0V8 z;)(Ti2BW3H5H+y0G!UW(#=yW3LofyjhM0h5hgKZ%kh>;uj_u;^!Rb>U7P8Yzvy=b8%Q&`1<9F8?@$A)#k^_$Lp6`L}um0!@tMKjlzR=|9aNg30_T z2SsCu^#+CFh`0aiKNKzVFNIKPxXhpaL#2^t`ThqmR2oAp**|#1f9w8?6BL6)5)=5J zyJz^sg^?lF_Al!Hjgf($W%_LW5099|fALWNtMJzas0`-ciG;#n#OnHoJ{Xh?1%u)f zuL|OzGN?Z-fWcsxKV5>s5N9?0Pc1O%f9DG(bJhj_l!GIXf98Q0AmU_zLS=}O|8)~a z4EdjK!VpmDf0>VxhRNV*%{fs-y$~ZWFMlx6wzEl~?dd{ngTEV2Q4#3pZ}01W)7RMr R2t#3{F+d?9jVqUc{|DX6;Kcv{ From 5e676bf30a84e872e0193066e551e67687c9d450 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 3 Feb 2026 17:29:17 +0100 Subject: [PATCH 178/237] evaluate circuit collection for qiskit, total_evaluate fixes --- thesis-evaluation/qiskit_run.py | 4 ++ thesis-evaluation/total_evaluate.py | 60 +++++++++++++++++++++++++---- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/thesis-evaluation/qiskit_run.py b/thesis-evaluation/qiskit_run.py index a82339dc73..29f23fe4b8 100644 --- a/thesis-evaluation/qiskit_run.py +++ b/thesis-evaluation/qiskit_run.py @@ -92,7 +92,10 @@ def evaluate(): continue # respect barriers + start_time = time.perf_counter_ns() subcircuits = split_by_barriers(qc) + end_time = time.perf_counter_ns() + collection_time_us = (end_time - start_time) / 1000 decomposition_times_1q = [] decomposition_times_2q = [] @@ -151,6 +154,7 @@ def evaluate(): "totalTwoQubitDecompositions": num_two_qubit_decompositions, "totalSingleQubitDecompositions": num_single_qubit_decompositions, "twoQubitCreationTime": creation_time_us, + "timeInCircuitCollection": collection_time_us, } print() diff --git a/thesis-evaluation/total_evaluate.py b/thesis-evaluation/total_evaluate.py index 840baa9333..3477f392d6 100644 --- a/thesis-evaluation/total_evaluate.py +++ b/thesis-evaluation/total_evaluate.py @@ -75,6 +75,10 @@ def define_division_metric( x[new_metric] = x.get(new_metric, []) + [benchmark_name] +aliases_qiskit = { + "successfulSingleQubitDecompositions": "totalSingleQubitDecompositions", + "successfulTwoQubitDecompositions": "totalTwoQubitDecompositions", +} for name in sorted(mqt_results.keys() & qiskit_results.keys()): m = mqt_results[name] q = qiskit_results[name] @@ -84,6 +88,8 @@ def define_division_metric( y1[metric] = y1.get(metric, []) + [m[metric]] if metric in q: y2[metric] = y2.get(metric, []) + [q[metric]] + elif metric in aliases_qiskit: + y2[metric] = y2.get(metric, []) + [q[aliases_qiskit[metric]]] x[metric] = x.get(metric, []) + [name] @@ -114,16 +120,24 @@ def define_division_metric( "totalTwoQubitDecompositions": "Total Number of Two-Qubit Decompositions", "timePerTwoQubitDecomposition": "Time / Two-Qubit Decomposition [µs]", "timePerSingleQubitDecomposition": "Time / Single-Qubit Decomposition [µs]", - "twoQubitCreationTime": "Time for Creation of Two-Qubit Basis Decomposers [µs]" + "twoQubitCreationTime": "Time for Creation of Two-Qubit Basis Decomposers [µs]", } legend_positions = { "totalTouchedGates": "upper left", - "timeInCircuitCollection": "upper left", + "timeInCircuitCollection": "upper right", "timePerSingleQubitDecomposition": "upper left", "timePerTwoQubitDecomposition": "lower right", "totalTouchedGates": "upper left", "twoQubitCreationTime": "lower right", } +pruneFunctions = { + "timeInCircuitCollection": lambda value: np.isclose(value, 0), + "timeInSingleQubitDecomposition": lambda value: np.isclose(value, 0), + "timeInTwoQubitDecomposition": lambda value: np.isclose(value, 0), + "timePerTwoQubitDecomposition": lambda value: np.isclose(value, 0), + "timePerSingleQubitDecomposition": lambda value: np.isclose(value, 0), + "twoQubitCreationTime": lambda value: np.isclose(value, 0), +} # modifications = { # "subCircuitComplexityChange": lambda value: -1.0 * value, # } @@ -151,8 +165,15 @@ def define_division_metric( y2i = y2[metric][i - num_erased_y] else: y2i = None - if (not y1i and not y2i) or ( - y1i and y2i and math.isnan(y1i) and math.isnan(y2i) + if ( + (y1i == None and y2i == None) + or (y1i != None and y2i != None and math.isnan(y1i) and math.isnan(y2i)) + or ( + y1i != None + and y2i != None + and pruneFunctions.get(metric, lambda _: False)(y1i) + and pruneFunctions.get(metric, lambda _: False)(y2i) + ) ): x_values.pop(i - num_erased_y) if metric in y1: @@ -160,10 +181,18 @@ def define_division_metric( if metric in y2: y2[metric].pop(i - num_erased_y) num_erased_y += 1 - elif not y1i or math.isnan(y1i): + elif ( + y1i == None + or math.isnan(y1i) + or pruneFunctions.get(metric, lambda _: False)(y1i) + ): scale1.append(0) scale2.append(DEFAULT_POINT_SIZE) - elif not y2i or math.isnan(y2i): + elif ( + y2i == None + or math.isnan(y2i) + or pruneFunctions.get(metric, lambda _: False)(y2i) + ): scale1.append(DEFAULT_POINT_SIZE) scale2.append(0) else: @@ -218,5 +247,20 @@ def define_division_metric( print("UNKNOWN") for metric in x.keys(): - print(f"Average MQT {metric}:", np.average(y1[metric]) if metric in y1 else "-") - print(f"Average Qiskit {metric}:", np.average(y2[metric]) if metric in y2 else "-") + print() + print( + f"Average MQT {metric}:", + np.average(np.ma.masked_invalid(y1[metric])) if metric in y1 else "-", + ) + print( + f"Average Qiskit {metric}:", + np.average(np.ma.masked_invalid(y2[metric])) if metric in y2 else "-", + ) + print( + f"Median MQT {metric}:", + np.median(np.ma.masked_invalid(y1[metric])) if metric in y1 else "-", + ) + print( + f"Median Qiskit {metric}:", + np.median(np.ma.masked_invalid(y2[metric])) if metric in y2 else "-", + ) From 2ddfc6cad2bef4ac19d614fa36bde3efde8d9e66 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 3 Feb 2026 17:39:32 +0100 Subject: [PATCH 179/237] update evaluation results --- thesis-evaluation/evaluate_cache.json | 2 +- .../figures/numberOfTwoQubitCreations.pdf | Bin 16543 -> 16543 bytes .../figures/subCircuitComplexityChange.pdf | Bin 20391 -> 21030 bytes .../successfulSingleQubitDecompositions.pdf | Bin 14784 -> 18729 bytes .../successfulTwoQubitDecompositions.pdf | Bin 14819 -> 19324 bytes .../figures/timeInCircuitCollection.pdf | Bin 18600 -> 22681 bytes .../timeInSingleQubitDecomposition.pdf | Bin 18967 -> 18996 bytes .../figures/timeInTwoQubitDecomposition.pdf | Bin 19312 -> 19323 bytes .../timePerSingleQubitDecomposition.pdf | Bin 18108 -> 17857 bytes .../figures/timePerTwoQubitDecomposition.pdf | Bin 18237 -> 18236 bytes .../figures/totalCircuitCollections.pdf | Bin 17475 -> 17475 bytes .../totalSingleQubitDecompositions.pdf | Bin 17461 -> 19814 bytes .../figures/totalTouchedGates.pdf | Bin 18909 -> 18909 bytes .../figures/totalTwoQubitDecompositions.pdf | Bin 16986 -> 19402 bytes .../figures/twoQubitCreationTime.pdf | Bin 22875 -> 22947 bytes 15 files changed, 1 insertion(+), 1 deletion(-) diff --git a/thesis-evaluation/evaluate_cache.json b/thesis-evaluation/evaluate_cache.json index a340bb90f0..92f5381da9 100644 --- a/thesis-evaluation/evaluate_cache.json +++ b/thesis-evaluation/evaluate_cache.json @@ -1 +1 @@ -{"mqt": {"modular_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 14.85, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 817.4499999999999}, "bmw_quark_cardinality_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 38.44999999999999, "timeInSingleQubitDecomposition": 8.300000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 13.999999999999995, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 11.999999999999996, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 819.3500000000001}, "draper_qft_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 22.1, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 721.3000000000001, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 817.45}, "vqe_two_local_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 280.54999999999995, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12609.250000000002, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 775.8}, "wstate_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -8.000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 30.249999999999993, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1362.4, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 805.25}, "bmw_quark_copula_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 148.05, "timeInSingleQubitDecomposition": 48.9, "timeInTwoQubitDecomposition": 806.5500000000001, "totalCircuitCollections": 32.999999999999986, "totalSingleQubitDecompositions": 8.000000000000002, "totalTouchedGates": 50.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 795.3}, "qaoa_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 11.849999999999996, "timeInSingleQubitDecomposition": 6.9499999999999975, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 788.3999999999999}, "qftentangled_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -13.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 33.99999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1859.0000000000007, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 815.0}, "wstate_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.900000000000002, "timeInSingleQubitDecomposition": 6.099999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 788.65}, "ghz_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 19.699999999999992, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 808.9000000000001, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 819.1}, "qpeinexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -16.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 23.749999999999993, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1349.45, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 789.5}, "vqe_real_amp_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 14.699999999999998, "timeInSingleQubitDecomposition": 6.149999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 811.55}, "vqe_su2_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 5.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 20.85, "timeInSingleQubitDecomposition": 9.149999999999997, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 785.35}, "qft_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 9.350000000000001, "timeInSingleQubitDecomposition": 6.649999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 807.7499999999999}, "bmw_quark_cardinality_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -55.99999999999998, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 2.999999999999999, "timeInCircuitCollection": 154.14999999999998, "timeInSingleQubitDecomposition": 26.8, "timeInTwoQubitDecomposition": 4105.85, "totalCircuitCollections": 47.999999999999986, "totalSingleQubitDecompositions": 4.000000000000001, "totalTouchedGates": 50.999999999999986, "totalTwoQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 778.8}, "bv_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 5.999999999999998, "timeInSingleQubitDecomposition": 5.999999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 787.6999999999999}, "ghz_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 9.3, "timeInSingleQubitDecomposition": 6.399999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 784.1500000000001}, "vqe_su2_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 617.9, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 22416.35, "totalCircuitCollections": 20.0, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 198.00000000000006, "totalTwoQubitDecompositions": 15.0, "twoQubitCreationTime": 790.5999999999999}, "qnn_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 40.300000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1006.8, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 13.000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 805.25}, "qpeexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 23.249999999999996, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 792.6499999999999, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 790.5999999999999}, "qnn_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 17.75, "timeInSingleQubitDecomposition": 8.3, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 784.45}, "vqe_real_amp_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 278.65000000000003, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12623.799999999997, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 811.5999999999999}, "qftentangled_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 10.15, "timeInSingleQubitDecomposition": 3.999999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 786.0500000000001}, "bv_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 2.0000000000000004, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 13.200000000000003, "timeInSingleQubitDecomposition": 8.300000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 2.0000000000000004, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 790.9500000000002}, "randomcircuit_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 11.200000000000001, "timeInSingleQubitDecomposition": 7.700000000000001, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 794.95}, "qft_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -23.999999999999993, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 24.799999999999997, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1866.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 795.6999999999999}, "qaoa_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -6.999999999999997, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 25.950000000000006, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1508.2, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 783.8}}, "qiskit": {"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 209.6128, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1419.5159500000004, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1343.9643499999995, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1351.9793, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 7665.386049999999, "subCircuitComplexityChange": -75.0, "totalTwoQubitDecompositions": 5.999999999999998, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 3760.08035, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1407.0931999999998, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 169.70975, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1500.8784500000002, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1378.24565, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1358.2719, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1285.2263500000001, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1341.5559, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1375.4873999999998, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 164.0675, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1406.1616999999999, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 161.11284999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 162.2279, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1225.2042, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1288.63685, "subCircuitComplexityChange": 15.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1383.5497999999998, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1403.6088000000002, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 164.67900000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 482.5216, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 1830.4915999999998}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 161.5916, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 160.09644999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 149.87760000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1410.09395, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1356.1121, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 162.5001, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 161.91225000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1830.4915999999998}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1320.5394499999998, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1830.4915999999998}}} \ No newline at end of file +{"mqt": {"modular_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 14.95, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 815.4999999999998}, "bmw_quark_cardinality_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 39.49999999999999, "timeInSingleQubitDecomposition": 8.250000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 13.999999999999995, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 11.999999999999996, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 839.1}, "draper_qft_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 22.699999999999992, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 755.4999999999999, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 822.0499999999998}, "vqe_two_local_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 291.70000000000005, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12806.850000000002, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 803.05}, "wstate_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -8.000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 29.499999999999996, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1395.1999999999998, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 782.05}, "bmw_quark_copula_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 149.25, "timeInSingleQubitDecomposition": 48.80000000000001, "timeInTwoQubitDecomposition": 829.85, "totalCircuitCollections": 32.999999999999986, "totalSingleQubitDecompositions": 8.000000000000002, "totalTouchedGates": 50.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 815.4499999999999}, "qaoa_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 11.600000000000001, "timeInSingleQubitDecomposition": 7.299999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 807.8500000000001}, "qftentangled_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -13.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 33.99999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1859.0000000000007, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 815.0}, "wstate_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.750000000000001, "timeInSingleQubitDecomposition": 6.049999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 795.2000000000002}, "ghz_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 22.699999999999992, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 831.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 800.55}, "qpeinexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -16.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 23.249999999999993, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1359.8000000000002, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 815.7}, "vqe_real_amp_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 14.799999999999999, "timeInSingleQubitDecomposition": 6.1999999999999975, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 807.55}, "vqe_su2_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 5.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 21.3, "timeInSingleQubitDecomposition": 7.95, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 823.3499999999999}, "qft_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 9.399999999999999, "timeInSingleQubitDecomposition": 6.749999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 811.0999999999999}, "bmw_quark_cardinality_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -55.99999999999998, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 2.999999999999999, "timeInCircuitCollection": 153.75, "timeInSingleQubitDecomposition": 27.250000000000007, "timeInTwoQubitDecomposition": 4109.2, "totalCircuitCollections": 47.999999999999986, "totalSingleQubitDecompositions": 4.000000000000001, "totalTouchedGates": 50.999999999999986, "totalTwoQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 796.4499999999999}, "bv_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 5.749999999999999, "timeInSingleQubitDecomposition": 6.049999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 806.4499999999999}, "ghz_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.5, "timeInSingleQubitDecomposition": 6.399999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 806.6500000000002}, "vqe_su2_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 626.4999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 22723.0, "totalCircuitCollections": 20.0, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 198.00000000000006, "totalTwoQubitDecompositions": 15.0, "twoQubitCreationTime": 825.6999999999999}, "qnn_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 39.550000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1033.7, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 13.000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 802.3499999999999}, "qpeexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 23.449999999999992, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 809.9, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 808.5000000000002}, "qnn_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 17.249999999999996, "timeInSingleQubitDecomposition": 7.800000000000001, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 786.6}, "vqe_real_amp_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 285.54999999999995, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12790.199999999999, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 806.9499999999999}, "qftentangled_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 10.099999999999998, "timeInSingleQubitDecomposition": 3.099999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 812.8000000000002}, "bv_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 2.0000000000000004, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 12.65, "timeInSingleQubitDecomposition": 8.500000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 2.0000000000000004, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 793.25}, "randomcircuit_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 10.4, "timeInSingleQubitDecomposition": 7.6499999999999995, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 794.25}, "qft_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -23.999999999999993, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 24.449999999999996, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1888.9499999999998, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 793.1999999999999}, "qaoa_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -6.999999999999997, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 27.500000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1481.3, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 801.7500000000001}}, "qiskit": {"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 220.62315, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 530.834}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1449.0686999999998, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 664.06875}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1400.6623, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 753.1374999999999}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1403.9076999999997, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 302.8408}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 7828.1339, "subCircuitComplexityChange": -75.0, "totalTwoQubitDecompositions": 5.999999999999998, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 2283.7117499999995}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 3831.9234500000002, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1648.71505}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1465.3955999999998, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1092.5579}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 187.1023, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 393.38485}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1550.9868500000002, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1698.9834500000002}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1430.4808500000001, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 552.24995}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1362.3799499999998, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 570.4323499999999}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1306.5448999999999, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 385.82995000000005}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1374.2908000000002, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 516.0969000000001}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1427.7414, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1202.9632000000001}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 180.8468, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 312.29975}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1457.9090999999999, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1094.43475}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 180.33175, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 282.276}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 169.9849, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 817.0377500000002}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1306.34635, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 336.2191000000001}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1333.5626999999997, "subCircuitComplexityChange": 15.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 712.1571000000001}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1426.60265, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 601.4511999999999}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1457.3285500000004, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 622.4298}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 175.84595, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 653.8033499999999}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 504.0352500000001, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1364.66555}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 165.24959999999996, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 163.43635000000003}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 165.68475, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 284.16045}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 154.48034999999996, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 365.45809999999994}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1490.2478999999998, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 576.6129999999999}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1428.98125, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 606.5438499999999}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 173.56869999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 284.78909999999996}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 169.89490000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 372.2458499999999}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1383.5119000000002, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 552.30705}}} \ No newline at end of file diff --git a/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf b/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf index dd136a0642f3b41fd38ca1b7cebe9f3fa644338b..d7f60e74f45b87e9fc2e2276d1b09987f43273ca 100644 GIT binary patch delta 22 ecmbQ=$T+`|al>32c4I?xV+%vG%}Z>4GXnrxwg=Gw delta 22 ecmbQ=$T+`|al>32b|XVGLrY`x%}Z>4GXnrxst3^k diff --git a/thesis-evaluation/figures/subCircuitComplexityChange.pdf b/thesis-evaluation/figures/subCircuitComplexityChange.pdf index 3a9f799e997edf0adc569c41ef92b4aba8e2a476..8ec6d6ad0bd8f8ed3f75f1512e8f2bab2d6febe7 100644 GIT binary patch delta 9025 zcmZvBby(Ej^DkWzqNH>;>@FQ5NQZ#H(zSF6f`sHEQj*fJA|Tzp)Y1w|igb%e3G9;6 z0vA8ud%yR0Kiu=jKJRCqIcHw;nmIdX&MD5pEGodPNyUWLvp0FPjaFymzqS(UcoJXW zC|p6nqzXj6{fyt|uOu|W!I6}Ng6{GE7U3<&7Y-x>qK1;K3!C7N!z*xfxN{Saz6YM{ z3hga-Tpk{2S9e}rt;b!X(c09|D=KJ5OY2V4+0jgSXK?(JWwXU&8{ey!?*!btk+AjZ zvyMN3*F)<` z<5UW2a0>-hQ$bmhZ#?cCk&|p6^yd6kJ!C$J*W8Diw->d-A7q^Z(ni7;lRG=w3mT`s zYw&(vycd$@vORCK@N}5R*9N7c_cC|mmlz;>zeFkyazty(j(4AppzN#nd0S|l(<@*q zMKy)NWK9Z|Xb)zVWV$+F^rA8%%|+OP!Kx)YPwD>Uv}f#D=C?j2Baaa|vsxvscZRrD z7joTDU8s{0L`j~R2(T;LZss`$OX#?C88J;`;bCKM8bLCevAFD-9x~4I>ZD|uwo^6r zPCBd3n2fz7mVZy#8IFfCuXx;&HbmK&pBZ0DE|&&_Nzc0Fjexso@l54?l9 z{VIb!O}5CS0b&9j{i6&?3Z1xx9pNjV{oX=V$UJ`y;5100v>6#!shkB`=8Lc~B&o69 z498d42@2~zrx;7*)o^3F4k0B>@i!kSYLoo}vtOND$7W~PI-%<-Ojn|4PXeH@{* z3V1=vHeM`HIE#f5UJ0R4iB-vInhoR)t?a8bAbu9B$}T3dD}@Ih@7qp;lrPaNj8Q=8 zOC*~~lD}hiNRb2&$-=W~5+UEk5t>mb5JtMD@-0YU1`!JUnm!dWxlPoAXh0?^3 zNPcKEazT++MW%>Z&5?mDZT>lI66%C{)B6Elz?z0Km#*Z<7#c&S44dduL#9Nry?d!7 z!4`xVrftJ z@=xf{73y|d-(??2S}i?5_p^Bs8%U_8m14uRI?$9*~bp=Ot`lPkh=%csbj;&CVyMmyf_!XPM0;= z{u35w4Oa0S4De7UXjk#LZ?n%yU&|=qyw<1APkSj};vthM4k-CR{A;e5-l_m2m6VZ> zz)QpEP@!JCnB?QSySDad3C`0;EyG>+iK1N@X&Ov>HyPirq)pb^6lUr`U*-U8(egw> z`~ya@^a+?*0Gu5o8EN(G=Q;lTu$DXuBG=Qi+CqKLIl~{!3*1I_^^^rNk6gUfUg$r| zm^3|ZkCgE=_9DPxSwYURZumU|`nU+F1pIu9>aR2c7RYXn7oG&@Bo{hi-R0l z$X%rBNbqSygeo3xjma8uO0p4y~7x=jG5wJWWx@b)Jc9Y>fwn zdxa0d`oWrcy4IW98Wk!5^;$M;Q(m^&>r4StPZNPJ!D=CJjP+S4R0-Po^Bm_Pkg&d7O5wk<}*W7llXFgY}gD;X0yov*Y~md^h$vU0`I{S=5c znn+~+lgglhdEYFlMFa5OpLhB^sOX_1d)c@=|3y01U#Oo94p=6rWCAWl%5Y~cUc+Q; z@C_0luF|T!ud9f#*VzPzoMY1Y4KPS@W)ntiQDQv{QpJqmV0iPZIgSC<`Z(#4Y$z6? zFs+S2gcei@^G5(-I69GX^f@c;75Kh z{MDvhtY7k6xtbo!kYz;1tj2=5*Kf7CpXH^34i^m+SBaocoe0m%ILvTbGlkM+EtPfmA_i!#FLm5GxP3Yho>x4^xY$)5nF)x0!t~(@|E9O*y!)Hy!buTN{!WcEAn%nLY zL(tHW=z3e)PQKvJ@ufL8D+{A&1BdLzjp?D>csh2wd^n$!VQ+G3vvtB!S@mTnd1ZBQ ztd@x`MkcsD5}*G~#n%|;wzf=tW3Br3vm$hpFK8hFT`Hz^$n1AcJ&+5f=dXmE!|VoB zSY%Je*o@294zQ9FJlF?530C)J?W@6vX-lM;uvE2h|Fmlsu)Y29Jk}~l_^Za|c_`G# zon-qt4&Byb=m?TF@<1vu(tLSw&qreHb zU;dJq+XO8BFK;oL;~h+@n#kEyRqT zC!&`kL=hC#&jjuyQcY9AR6i&v|)mV^E z&d^l*&<+~$7-iAv5coQ!?=U@{s0zPvh@uNix=e7+#URDh8^zEjG3Q%Yg?t`8K!0fT zXyUphUJCeKOibnNkHP%+d($Qf8$L`E-^EbG^Rkj;$XRdoGKLnbysqpCh##X8k3wJ7 z#DE*Wg=CxJ3J_=!D)H~^Lm^V(a<>T?;-RL(F6G9CkMK@7nAOJS8X)xA6QnaN?N_s9&~~XWwf=M3 zu4((8P>0 z$Vs<{DkF8>^3Uy&H&y}Dhv=<^lP9^hJ$1EAgX0i+xw-YVW0N2GxVI&98yHqQ{wchc ziHja(M5aK4$Mg{Zb$sl&g29Gc;NzNavc5IflGyjxO*`mgTZQ)QJYr1zWI)^XgXXR7cijC6 zyJTOyjz^=j*z2)4)Y=j?_)mc-ckma?){38eWc}P4+q1RfngGq>`;Oc4L^T3ij*7?^ zYh5WWp2csW^es&D>#Uac(qjCcR6LZ_4A{B#Iv!r7O~aq$gRCdcPJS^ixR7j!0t->(6T_23M|`%A+!#Z1oRe&Vr<5@gr*p=}WUz={%)_P~#(4`b^difL^@Qh7OR-xH4MPo=w8 zV%boqp&u~O1_v$pw#|QQxZ~}4Mvnk0a?JeqK(pAn{Ka11`sA{Rto|Dso6E|@01yXS zV5>9eev%r5e?I^<&OdG;X!XlSp4Zw^LXK6#8lQf(<0$d76d_MRzC6$V`kzud8wrz# zWX_Cxj#)D57PSIBg6g;X&FWq%J@5LMWf+aAU`sxGyT%wtoK~q$KCy*ltQ%C8>k?q{ zjzfRd@VmjWL0?tG^RsTmtM67PF~9!62s&Bg+Ic&}x_?Jt6;6#n$y9Bk6KT@WXgr5zvEo^66 zRO6lI9}o)<5Q-Gy5WOGAAA=-8N#;*H&5?`T@m&c6i)e6kR1m(HJMd#NBT6d;cOaLv z9OE;#SWl9)r>4w9;t4(+*rg6Ov4#}Y0O!DVWcYQPCPde&{&|~uQ4(tS(Bh>NLd{_1 zm-cCvjWhS6xqwfmx;fqC{@kE#qIo6%jtNqNKdMzZI6;FfKC+7Uh=b4KT6ABIc1Cg z^Y5z!)FadVdmOM{qp+>sFrT#?3V(F?`Wa<8y==afqiX`#$MFS)S&hLMR_+q73Z`9( zso*Cmp`f<=-MhCffyHHTI<~pF%+pZsMkCvY%>FnwFQ6&`hy*A6cS&!mQ8c)~MpnWW z7$o<#+w5rUi~{kz%~6PM^*b_>Z!RHX7`g-NAEST{B51-)_@m`FJU#qzVW}+E-qT2QAz6?2+JV_KzGm2KE68bkvYu zQEZQ1D(Qdv!Z)FR`@%obSlSd>aD?0p2UMnf3A{7etL`YM2GhqBkmz!IIaBtYk(()BSBBj1(vOAikrD-Gcc5U6w2sbBH}!u*&I4qHD=YrijspYJNfz@Kv@&Z zVQQr1P6-9|S!`E8xo zVt9UOD;16i3$bf__`*C4JVQK5X^QeX9Go}b#-84Fh9>&>Ds^AQ;EubGFSt+dYSpV- zAH#V{9nKl}cwQWb^YeIamYdd%xmc7RGPsA`T3=?k#bF_m?Bm;!0<}k1DHqI}`?n0u zn+K&()NZFt)=$dvz2KVmVbU|jTc#38RQaiAD!$04yUjJeE7)V%|4=pO-Ec(Q%ryxM z{|m76kkvZCqRPh^d2>&>3m4jPmTjjuX?>{Bk= znhi{WDwE6HQ_5#5Dog6^tH*e}mE_>cZ^S{YyyP!43{^Ms9&R;O?&dY^rKcJT7bS-% zJ@VrSCVaa^@^m<;c&vqz@|1G7+~@Qkb3s$PkFsyb;0RVB1=`IcHH3zbZcjA)lOb$u zmvqG>o+GOPk+;G9t7v%MJTcfR`pmHb@s**H7F|UU+GQp>Z>}HytSZYCEvV~q?-1+K z84g7x!8M|~oqazlJK&TP^^}=zel7?DBP%}G(NxrdzuA#n7z;l)s3U>%B zd&J=T+gZQO+1C<|=*;#p^aPVMB`ORW!0U;q3~Ui&f=>49ijrw`>bkQV2Sv7Fiq2+! zds)hRv~g9^^U_Yf#FqQ7NeSVIalc2Kq$1m3-FA2ffi!p{EN zUw1d@qhd`S?4@9ULkhw=Rf_Lp3gR%djb#&@zes{RAQJf!bdoR0wn)i9bkYEUjEHS|IRMl6dt+#kFLLz;|Rq4|^yZ40Q1*=}3Ei2@bi& zqzhJJIOj+s4BvX#qxo3%AeDnIeD?Jex?|f1%CU62$CK((Mf?gvIj$I;?BnJWC>je$ z*afNv2?qLM3Lx#EJTNwsuJSaLw0=J^wU0O177S8KR|RP>tInyDB@?C5C)=k%jKa~F zQi44(E}TE3MGpsY`0y4OqBEqm^Y8KTQq61V+8@ry)K!4d{BNfAh-|z0q4mr|F2?}` z#_!6PjU6j~<3_sXSVlPFbRW4qj0?=@upNtv+cX3{f`D*UpMozU6xg6Hg~e)E8rf<9 zmzVF`44(ggd-n?thEvP4{&~#`m?yty@d8*n#%jGW{Bxay7s9*Xhgb1JC7T^0*^N3U zX6`iE&G4(k$wq}+(_l0`VhU~T8|~d>yj10?6{P9gYCe~3zo*^0RJKA+>kMx-hX$K) z)U0LV)Z^g~xDa|7_Zl+3fBv7lDagzJePHF^;#NeKyPblYF3T2zrqI_nzkJqzR5(z7 zZ8fh;CNLmfR_wC#Lacr$2rRp`z@?JJlI?}?WW^lUZZ)TlzlAb0FyKyT(6qBgVs~WF z#Ag{Y=ZTm=vlihnLT^=6R!Viil?F{i+T}QDkISv5f9m)NyzwbNTf_z}CQ12Jh}@yqKp?sDbA`a&?j3s_pi>bQ#Lp8dH!?JfC_9`Sh`55j84am8(( zxP7KY9{tREBR_+pY4|>{it+ewqw(TN8hm_${SC(`dj?cgLe)Z}Yg~Hx2FGR!sdz~qMJ$9G%VldWi+Wr&TG&v+V@Bow6LybY26UG&*SFHK1 z2;Tu3Qq*KCd`d&aTbGP?>B4sxAJOok*}n2F*KPvhH+R4M z@^KQf@j|@Xy#1kI-tuwcf_Nc*H*EV10JnTdh?8b`f3-ymJz-`ODz!nX3-9=}=&3f9 zKkjc`;-EcU;LRWQGChzzkzU$upj)a^;f1=fWO*TUi`NZyZzJd(HyoJ%<=LT_;B>N^ zI1~D#7lIi$I0=mZ&_j6JJo#+f3{Y}E$8!fK82prJ@|1b^oL`o z*}Uo?(M-%78x)Q4sv?)b^SK*kC(fHrmE1CrvfOkr zf#PNC*CP&6@Qx50!6}qLzu^l({{_VxdLt>?u366C>C9251VvI2m?Z~JmrF5#wt{c% z{P51j;8yn>eX7`y5p7`9H|$jG2R!zUfrL?Q7@ua2NeDns@K@HwCk%e~etUfbim2-X z2!+rQI0waJ@R=(2I6>0YuRvj~A-RU{NbqiTS@6LFEZr@-7ElqM1+1{|YNE{jf9P>Q z3Zx~!q^2P^5AFwHJ{6J#;lX7ibrknwq{P`cIvuSql!6N`J@IfdbMi|(`T8I^$xr>p ze*cc4cM{AaCjvcD8B=UG;`nfWme;aE?WenU2q37F?FCc~6jrHojo3AUATQ)fjwK_wFA~_R z4PyfS+Ok!1m;LLnO5!?0Br;fzw~P9d(VE5)Rh-WIbIE8{@d+1XkzR+^DhD?eq2BT^ z&HY;zJ`IX)>eso3T8cfPK2M52`)7f%HNYq|zkoq$_nvd9QqS6iHCFdVF>_w>W%|f~ zQqmcSA||Yq>pfnWNsi6F=LaE(W^Re!7zIg;mzs+4KO_*;86VfV^BgNBi|{9LGQ|M? zazMVGNA4rX=;9wh9<4N9Akg@aV|9zB^5*Hfo7jVg!-D*R{9!q{;)icG7g^J^eN30 zF3U_!$U2A{L5-o+2^IWd(#Tz{&u;~n#o*_X;t5N~AnoL}(lvcxRabS&d1&tseHlpm z?0i9sui==p&`f58p{~k=TFdQJ!6fLQj;gBjb0ZK=RFFv0qwhfd??h|}9SGedQoB=lji{>&U$^mh^mu?}14tLd}=nF}7H87LUHgm@xRx#YW3k+lhBl(Z-omQPTSSfA$sUS` zcC5=y{#kmB`@x)Xk8U7x^!NT4J*}{Gwmr$3w9RyGspwCK-UD>o(aF?X5ci*hskfJw zw=bY3W6$z%=FC&LrI4oLp8!d4I1Qpr^jR(OQrsW^p0!KpnLRveAS*(>xW95Z8`0VB z7uWwYZ;QvXyeL{-zCz_~Ugp*+-;e&;_-P+9d=JlhZ(#-a{bv~UXe=$v;x4hxu z^{yuQOf%%9e@|icSJ~g|ZV#wUG!ydF_uw<-C5O0|_?iU8`EK*4aUkG5#AZNYLLv7v zYg>&yYX#JZty&QP0W8T!l^$`o*5&2hr)+v7N;Z~||4MLZoIc5uesSpthRW$Yi$1!% zI<*@l+Xw&=!py#DAGoz?3H!>%7E0lly)Ma3>b0*3<&`$8UmWTO1~6vQPBC)F5710h z0)8_&NR>VJpNl^vKh~5yGgK^)mOh* z&{@m;uc0*3LxYlA;pz%8V2Ic)~z&VF_R|lg2Zsm^hFb@OLn~-9GyM z9Y9H8G2y#1NzpqpQ3=2unHb>idSakk2!F?aZQOo~{j*;3js`Q1gs`M|GMOejR6 z>|dHdaY>-a9ho>l{EiF+yn|XoQtaP)00|M`U70xOFB<6o1Mzn99oUipiGR}okOYa` zSx{0`{H~t(U4Q_98Yi93z} zK~Mnbjv@#safg18FyMcn{tsx7nDAXa3Ba9j3Ijy`%_>k>MED=jxBmY(Lx95KpgZ0P zOGw-?ECRfnnIa;0=ZZ)O|2rf=5lQj?$<{yo14V_!?@%o&B6e3MCU(a=F@X3T2E|0~ uvLz-edS^W`abDn^y(1?1PiEe7{C}B|6c+|28)_>PNk{^SI5?DUzy23ySyHS3 delta 8416 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 delta 3938 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 J#z#%y{{;mkE~o$i diff --git a/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf b/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf index 253f8d968aa9cccc492307be6cee1357d4147fb3..84fbad7fb5254a0f1de115d720795fcd612839c8 100644 GIT binary patch delta 8219 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 Iv!P1-2mFN?rT_o{ diff --git a/thesis-evaluation/figures/timeInCircuitCollection.pdf b/thesis-evaluation/figures/timeInCircuitCollection.pdf index c2edf7e5253fe8df00665b97ba46ac6caddac76a..592f161a0bb501723dc8cf2213b2bd05f882ba93 100644 GIT binary patch delta 11437 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 delta 7366 zcmZu#bwE>X-)3}5BhBa%+1OxgAd&--kd%@}IvrBVK|*PS(H&Ask5(E11wk4Kl@bI& zKv9tTrjPIUy^ntT>vyhmo%{ObxzD|fT2NUcsD=$h2D=DF%0i{E+#n2Kadm{NKW^cj z)##voqA|1SNO4XEl+Kz(hj@r@K#0A6xl{Au^h(;Rv#eW>JxGR-^i#Xh*e zVs&HT^Xb;pvy;UhzmC%no6Bh(0nc#2-Ge!PZlE{sbAs0~J#ZS(yUcNXcuF5{6~4Tz zl9gNAB$9D|958A6B>(-0QpDR1@UvX$g6SB+!j4n zj5M$~nYQ&ldtO=_0C}4aJ`JM`HX4rXIQy}%Yt^3@?`QRbGQyWaXVXUoV)MImc%*Ojb{)NPI{=O5A&+KWYq|D|dXA*~-nsT}}{0 z$xzT|iVA7qpA;?4YjcVm9fiNu_iEBxy#Re6@W9*flDYVeXl&(2wi|!jdzPYs%K|RgzN&+SJOeaW35Id#(nmu7& z$B3qlM>^L?>DBvexorxgalL}e>7_)E%wuye*;G!9?@8Ft-!T}>2pS2PzY7cZcF~%B zwoWR`)$w70l;Mtk*Nq3F<60TzjiX*ox#fM?ClR4H=BO4~Re%cX#QeJGbETSNmRtk% zdP44#I&ws8{m$x)TQnYEcV~|q8|aT4$?eDahW_ZJS=wV_st<)bVT81?N`XETV~cAH zu5vqb6MUa8^-49zy(=N7<#$vbk}iJvPJAVGkzN;h;GURjun%u3e(zBvbj&0rfQmmH zGS#OS=8)036b|TomPt0D@hWGh9WTk0G4L!}+>Xu5dhh7xfTGv^+Pe3lY$wXtz|w(= zZt*f%om;Y~dBb3zVxQOSs6bE*2ilxtyx6V&qR!?Z8~6Qti57yJN`)1x{Ujfo4yO zW1r?37QAarrL_~Gm*KAMQjJkOEQt_;J`CmJC3(6Gaj+M&bK#)TA%~Srxb9!)J5iPl zw_c81Rgb*VB2m}U6{J?>C9=8pa-wp|*N(@|={9`y?GowOR9|uwM?;ap(O&qu>q7@y zMtADpa%_eJRgUQ3#Pu>RHt1Tn_@b$|_0~Z44H77++4F4uk_2CSU$wgXq+Bszk*ae{ zrn*Q-6btdQb?4pG(5G>a9U~!aoPB!D4c4Pe_EZ;yX2R>E3RJmc)!4YSIiT((Z_zyq z0~PGGRcN=93oT98ID76JN|4LfMS7$>*Scwz3!Xy*8I49y-Kp_?`A}#s3*VQ|TT-Gp zDvQQ{?1i(wkKN&#ry%}nM_iNu#>vxAc(|{yJ`BUg+6rb*h;>n@gyFvHJ+T&da!;AJ zQ@oiVt-|zO`xP6BL{t+5-SSf8L)0OQFSPl^41Jq0n!CTD&Sk2ZK#`rivQ&N>QFCIy z7?KXGPw1CU!SB((Ab^4kbtYHM1nhVC#QK?AIG#-G36ryuqE7(3EsmTY#wYC!6ZY#` zE_LU>aOgA{8t}hymee^a^?O|nM;i)~D}}y`U*heixP|65=Gj3Q6?QR zJ9xM7A=ZuMUdkYZ(0ZN1SyH*woXJJfUe*Ud@Y!t$MmBL|Xo^oL2N-YT>sK^=vn`YaZ=nLKHSKe<5rxw;;NVoS=Y)aY$ zeBG7GRIf9RT2Npr(~30P*}>~!-RFnz3YrjG+uTj^u&K|yDPxqI?xA-+EGu^9jhMOg z2y=@d;v0q=R53L;d5Wx`pU@@Bu(S=#po!HGR^u7B8(-BI@MQX0?q8w>jT5p2y>4Lm zk+jkEQb*!Vmg4OXJ{GTP&fT*jR@r2610FN`u|y6N>Yi7^it#$U9V}_qeeQS$Q!oGdOeNnYV zE%9Drt$Gq=QL=Z$Vc-xnh@ghM)t%LL{&JVky+CVfUzaEY*UZjH`w&vHXRNqSW?z9* z!Q`Z;YmawQLyf`J$mZ6J(e#fx#%$zDlu>uV=2+Qm!51fy*4KZ4gS zslsv1_jruc0a^0aqY&}L&A3>a7fAJrB6OwFRcitjk`SpMkHa)Wxrr=GB>5WX;j?vU zQ2s*QL*i@AMK!lnKSh;3+C0s=;CLU=JKy$|=0=W;baAh! zG>tXB=yPFJ!fC5!O4^Z5f>hT#(R*XxCt}8+bnbOmfka;k=<`<@93nIw zA}dKlwUyvYPD)KiuNKh(FZiNFYr#?yOG<9M%Sg~&NFg^HCL!1G@-4B0^*-WGRl0}q?85KPs z`pwzZE*c7EUsk_!LHns_s?-ul{XVbA_Xv8^vKxgLNf8-X!o|orZ-<+ys{7Og2-m2S z*&fnVBf&Y&#svcjXu}XO`cZ;U_AKQlou7L|IaRCBNCW=T=7?Ddcy%GO4pre>YPi6W zza1+bfO?X7;6OtC*j&OuY@3=c2b6m*RESki>^c1sVc>f>Dr4BjrC1*wpCh5ivq~rI z`*65h=oTSD6G5zFKLy!XKUZ;`CZso^vJW%9nfzQYp;51hwRS$qzatTOXDF^A0lFGS zxMaSfqWheb^#im5n3-;gC!^63_cha|VojueSgm*VZK>Mb$pcr{g|Xl$typiHi_Wio z>h1>Nnr$?AvZej{eguOB1S{eAUrv>kiUpV z*}$+{x>}{B)J;fb*~w!Ibm%t%}p`zJi*sF*<5)JaR@+S7~|+ zf#eh8YkJhP8y-Ms>Ff4Xl;?hL3B}{EuHKgvM0JqmcxOSKC(a=LB#@NM?B?q(Pl@zD}bicdF8aQ5>>$P17qbw{!NUnbS@1gH*XROgL(aAEd+t zM}Rl5cWGWv=vGL)%eoe_KAY3jWOZL`7UJ?IO~)LoKmO!0zd7_&)U%wtSCQpQSYpq&OYJ(o|gANy@?{MP?7LE-%)p zbSA_*+h0c`vEm~UoZL+~d27|Av8N^v0MoH(JAgP4X3yI|OT#AEQrzPxE91`m)=8;i zdq)CqQhyZrGM`w7Jjw{}vF}6IJ)LQ)swKs6iL(XAX=|w!kq{OxbA=hr!|Zo;H&p#7 zPC%5}1?-cb;$bTF_>*!9m!9X9=@Uf8M?#MGUh?W!^_{#Kb+Nk?(fzdYwf~FZUY?|EgsZRf^h5@@6s0q9^L&9F_K-1aH^|q0%_^5PXAH z;2ZQmgPtVPcBO@yI*-Mecfe;L@k(Zqrmc;!En8UlkW18Z&QSS6*<7<+BJYC$1~DIn zG;W-b+GG0)-TlUR z4nGEjf=xkT8^*3;f^tNWu+DL%*JMTe;&-WCUCA9db+FwnZmXg!{6y!-{EE0^oT3OK zbcYXA$#sw)x)Xh;L$2#V@@v>fG-bOgG4>gCV-&qbjhNm$SC>xEq{@bi&oFzcWvZJB zeT72pI7o_KuUwhQjpW)3uX6M^k~2vmd~AT1X3T;~s_S7a=b|P{RrY9I;sJh3w_Q5q ze9-7tFBU|UoKwqb0DD68{(5I8z9}2*9SOaK{81VXaufQj(r#(q$^_@e2)~#zsIPYQ zrX%0m*iefWk$SvjLXyVFV^WrkdJ#<2cle}8fwFvYTBcC{!6gVQn^Yj*+RL=E7@$l0 z1LH@FOwM@mBYCz5>70Zn47ZcL_qqyJdlDe=@N_d7BO@d%I#Hc z8KIHXV=fjTLk!AdaH(r5$IRC=v=x@-?R#IEc#x0PtjW6M`urO(3Saj}INq1g_<+iM z1Sg|WJ$7e~RT^3MwReqNq*bE=Ah*VB<{}${rc{`VyIDUdQFb$j9`E;-5g2i(yXSQ~ zQE7K~Mf1P>xfcy-wvnHoYNn(O%)cbg4MOm?c8U+Mn<*$Ljdc3E1V)X!&f>TB&MWRb zDz~0C?!yX<+qvi8S#Ye?Uuvr7b4qbA7~yuCE1&!(B=#ZCCv4l;ep@&A^4KZL;75fzzaG8eIE&qXCk~c ziB9RO#z6;iFx&5YyP!pRb`Az|nk8?mjm zCYzN))7Q#PI$ND9%G&fHa+p)MI_eYFtUZ^QfA@u~&Le6!(bc6gP2FGxO%)e3ui8w7 zpAY3obfX3v4E_8v15a@rFGext)e8aOnSGb0el>1B9{*7X*jwA9dwNYDu2BMtM-1l) z>cgkJQ~SfVwA$n^TAM|UyRdggKI3s`jFq_C!qTFq+)|aJH41Nldffo!$GcB)#Z%Z(Csi#6r+rJe_mbGKF8C!WVr`EtV+ z$77r6p949w2fOpsTkOglo)p~a;;w7RU3R;F8JL%!#E($Db^Ej=0 zpH@)tN^v7k@AENNbU7~>->!*=B1BPIrq^n=?88tp89hVal>#@jZ^@qYesr9Ihy{4v z0o@n`d>Y~p)ynF;rl6e~W|zAPvaRb`FiMr?B>Pk!URUnQJHqOFYAd2uwx(ahW6gt9 z9TQ4F+kZ2TlYFw!0a?`Lv+7o!<#w3Vc(D>$ADez(-z~ea$6BRX&w2KJvk)B`K(?fUN_@}j3ju2f|C7uv>(EuLP;U;I z_7sNF3Bz}h&$uo*w`ZF9xvQ!(`}YofQT_OkZemGzuX@&2OGkLBeq3xz;$h=drbUja zaELF7kV%J`L1i;c2_xj+s%~SIvX3f#@kQItr#d2x0JF9+V1}X{_AcFtG~$u;aiYmS z1!}_!1xM$bHU*`US!dCC>4)HyNhbXH%?%~1P+A^8^XuF}eRZD_Cs&e<@!HOmCszBcB<=md9je?X0y-yTz{}79k~}3qF%*Z#wDeVZr zS?#CTCCPnu=a}S-$FmCEp2jL&*!?V?`|aoZ!3PHiKTehxfSv4byQj3Y?Hh-u9XuVE zf?0xtkGK8Lp7eKg0C$d9HRd@0?t4Y;sv&W`{Wc8+R$87)beGkwJ$Zlt;`kp7pefP1 z5~aL&w=bl`dq~3XQ!y$vl-cKN5#Zs{(?2T&27aCZG@d>+@SXT|5_Mr@|on2;@S z1`qv;B=mdqPGx5$jrBkWlmuhNxLKi^P&BgH@%y}qHPW&;XF6y&%8->anYp=DW zOQh<-aW>f)*RmDYEzemX#h0i%=kES5YWV$-=vKb9RD}CbBd9jJ8cr8~%ubv|xU!p? zTrK?!r1~zThVc#+xq2{7;ZDRgrgD%>!1%Y=ERl~1Q2|VNuG;Ecq^TO?h;awWb0Pg! ztOgY&Uaio3MMUY)b~Njo`n>8@VE%c*&dRh1O4od+$rAw8P^50lJ}2x(vMh6WoxpBECx%t=)@Bh&caKiloCSzp?zH@3z!pI7jP%#!?kt(dZpx*u^DMuzxR?>XOW;QB^e)8qJgUixX6eeRZuybz zmuJCIJO|Sq6F)MXj4_qLVzXs(qxX0O79TKK8tF?G-)`S2$kUYGuyk3b0_{5p1d2*; z_&+|($ccNFHrU$!WoR<(s5Otyf?ePJ^v*ruL#oixC1B66C+7BcgEidV@l{?xD*tDa zFT|J;N%Q7By6o5TsUycP6Fb1^c@mGS<)0jfTOZw?;+p~VUB#OlZ{1mcKiD=aW)g38 zBafNGAZH(EXEpQYi1yBc=G@_-ub?q2AuBpe^xj?d2spcqyO0# z?~9T~0t?;zBA1UAm;L%Z&pvy0gl+LOZN0mjB5RUmb-Rbb;+=2M4%?i;2t(iXQH8xC zHV1;c`oSv?Afe%X==CeE3svPBs>P_J*sE2a9jP^60XN0?_qH6FJhr?(xwchji^P8I z>XF_{pdD5*VArLL8=7|iG+Qd7I}~4?n70mCI2l|K{^%SvB5{!IwB0W5cVc%^V$pb% z!o$Cuz;kHY%rxeY4c2FT_ol@XzBrWP*)+|+o0GFyoZLNP8^<5Kj59e7A#s!&7(1?y zVk#*R1)k)V?>en3yET>V8dW)quBX^?TD@=0@IeoUQNJK6^Km)nz-i)Ttyj|XF(Av? zNLw5hLVtPZNuYEB4!Nx#%(osW+_$^JHn5qKuBLD`elv&lfp6)AdpmjM4y zP>n}JiLrxY$P)DqrVz={LADrNxOt}<42hUlqn!kTkKU@Uc=DXlu0!a|u%aRy#L`Gv z1WrTb94&SiVUK-_V8l7e%7U;)vP`&oqyz|?k7UGtMZLi;q8M@S<@`a|S8|Lv*GqID ztjQ%tY`nq)Y@h-YR`l`|mgO=djzKXTggv{=h%HwN!lo)Q;tVL;SXWqO$+kK42K~AL|wv4*6+O zO5Sp~?cf21Lx1DW{$TiTy<1*3b`A<)_%BhgBnAxst%-5+ac}^`f9qoWJi+i^vXqj( zVEAv@KnEW@EQ`CUZwDfTBe69GVPsNJ87LBlz@iK}KoG34;YDXC0tUe=|NMbHz?8oX zC;}q&|6tO1H~!#B{fz5ROd22IpO`EJyJaZMjCcQEcOWuwDFjy3h#$aP5PS&#x(k(- zmcfVk2L_Y=OA{{pGco_>$;d$ejY@DCp1=Oq1s$^IQNObYQ|8dATWfWlxh{{{ZWr2mc>hLn}YE*dk6{*^O0 zzGwfGN5EvD*efQS_<@(y_w>Yoe~B?-8f`dp*x{z1Q_VdCy

wNFeIrq8Gl95VPpGsAePK8go((5(9X!Ty(v<$r{ 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 delta 6253 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 diff --git a/thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf b/thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf index 2ec485ad49fe7beb0c6a11e1a3936a7da3849c24..64aecaf406c1c06a80f0a7bcd0dc3bf3da862129 100644 GIT binary patch delta 7062 zcmZWs2Q*ym)yNukUZr+RjoQ)eMuS`bUY?k`<-hwj~@6C;OiWK!f>`F8I-OZt)J`ZE+B z)e4W-{;ooK`OrLsfwHh(YckoD)?@T)1*hZV;BbCRIR9d~^G;`3E3Ld$#79GOwZY~q z-^0u$)9!?z=YrY4Lr2OaLGB`3MLRkBColQ2Hzj^g*D5cGzl(2YF_SLE7BaX*-74R&OzT-GZ;Mow zDO``SQhX=FC#Nvst%48!_!4EH<22xr)YkU-QS7za&*)l*rslXWLZ3``#+Nwq%*&Ur z28+qkbTxYyh-H>P-^8oZ5M9iO9*kv91GsAM=IF5__)+L*IGh!Ifc5;+= zn#$Ma7xeC@igs+|a?`U6b(=y8TKcF&c4cgEOldOrRF9iHY?-PPYq_qm;Jizh*TQ5X ztfnE`jD~t9X22Z0v-(S&n2 z+Z5^$;&sh#8dC8l&`%O_%UYGtH< zd$ej{6!w+0rb1+nZpV{HIkU?BtybomMaY2)ke z0}b5cR9G|J*6_LesRw86!}GK;Ce>xUFN<=~3i-)3J~I?&L>tpJi%_!>PR4I;)&`b# z4YRkOw1l7XQF}hk2j(@!NF`sIO2t23;&)k(+5M@zf#)!z#n6urI$Jc`-~7(AZP{1) zBcWUIfy1j+UL5TepC5#emlv3`qBb3$*Pt2go4W7f=`~uyl`<+YktW{^-I_osuwh!v zD>9>fpM2L#{eMJq54wV(4R0F#o}DQkrb3HDXQYEmjd=h;`d6E3J`}AxkqEB{gx_Oz zw`dxl06Ld=V~lA^`A4)NwG-BbCJwA&Akr@sYToPVHKHEYuS6Li6jPNy02n@EMMFnH z0U9H>eJezZX!*Mw-{5BZi!!Q&5#Xj-BFFQ`<1;ddF)oLwo5`#w-$O%N*&FM_e0E{} zrVojZ>QznAb&edYN0iRW$vy4zmA<_%65~Hjw7M?hRNh7C&WcD%6(kpXwEM46Crw~8 z>8YP11iY!F4Q#M66J-S7RmMgmbfabo`2?e8h>df>+kuMa7zG>$;hEY);nvDIuVaB5 z`(513pew?V9>E`sTgVjg+bO=O+$jnJYB!487D)3ZQ)HsNqL5bbSH>|E635uF5A^p6QnGJv9j zbKbXi50mOiIY_$$#$%+K=dXNsA}`7}^SFPR%h)s0kFrz5vQQsW@-_RrgOeP30JAfeUm-1=?;isv$vxHjGM*?^cT&EuD4GS)VHUa|RU;OPCxOzW= z7uV(Kh$PM4x7etibbBitxQ)k_*M5B2h>Sz;`c%@rl8I9$Wd`K+R8pHLN(*sc@Z&89#!Q9hJq z=85#*kQG@>o5!}?9-`lH7X6e(2iO!5S)vHZq8zfr6`kpcGop-%u;BxJsu|*?DCS*( z9-C62!!ni_i1}voiFcq58FwG^KcpWIoK8qiSm@c3DB$6Gt`-dq%y07f9E*x0U6-L2 zRA+b|9gQx0TLa-)^k*%Uu@}pw-kHkh;LP`J%CseEdpkZ4`1wpQ!hyQ1yF-ND9I2_! zlg0&3VK#7cmt8@YWr^SM#M=c^KaFA{(poAU6+HdR&^c#PWzfy$sE`wqn@~p4-BwMj znrK5%)mDDNWfIDnRIm_NKt}}o&zB%R`X=4U8baG#8gCkqm^oA!PspXZ?QyzB>Qs`xEfM=tF!Sec}VRr63s6 zWpPF=Y z>Mz445N{0`3<=aoJYDdAwjbZ`h(G`vl-{XtQn z%mG&NKKwI&$nJpObQH6-SrkX>X)50!&{0C~2Bm{#7_7*0^BDji$Ok!NI?L`sEGVg7 z-~1o|#%@&R%K-TFXuXYA)x{NJda;_Qv(<1iW9bI}M2_KmpYvkT)>Gv{zoJbi#u?-h z>kKlFb%p^UxtN?m(YM=DSAtk?eR54?B_E|CoA{$ape|t~r|K|hzl-Lpr3D$cWA4U& zBsJ5Pqat2f2zfKzEmNh{=c&Ni>!c&4!uQsJT;u*>WsW;wEuvCkP=*5@i))E6OkN)C zrMjk5kMj(V<=C{Rr=GsA8}6^PSWk)b&h^ldin=Zv#OFFq33^r|c3iA6yb4my-NF{6 zrM#l)u~@>3hRpe~7oxM7wWFuwZRK5!KIB=-6bL%zytc8$h%Z7{8abN#@)V47WPtC2 z2Z=M&Tkq_XOH0Rk>&IGJr{*rpg4MJ@)RF?hy9rf<5}XRw`Te7LDU=kCv0q(E(d`^5#&trZhj6#pYnpx@u`VB69{S~~t z&%58!82v`zk)r|8{b#Ah0LFL84QlfHeO@mh_bvy65@3fMPCD+A9w=10uH>v2BowW)GTk853P566C3-OdATiEg z`NcuROjC=wOf=qjE>oCnE|bF){VUnJ<(ElLdgF~dx{-dME%s5&ZgQgb*?$Ie*yB+u z3T@4=w}F`%cGog(gu(3Qcr_vJy1JvMaJyZ`Ui%J=H4IF~ATDH!)YQsPg4R3|aR6o7 zkUhGcB{}qA)^AT%t`o&6ttk8wDfA=7NBa#KWC&9$fgZv*OAKK^j6)x&5aq2gtCU?v zwO~*`r%7JM*-(%SSiY5)QL3mbNWPy8z$)h>Zw%yTu<#xgB&c9rVzBE$br9_&0Vx_q zT3r^X3pZo#jxQ%XET3zGq}QhR>QD{YMz3d2k4tqx*Nx6Vd^j1?gWu6ImBwW<`MnRz zVr;n4KZ(UYFTI?5=WZhe2n(5^Mq8`cvrmGJV8}z_XSU4fZ$mojdN4Z!^Bn*QeWRsX zC|S!xH=gin>Qv{tvzfLDKwtUo-x2Exh^bL117cS9Gka5uzIartvfvHpQ^T*kUC*`y z6C7}yWVbi*`#J~UzUHUyLY;v%;$vEsCg`r?IePcpmq^2bd0VP!@~qk~Jyc1fS|d1H zCnoCYu2xw6UHFkIqUu?X1XFC!F1}QBa$f$Mv0Pojkq7H_Jy)m%iNSHCt6hkLWm4pR zck5gGbZ`1UBYUFq#QS*DFI-_y+>HYrfSQ1S-+ta};MY{V+r>x7M0GN$rq5PFwrH6f zgPMTP%T;wF5ia^c_tsWO9QIZv@;hJ|c(9DL{Pn2s$+zvTsQPoMNNn$`Sr9#=jfp{L z4HLm(-r(C`rZj6jcedx4D!ls71o!{L22=|l53h&^=YF`(x^hLw%+(3VFV65Pnpe~! zNKsrNZs5jCoV4P4pDo&j`Ps{yG{;UFj2wNuiqoPIFDgU*p~7u%ioFQO&S`UD+XOqT zti3i07?iOW8ShiiGV`pMB`zvmQYsoqCz{wrN1ovp!i0{t;J*6 zj{9pKJ6Td#8((#t&1h3e95+Vs7e`cU2w$DzWlpEg?p}-rb0fMzJA(#3$3EmMfRDCNg}6|^qXbVFQ-WU`Qi7@FDZy6|Wd!wA z#(bkj<&w+BjLt%o9m-Mzl;D52UD*zDZRwO|_}lpe>b3~(;S3G@&ugYX-FDjsc3-H= z$E~L6ss{3)XUJ0e?{auO1N&1|sM^126=W~KtCX`?71Fej?@+S#6@7@f*V!-9?gaUy zF(720Dh~CUXLfTTFdJN-XRZ^QXI3&dmctbsIj|OEs(iZ)8?PB0$6A0w90)HWPrHxb zI;D6Q*%D-dDct%CIGa`7M*?&D&OE}e_0oE`BTRmvnX0iW0l5j}`o9ibXEpi1-W;rD zPS$f+V==n^+DNhPlMu=MsA~PkouYG!<_FDr+v)?NdAQxN_G{my0t`)xKhpe5e4k@G%B?K|0gMwHr9 z(%nS#=ZN2=cM@jxbVPh2ULN%|mnC!=#@;-II^k-_nO!U)HEKH>C^P3sb<&zzRQ# zkf@MtMxZ*O6Tv(KPB|ucQ#?oT;Xtf~lf4IelW^szzN48nhT;KdK#(G%t%#EU)7xP6 zDApY8W*F6P6b6-BQQafrQa^1PAK8I4#^dQwTVZP4Zx%=9nla$|=z5uD_BT8Eu9|zX zlR#5qn~u-ICp#4T?}NJR;!Cq^M+v>iftj17V*lSPHgWkm%Ar{iP~^z zHLG{M{?Kr+aO}tc!K7GPz$B_%Pz3?+sD5S44_Yv=YR)!EWG-McAbqtZSvhL7Xk>O^ zJ#flOn!h6^Nnn!qw7V9TP-d)0)I#slgzcbW1JrSEI`Y}?VRR_@OPdWs6iJ%W(y90> z!-Djtlt~mcE1{f~v=$ zl|1eJyLVFS)v(uF>(KTDs_pP>t`Dt(gPaz<>OGS6^iN&4_uen`OQxotcF_>l2Lp&x zf?j`5wwKo7r-yr~>x0>1rw5DMe_#oe2J%eNyFjv(5c=h813_5|g~mJHGu9#`0J+-l zL6=zehFgyPh%UB-@;>kmC=^W@X7bSTGgA4}0Vn@9+1Xmt<_P(Hg7ii3r#io*&WID>?wi%pNS0 z#UF>C_y@AZbhjn|s;MHQ`SK{$7@)DQgY)J&h1meg+O}Xc3n&x|au}_Fr{_jIW{ato zx8?f#uc@#f;VHIiIXlu0s&d~AmBYAY&qDVh+>li~#Ie(AAQ5vUX5GcSrNr_{z{1d%9TL0e8vv2hFzkGS4 zXuqRBC#9`CY!B=YgT&YB9KU{)3$08l-MP7{pF?F&OV%R}M%qxQ#BK>#n9smD|}a!}@bhS)F?GJa%u}@vpdD z<=NRg(b5vPe#~cqUVCg~6|K|rnv&0)FTiVSt~7A1CHO$6baj%1&uoeQwd_@ox#C~8 zM`M3cU3x3S*&3Ew6AKj7x4xIQ`y9+MwEIL)4F6Tnt<4nJxsyJ0Y`efVx|xbpM4M>~f$D-khvKF3~1Ys8B0e;XBe14VVEsj3P*v)fKLJ&!hsx26I? zJI}8Fino+IIw3Ai(In3R*UG*gUsg_Bnv-nPJ^s^xetf#RH{Q~~cl&so^K#d>z7LTmmb8h{&q{y;PU+?rjHw0lQQTIcH3@4b>u zz50*c`jg7_AM2XQeoOoupceGom)ttP$hs08kl&JiEo-DxGV=2#|cr&#!qUE@N;|3(yiD~Is|tt z2L_4NZQNtmlODiEdFd&dT>CW!UI}Dg2%>uPI>;;Lo-!}ro;W@~x}NPotPEwS6Qw<= zwVP5AoIl0oHCp{*1!Wb7hG#Zm0f2V3!o47^kjJY-m5b7;+aGs=`G9&(i7aDR{hqkU z3ew{qPd;x9W%a)h^J^alzIJaLx+)O=`$hc$-g2Lr^o);Sa zK_+&u_I*y;N|>%|G!IFO0wGMlKvlMP;^dtS-J)GILF$sCFlM-uHN`SzJ$<((8Qe@Z zX<6Oa$HRp;rit!KwU-pj&fM_w{`Isgxpg)4h)5X6=SEuYOXC3$q;q=s*Mujk;?^mRyJJFi5>N&}3*(3Id)c{C zBOnsU8ZFKzH!0vyNjMUUNE%nwaugTmhx~K!d-7BN`v8?hz!4WQ1o8rglz?BvBrozI zQ4&efccjQrh@`GNV6>#f`L__zi%b$I@e5H%h{Mj3ITtMqa^4PtkbqvqB>&?B{ohm} z2o&^uFc3+Y`1!OT|HyUDF+@@laREa@B+ttPLBe4F!-xJSek2@mUMC0=fk2&if<&P% zI6;X^T(CnSF4PBtLZKk%_@EG|1p2%*PzVfpK~{)3?4q5-1r?wW$+OluR}v@$em-a@ z6bAW^3h4iK8dL&yUIpk``&}S}K_o5^!Ye)q2RyFWO8oORZ-_Sw(-KF{9o+2NKhQO{R}6xdvV09Yp^H_8>*{tfxo0S;M~m>*5vkAAcX6E_;UKI zVfypgueA?f#?EKcy9G{0Cu12K&iu1CVby=`X0Qg|knByN=O;b(n?hxywy_+$X3`T4 zgcoy0#ka89u!~*k2g%ZAroNQ-*mAauyYJQ7?s|44+rG_;R`@`s=SwjtmT=)ow=htE8*%Qp)>&L@0(!{QMOnO?5P z59D||Mpu1g;8phc>1Q|W2XB8E-y57~33Tigt@%!GZ(5U>OwbXjm8wSZ)^K#@j@BCY zZjn$1c5_br`UGRLy8Y;&k${cB_Npxf>FJ4jkdRc>gyO`hXr;D#KCW&Z!$<^UA7p91 zn*L1_nBNYsSlqj+2#~w{wxNmJzd1Ix(#}4mwygpO=6o0?hnttj zm}rO$o2#s1x9pdYl9}LYjs1P?HPNK6+TW5Ev8!Sh*$S4u2HBQT&uZV;Zq))(WN{P* ztAju5NMmg(L0L(nQ`ZN)+f4W<3M1j4V~HrIJ8py=Ez5m(8J9a09++&9KE@sm7$=ih z!EOwpQ?lanP1rJ7=S0P#>e($8bgT*%_CHEivA%ju)K+j%;4FbLS;Qx-$jkoobHJK% z5X<6Tza2YD z&uR6BKiS(xOs@E;{^W6tbwLPPuh>CmF@gd@Xa=4He@YA7qxf(t2JoX7)Zxd70(2Oe z&>JP+O3KM))D0nSH4lfFEgA5F35&g1E`F9&%?we;f<-#yj)Lu9AfJhHjVVaZ`F_kzkv8K8z)^oa;MP;t3mp`aw^9Dr55(e-Lc zZ7bwg?ZVF30x?_rQ)bk`M9L>#aXZYdjy?{LccrXJ`^!{LL@(C6S0AgDRc(px6RcLo zhIw!9rT_w~oIZqRj&q|#UAhR(%wJrJb(L(sf2Qa1-5-rgdSQc0T%-%#_Rv;l13 za7l*q8lXn66j>ZR3`9zrn_~;AdrIoaWfT9_1@UQ2z*w75*-$|r0ga@-8lxqRQy76Q zf7>X!Gk}t8*M?%!d7ZgzRvRNJ_%I+GY-qU* zMOF&077Y#t10-AW!TZsKNjhbEbl&MT!!3WSA~+E6sX!44wZY!;r`CF5P{Nz5{(Q?< z37EnKvY@j=*V2!0kQO^;s@;447`sZ;{OpG{YFH2Oj(7$tBP^_klZ>@j?&r%}X#;dh zh|P?Q=LeARfU6}|^IHbeIeJtaz-8}RO^L{|k3<)oqr+6ga9=IkaErT(A|b+pmB<#( z^}mg#Vd4eF1{i&I$W4q{@Xw4Ulws2L@U!D47Tk3&`o5cPpjR(OWG*MQS);m&lPR%Z z9etHCImxQ_P{uMgAuyYOi}haqL`0y5K=q&)u?_u$Z~OtG#1kaf^*^n-7USf!xQMF> zl(sL~0uLSt+CKu(UaP?g_IudSJLy(uwtnsJHz3kHyLkq{#wcq8p>lMvA!XOLSTa; zRzKUtKW=NdD@GplD8%<71xcQE79sZGK!XK0_d29Ge<-fj%z_VGg({M5SKZ4;I5*JE zOWt5rirRH(T2KCUG)?-_>?MlcIOS&R5D%fDx7K%oZgV=aHv0tyu3D!bq4b|~DEWKt zgZFJ)^$Aa|$0=v^nV$@um@>F7e1>B4R|psnXo!t_m=KkgW%o`%JCRN?Vty`1XH|okOk6hCAb%Xly%a3G@3GPFMJ3BXKv9$7Cc0md0;giB49cYU0$*bjrD*$i zILQrj9q;h8VjkUAE@wR|#PpZOCg5Gdor3T0pgjddwdvZKk6#b+8#MLBJ5fG1R}oiu zpNh%Iby*0F_`$%8-|=QzD9-{xP6cm~}Mc zT}GLHN;ejG3;kT&SjMd)+*t3sn7p|hzvFkq=x^S(#^UF7*(2%t=U>*__aa{J)L2Nr zhgG^Q8a!%${eJGA(f#t5=BwA_1z6nc^z=D+X#&9trVd!hhZZW%@GZ5MGdp_*%MJ+! z_+psDlLdrXhO7feJHOmV=%`;WljCG2vL%=r06@${2>+EeWlS z>R`Jc+%aTbXGUd6lG%9I=NmK54-Lg|yzTieQp#_b2Rhaa65g2FTk_1vQ&@ARnpcEu zT;tbrd6V6jGRdtI6J9An#$1SxU&(nHO>F^O!zb3c);+-Az1v(ez;?i;T2(r>ww6P_?z&#gv(cZNUSm?@(Xh( z(ZSHBUa;04Bds7$>Z9fxY*N8-jQGrh%r~)Kne;LbCvgn;jr~@0PN|}(w!}JwZfj2Z z8n?I4Pl*|(f*jS_hp~BKBGD^$x>+V`tx)ezix1OpcVW+GkY0I)QNbKQKNs#E2-M7Pi1I9tkd0%KpwaAzly%bE4C5(8 zhx154u6>76YrcB8-QF%fN4D=bru*g{Gc9~Wj6qtOtQqr+Gs`?Cr(F3wJ&b1zNNj4S z(L!tL?5n|d?@a`$%DQ2PzZ@Rlj%;KM08zT`I6>q%E5)j_*P8QU=5sD=%Uhq>2>ebgg zk;S-3#!zhFT_W-7zmgyEoeW2Pa7n@p(8g6wV6v% z#8`V1Z$>_5y_yVgP%3!GUN04%eg`Ep^>bd5QsaWQ{}{ z#72aKMmtDxaG4eYZf{FgzbHIG!UH#nA+cXj5}%z2CHHxp(s%^a$+Fr8iC`;GXx)XO z`cf3o8CUIOE<8f9T9xFz30ceEnG2;=u;Cfbpy^Yj1>pN%eQa9*mb4vf5#xd2%IAwo z#-WY(bML9$$6YsHB_I;`Z2de@k2E59Ccqs}9|r64sHCN(h!473!eQ@i?JMK6oVd@j z8N6owz>PEI^$B5}kOtn737clf^K}}uTv3jyDc{ZgqFK+VkT1@*iRM}Ph$58H8BwMG zknu|DMgUL*R{}yHi%X^G0;m;k{J#;czbI{w@BF3ian}oefs4q)3l%XNW+NCXAP$9k z`wt4l?nN%@gc95DZ69OwCT;U7dVgTg{f|f%vR##l3abwJNlbg+XDY5pAKBSxydUn0 z?-odIe_Qzv%Jcb9%{cYopRUP|e*=he{nItB7HT1`xqx6-3-fPXC6$!2M*q&;f3Qq; z9POIW-OPXIuGoCq1Rj;Nvltg=l@RAa&+(t~DZD)BEz7sHJqff7+M?T4;GDRvFhmpf zlLXm(@7ESM=3Zz=jW8IcwX2sks}V)pb2rpA?QxsxVj25VOA8#F9pyOKQopT%D;Ur~ z7JSths{5*O!^ZGPH|bZhUOqQvH?!vm}Dwt*2u3@!zH;Xtm}rt68>zoGf?R!)>*<)~2ot&Zn7Y z&`r7ih|}<-h^ZRaw4&Ya?%?hP@{TJ^+#Xk`yghCuXI>AK{q)=RR#0lNbL40bv{upm zak`2S)8wDV9pnDDafv2_gFfEHgrwS8SmfA$<#PXlnezm)j(l@U6L~q)cGdugdZh}*A$!E2J%8E6DdEB{et;sOqcZ>aW`g`%NMTEh+@QI= zF3qUatP2pNeOA;Pe2N868R387Zh2VQ{YF?RCQw4~+!*HvDFhEYxo{Mg`!Jc%4X4zb zyO7l29?RGqodk95-n?cHS`8$Py_Ysbce!&uW&O@i`O-G!?76OkL#Dd}Ps->fZ+kTJ z`C73iDo;lqo;R{#F4DjXwsC3YHN&n9%r$VZ)K%F4Qmq?P^p@CDNW>7&7O|t0Z zw~w^f{_3H_Hf<@1BI4u-sqWPbq@{AgbPkYlOD6mni-^+ImIu{HB=wj5%H;2mhxvtF z8}mpy#1HM)0E1rZDz1x>d8BAN7tZXI>B%`lf~So;*Us&* z{CgsBMbsZl{9yhGJf+ z(rv6zzQl!8SUmtU3ZW&XmL`ew{22X+u^st>EUXTIVFdgO%4PxgH_-}W)0iV)pgjSe zGL;9&=|@Qjp{uAXwecQE+4@Btl3~S9PA{!UZNjd~ApYcw%r+-oq!$OtV_I@*X=UnH ztQNO51`hA>GSDiZthwnp{bD%d=(@0vES4fpY3Qs2@RWdm(cD+*jQeFj&4Mk!idK<` z9N@MK-ObLx1O^db4BoUSE}TVZx|?QOhi)u-`}j#eT1b5&(rsQagXc!!79x-|Z!D)l z&^END^!*LfGNHL;(o$E8QIPOy_n zu#ML4b{H}*fRHbd^THzC|l`ISMw)YP^ zyQD%^nw(eLuYE>CXR(7%raBS_9+YhE%#C8tsy!O)%=`}an@=u|x3+%>ZAzW~b~>5AcyUTwx903RTHODw@9V|+ zdes?Ga9yv2_%5SvkI)wpv~8@5fkooL^9AHk~Xj}j}qi3*hQ!+9)Z*w6$a zCFUkZzE#Hcsm@-YdOlU<NqF-9+Vv~QB7R#!NslcBCV_?6p7lVR@zuf$oew#~Zvo34`^H}0VBuyQ^S2pya zfuz^jlF?vkV$`nI}G_Cu{;CX?e9R8N`&5ic+Vll_heN;we%Z{gyZ=dU9EZp5^ z6t%HCa=dSmh3Tbwu$Z`f1eVL~oHbh-b)!UwC-izsGibj34YnFft7CALuxb|=U+tElgO~+($Mt-^ z`}Njv+A=03Y_K;XO5wL0KTFeyMNXJ$(R#O3>ib3roaLKV57(O{g*Pvc;;FEx2F*?p zU`eK1DPKa<>o)5drmv?fTBe7YY|VMAeV}(XWVMZzzo@Z;LDQY5=f+AH)(=QYmf#Q5 zzNh^j4J+^$zDH&m5=r>@?c22R$Xn!uBCrbdt$_O^!eA&E0fEQQC~06J5O&bti~Tt} z`M(cf7*tsFDkciMg26?EuVT=v`QUJoOL2dQp#RGek5lF#gp0=aD|2I^m&5)n^D2cX z0&-<}q9S1MB?>SMjJO>4hZJ-bgZ&=|_CFecpa{_A#eiVKkjwi8!5}cuC1MZ^CVB;f zgP@o71HpyC|CJB>SAIA|^s;~;I8+pVnFNl2Um-z=h+K(7K(81FLLgv>OZmVcun6|D zG+>Z0?24=)5%ASG(JLx|K~VS=l0OMAFB%L6BmPeX?0-rSEDFA?0vHSvxsp&AByuI8 z@KxWz!lIzd@`Ht8m)`xOKNtcQxe^EwzPb|#6#ie{{c#Qqfx)g~2-syyz#_dTwjsKkH-q>o-3Ov1XHUOOcczz_ z`d9p%-{_+`*_dBctliq#`nK}>cTA%;`RM!MLhZoR;XI@5c;4L|)ieJA+=>3-imf=6 zD++gZ9NV|t*cUaBy~Cfs?^^iM% z?Hpa(3Of6B`tW4qu=8-i4F_;@KKN31)cU)eg7fkE^3jrOg#V=l+aUV79#TKzDacpM zQktl$<(=<+E>^`74drjE$5Isf)03N3J#g9fmy%NgrLIe+fA`T4W@~*5{dx-|AuN!T zH$=UO6yAT9^db{JIxP_KM8lxXW+7q59yB@QE0OK6q7Hr(Iw{yWy>3|I+>x98P0?2^ z5FScxCajWaA6B_IYMc{DOLMNy4FxOR_-k5j-iA{a~r zd#iGFdbygUtbDv?#fVi?^vz_hOKgghw0$lEt2u$CvUIP%o;1PbyKDrc1DC)f;I#4- zg4#2SO6Khz{j!dO*k9$>`wVK{HnLk?)eeJjl&e@c6B zF5?5C!;YscRs~2D1frb##q+DAp_J;Eis20~jfPmc$-LPr(M!>)Im-YsBOEi9m_+qU zSljaji~C{;A(2)(z^?{+xF6e_9m3#kZFr|*zPtj9XKtN;;$)Y?j##V8eHwl`pqO27G7hR zKCE5B^rc(4-FUB&jDe>PQ--c@tfW{V3yMb)?O=ZDD%y~h3cP77zV=h_umQj}-KsA9 zstX$LK>`DSE_Vi>d61Eakze~V0Uz!c*K(wzR)0lZE)*#8RJoCL()}+?K{vJ{=gU-v z7L3^b*AHdOa`R<W`sW|nyQrnANYr295iUB~PF+e+#4>;WNri-&;z&gO} zs-}u=MjU%_LOzmZjUIS|4sjaV+^nK9IBi`q+H#$0Fd$k*Lu!;^RR_Xz{|zUJRM4`? zVpzyLJdavEI-8XZE}<&w`fa_r14^1QM(;(=>T?ZT4eZ>=#gV4o&pPGgPSIuo1wI*t zns8RQnh|}#>#EO(o5ynjOvi3~m7+zKO}(?D-i?$PwC&;yvH{YH(~Uc;>C;j|oay58 zy9I8~HpQ_#8Lwlv|5(ASp{6c2-mBI{{uiJ_)6AkL-Z}xPdqY&9@_31 zj-4EEpG|RDKF(-NCgJT++>E2ebiEzH)_t{0rAyji{APE_Gw=5@!9`TL{HqfLVJ%HM z;&VOQTD4SQ$JnoHR5p#CqgX_-M$Iq>5tOWiQ<_}qo`K^Cg3P0xRTzP#0>lOcE`Q>5 z?2RPmp^gExL&HcI=%IIW3IxT{8+twI@@E$1+CqwC@h03Z9q!^QxdYOnh;%_{I+csR2g~}^k)kc{K zkrH~|IDy1y*a`VFrFX^&i6`=#_k&3qW9`tBpm67X$%Ob{Aawa%%l6&G+$%ob-8;0c z9qRUJ*>Wjx29eS&Q--Q+SnwS=H#s^_puRKZ*hwGwiD#^E&vmLv8&wwCiot+>11-1w zyT|g_`~o!+fFoYiaJDgss8&n$O&NY~{~g`{7Ytxp|2bX*G_BaF86S?Hq393O5M~T` z=2!5X2y|Dra@GA9^7o|v9@JEQ-cjH-JGn+b4;wyNiI9@kMM0! zT`9S)u4K1^vj|;anq`?zSuA*ha1knbSjk!d?HYL!IOWaLOpP5p}nGX0>O69)G zuZ@VxTT311l#3MF>xB~7h3o)0x=lii{Ij4~DMmo99Oj@ZWz4u)3$#Q{MYGDlW7q}k zYYz2AjOsy7pPlp6D{AXfDao<>!DS)Z0|!cxa<87j<&PcZ-xsHJH_`+_onx(M)nLfEg~W=dRpdP5K`D zwDrZO=-ky;sSgQvi82}{28ktWtxA>f;Een`ff1Hv%(hR{s|BTw$i!W_h~r|k+j-Gx!ulHMFDFl**-FXk zRrdOqQ9jENmou#4)#jA|l@WU(*w4k(W@8$pXkqp7v8GfWzy`0h7*ROO2RtJZHT4Mm z6qw<+_fUEaj`8Oapc@`;6gi6zh>e3eJh+`3#lq(~&wDImE%e@=$Jty)^A_9qu=CKX=9(#Li-I^KT0?8=LrR=|v6+(@!`n zYz(XJJ|Ud2P@udINN4$(9p1CTSY1MHSrhox3xqchY;6?NGe0|(urQn7GcXK!VO~rQ z(`z)yo7j9-?Xov9_3}djA7GgOvc*Pzg1%i76kzv>akP9_q=bV~wAS!^HHbd4gAKXL z=2>IEiclxvGPAPG+BYWk2jaAvvx*8NOR;WM6sc^3?&8jRb;@~k_sTv{Zp>eERN=y@FFi}jDX4M$km&C)G&b+ z&P|z+V2b>dWB$H!Uo&e?KIwh@LyJ^rb-s?DSGi;yaRBkPSGRf=sby|H{Sgo{_3TJP zdgQ1Am>&>)X1t`liu&|p=g#uj=O@D@Mf4)mmLDQJiTL|S?qD7LuS_(6@oGCj6pHdn9axn!-YjA*U*8Ex?GJrXz5hUDiU zKFLDZTXh_a?WdRan{_7zYpb(b-r(G*j@BQ+RYneO^Rr#X8ezakEx)*l4Sso)m9{~X ze`O|3>DuX~&bD7JNth@#vuWf^k2+h3BdcCvG&m!p(P|%^7NosSW}-0oSnq61l|5=m zpB5vulEDB8aU*Gaop=5*mq&a`8dQ8hUN6yHY$t+SPeAF~GrMsGgaw25=2Cqe6`#OF zSf2vKu;}h*zLQk!u8w5Z9sokM#KX6qPsnETzQ26sTGqy!fEh7Y^f$4k3HqhPuIZ+y zEK4EtU`*{^iJ|Ha$Sx?GCG+Ly55r8WH)x@9QfYYH(^D8y{dx^qesSTHW}32tf2hmo zHb*5+Y#J{_h+}cf9YYJ!4uu0PIAu10V)d`O-Y#B|`_#`aYLb>tD!3v+l#7{b!xtds z)9x`Q9%lb8MQqC%C-_iMde!q&8o$l;A|&yFW(9_lka z{t>P$=6(9W2HoGUK`Qpf`NbrxdE70hcKZt&~XT5<8o!xa`espi`$?ABN0Hib)8L2H%80Lq}=<5#8U zGzzzht@lgGBsPlgJYU*FWs;dhaQT0`9%(d8;gVsk*)ixkk)B3#*>8U$;)!Nx@%>D6!hQ6Tkxi8t1HN-*Z_K4-#p-B^6@fD#Hh}d6~>m{`iU}S__TGw zOo-zMz1k|n>{JHf#%}GE-eFf+&VU+dn318Dn;VXpVkq7+QBNxl{D{Q$Sm4OY5WjvXtiK7LsOWW!D6X~qa(kHx^u1eg;HMKMyFMF z#YV?IZ|7k8oh&J7XovI`LIbM~!KqUwu#t7Hf|{)jj77)ZB@oc5vR@;Nr))+IJ`qilIWk+|d z(4U827DYL{JJ2|*PA0k5U#=m2V;fFJN83wvVskOG2M%;Y2TXF0#olaQ#T|4^3YT>r zOMSN;JP^e3eqeHWH?^U4!Z*Qyc_BTfU8^+?#W38@qn&BVE&o&Vla7(_%pWWJ&SAEb(ZWgTW{OiU5_Gz zJ)<>o-^y@3%#K}K2h;08OriR^8dC`-#fk?dEO%LPNqg_YXd=|0)JD;{q;Bh;)2DF# zcV6N|uz8Y3^M&u@ef#mlrb$Rhi2Jt@b{eKNL`D6MKjWq3-jw*NrSIM^v3@Q+J3Xg) zmu+6j-p0nG4tWMGBBQYC5SwA8(w33WqPr75s#Z}GujqbC-O2djOg~88)+u27T)#>1 z*s$gR=jZyoZs|Si>4@a{{Fi6tUy#Xu>5>gs-A?V#GRuW(HFNTE=Po)3RZos{57gO##Xc*W}s?8#LF<9J=mM1VS< znVC&>sDYQFtRV4gIe5&{7r6}7Lz-6K;f5`opa9P6AT9Y@&!x-jl6#$@+k@PhDtT(@uzY>9gJQLi9POwYGh~-4ds$LGK|RI2er+u%amA$d5nnBp zERK*pj1GHP)a?KC*;K~uPT@U&0vh~Hp37F(+8n}B2jqRBmYg)x!=h8-uKf;>;!h&4 zrhv$Zq}j^xIy-q;X)wMq{lfQPb5UmYXNSx2`mT(%UXVMrmE!|9cULQOhu@x)g)KD% z3Ic05EKjzLE%;9xriWmD|zrE1nfNiw}Bi%)PFk=Boy&KFbw!Y92|Ad z=Z{=C=A6|Z7~;I*sXT@RfxzI=3Ie$QQ4@~3mT|h6c`1$APN`-`>)*d`sap%BO!lu2S%Zg z7Yd`n=nDkVaL5HRXawqFF6P3kfiV!=KQjEM3K%Hz!i5+(~?V-T?OVf=0X9v3hM zjr=nX0*0XfBm)7%q31(>Zn*zXhJeqf@sA53kaP3`21TCl#Bcj|VF(0qLF)4zMgLRe zKjNUU|Bm~uKLm;Z|0(}DhPjBLFc%2Ipcmwaz%D8SfuYbB?n5FlL}%Px*uKS$x`F`7zXr89fRAkZ+da593uc=Xsc~dL# zT=0&KyF#7t+2<@5`DZ&jJSNxi^(FCFAlCMahnqCj2w%&r`k|cEhJltx`ZF^ih$^^x zdT(R@Fd}UGAh-OWVt=r{9(^#{oabE?9-qf*J=Xf-ZF#PT{p`{UDG&SDwfJ1r6d8SU z{NUi;VQR~|VvRXLcHZ$|Q`x)z+4OW6lcuuzCyRP(Xy~IkltgGTF3W&ot8fPITW-MG zD0!#j#n}70Or|%1SqW*crFRbBzxO_@+iz(3fe>h;wb|cg+*`V^w-*BRdLI@T?$=Ci zcoIv>SKk`4dQCfdar*%|Q)}RM@4mZ5m!eLpLH+G*QeqvBP<78KtEr`#OuH(fG5%n^ zG(I`hv}JYPR#!g-1{XzdEKqG+i|n78P5=B{6^w(we%{ z!j+ym`z{D zjfq>6T4DQy*=v%4X;PgO^m6FB=(kiIaZj1kI3tKuxVMxpeO%69Zv3HJ_GF*bmN5oX?3eP=cdcn)m3kvu{l{c z43G@#=@kpUJh-LLHhVH7Zta9uy*z zNzV?>8FYTiYawTqz-F9mr~d#YGfCEwwq0+2$L?_c2Gq3hD*Q8hei6}p^}M1Jy8uYT zx;^A#vN@;WD+&SYtEqeUGHu)51xsGZAU$(-potGF>na2ec~tKVsgESTeSR#|bA%8n zn$t11wZ(~h?HQxnK7glG5=Pn81S!X^b;aa47aNrNc5soJfJHE55!_$2=u`yc_TX&^ zA2_Pp*nduX2qzkjnKif7iSt*3=RaMF`{RC755FU5+c>2TI%YaicHweVN|K*xt z;Z{0Ge#L#fR9_c<)2zn+3ZUOyVK=6Ylxgi^po;C+w7P4l))q74hHsOuckYY?9oq)A zpM+q+HLTY4EK(X3>P1#&B@j-Jg^E%{Bm}PsjjkdqC}viQxz4|F%G8K3PL)UotWi|x z1`?VjN{YI(R&)qAZL$s3uQez22pG8g1}}G@MNtD(G43-b9yw$~w#L$wtGgMqK4Ry+}(4+IeOGx917J8qWoDYqNLe0k=kyENI(A zR2=X%RH~X$AbRac4eLnZlW@<1$5ubbT!l$bZu_QQgb-3W6LcO2cwBG)>X5V^DcX1K z{VfT`63aqGO2|z*2|h-)&gjI(<3;OT(C;J%x@y?nLj9U`nFJ*@`MZy%F~A zL}`narx}EBfn(2C<`(Q;CDI5ntP4deiaV%FK7?kZ_c`lbI#rN;vOD}DZ)Gjlh39Fj z1tLH2B#^USg1ca|tt83t%=v=jBzCKNL+PLb5}fCj&7>#Kyw%EBfvtFvgxL(POcv4qEIYOWDNlBC`j~tH|O9f?!Jy1$g;Tgv-M&D3Nb!mDGxA+EbI0oYdkrY9oN?W)yLNBRU)kr^Eyp5zxbuqaYSZ= z@YdBe%)U=J-K-mrN!`_QHRhAE-SG~xMsisR$wEcWyWcP0ZNIf%6`1L3sly@2Y2PVi z8Zwej=vJ`E1wQ5~?3NHMlBR7Kqpz#b6EYGXtYvCukA@psN$u8NKA=&pS&$Z#e8=yB zmA^bfcbfxSFoX{T3<5Ehyi!4uW#ZSdJBeda=^cs5DGx{Dh^h2Ilt(G%5OP}EW34+m zdXjZ^jK`DsmUKMxA(9#Wp`8^Gm)yIW0KyaI3ZjNS)X;xX;R`suIG8c@8Q1fo5-y2AUVAN|N&rOx-X+w#A<*&23o(A)xIXf-O&(YKd{vFn0t+N%hl zPQgTEOZufs)$yFWTCTK%4^vq(<%@jS^@$D=atrSH2|7ydg^BZ^3k9h<0(D=`xaLWP zK!<+KAm8cA0QNJ=1Bu2_`yeqo5-V;;dwi_lYAa!{jKb^N#PO;$iH3`^Kh_k|5k~D9 z9ZiqDVAI5uCqnECGyvD;%o^qxLT09QRiO&CW6Z@!pf6yI6)$XC&zukF>%)4>ITOpr z*VPp@`7BlAa;5!TD_mU~-d!8^Xt-aH3GN(QCCt)2R=*zDmdy4uGCo;I$bInE-KDsI zHs_+!=s>pk#Pdv_@XHN{?C2*%ks7pxdrxpybZ^?K6B?M!DsmB00tzB{V8#kwNs5+B zGuXH>L-%jQ-ze|wE90N##y+w7n)Ll~A21#6(0=tLZ@`1$OzWmX6^npIbK#Lw`c5-$ z!B3^1eMpSa;hR)10BE-_7o`j_GTags(Wr27Qr3F_(Mn88)|vCY-F4;Ec)su{6{Vr? z;#;_uGgA!E#Zc3E6A|jLKkfP{;Ie!QmZmYb|Fv-by~309MckWj5Ni#U<`dP#)R9aP zzzYl|w!hi0rmlBM$;y7+0`g4y&<}r;gIxd|4~Ni=Wgrg1FuJW z>eQLM9SE_?J)?Xd3t4*i=zGkm{x~+*e_&%Xvo$YFc{#9ab3#x4kb{)Z*2Z(p#L zaz>?O{0vO??PH!|+G`0PJ~7yZndHS0ZmT zkXeMoU`wFfi3bt+?V& zndAfi7~-$swdOCrH@fKgNh4ZpIbRV3ofXZL%z?6W*6HuI!qr!S|)RC%u9@lQG{(#hA70o@q{ z1=CdpS!MwaFK!!ko%I`{JW<`(#}O*GuhBj1m5R}OEp<%tZ2LBQ(LEaBQS&7$fgA=~ z52-ZumzRO{30+ORCD%GlTXJ(L40R7Wq77O+h`7DS?kszB$BOcKb#?te0sMW9EOTF~ zyJm$>P5Gado*lYy|7tOxkyY_EWykw!$^@Yia!^yDTGrD_+)}pVmjsZTfbw>EX@gr| zXV2MaQJagh zf8@ck$e{}yh}RkC2lSC_sd#-LG6gKpO$wlU;M8yXj-Tft{jz%KY=%EUh%b2eI&k>Y zuA}psUyV;O53DaWe|gdgAP_n*SQSjF8=RhW;a(bb{th33z6{n~(uz+eT0n!nkjWh3 z$*><{^Lf>?1I2@F7XJGOLwaPw0aHv6JlQk*CibHlvhtQh+n7o#vV>chQ#bQ{LFz%KYMB;f5RcLJZ|{Dz5zoovm4j1Z$5Or z!0IU{OMS$)0CdSLw9X@%rm5XC*iFe{jj3^*I6iMDwEep|8CxlYGoX^OPls&GD=HXg zY?D}dx{&0eWhJp!{JIWzH7oc>R+fh|Zq7xF)+94uZZzIJwx^LR3Ud;}R)dlg0d0=_ zOuK@folc2ta3c43t|&M#b-or%mZa)1Iho#;>_JYEj+E^BhjXH}C-yma*T$}R_9@qQ z_;C9(h_6Q7%2x<4$Y$2j<%PP+_` z(S4ZHOnQ#^>}GUgsZm?iPkPtdr2G6a|FkyqHuJR0DQB4La35;W=b7z-;*Clt1}|3E z+}m0->~yGni3&WI?2~-syB{!ajS-WOTF=LgWH^DZ5*;S2-cOG)jMrPbMsL5i&pfia z7R?jC%?S*|%3InwK7=+pb@s^yNnO80tq7riD`IZN=IIa9w9&p)bGp=Tb9ZG!r&f;z zf_LcUGjNOw@6%$G#v@wx*IZ8|%dB3Oi*0yh5}?o#eobe3-)Hy5#6&JZbwsePp3s7A zo?9ajCJ&y5b1J<*sA}0CT+4eu-P(ErvKP%J1YS0y1M5~;2SJcq_b0tY(zAdcZ?LGT z3={#=t=hyhpuO+EN&r9T(d`*4t2%6$9XCgwv@F^v|5W7gjy`U=*!QK%hb3;%ahmwu zb7Rj3iz0PcK;LW-G2jhjp!&8I0X(hD=(sd=E_>Vel3BzV z4FXeiUKs=gjbQ$uQ}6%$0p+#W8w0F<%8ye@+NvLx#M!lFJG{yr5JpcbiH9{i2522Ak@X+(6L@JgKzJr z!vxvL)A~j)b4WR*&uE|XQv`Nvi_q4XlxM{yX*rQ}qCBVkt+owYD$UR5Pm?SbHy&cL z3x0a6$1g=ps9JDVUb|@zS1^BGl+@?lab3I6#9o7!>Y03@Gz|}_npgVH9lVyRu-Om0 zIT}}r9sF7iBrZ86>+>_VaS3UCusALjI`a|h(YfTLZ;yiWEPlRk$y>H{jxbw2leo$K za?R-Fwofv#eNU0Uyu|C1cSl2b^MQMcpxwUviOG%T)(1{a5yBsD-hl1ReV*8XiGFbr zG#akkJS!}H+P7357^ChvQ>8Hf=pv@BFL{a)S02jCQVfZ4-C=?j*|-mfF(I_pBFOwr`7URTsI*H2@j8f((4hIkN-ZBxxRrP2f(nTvcp zayh61c`3^bKO64%nP7j3mm6;s^Yv4j^5N)9dyMq6)_rA3k(qX4ItM>323=jwA3JcqmY=RJ~;&Z z2o4RS694Ng8bPK1x9?XPfB2{y6g7Pqj3}ihbPR<+6HU}a38GVU;4ulaz_Ir(C{Nc5E$4IpDgwD{thCG zL>viBEu$kU$jZxe9JvsMLC6z*HTkF`MaIrHDb8D!#~rC#H*1+E#3PyM=yzSXkjg14w#+ZR>id3YSl64gqaxRDzBeaV zS=T=PcHaUoFE;j!e|o~F(>e_39$^9evScUKfdd^@xeoI2t>c&X&M)S+u6Roc_ThaoeGlr>iV72<~c=JGbAG}%(iw+w_wa{-~A@LRt#X%}gQ zeeOF&Ur$Wsq8x{VNDD3woaSC^ORxKrBaGY{=_#xhBkE7FMMkk7H9)7h9+%!i2sqJWFhV^%YQ<2Z*F-tY~;;Y^SUC2 zY7kC|DKh$Oq%QEz)VI#5Jd9y(P0UrMa2$i;D7fp}v_R)J{1Jr+$Q2Lw?Rw{-*eFtY=KVfNi`Vj*u5f(Mc_ z>6{D-bf#&O=uEaxxTf#eGZX8+6zO|V%0S%Fwx~F2Qn&icmCT2IRNliOU8O~1^JxU- z${x(<|k=`mI9k0$AsRI88g^0)AU>nO=Rbt-n|-AoOqWlED@sHaKx=S zZeS5kPuh(1+cVk))n@~d^Nz>u-&Z<-M91&O4Ntv?L{x&%VFp`x9y&wkzkk;(v0T0^ zt+uOgOo%3>fE)KDnfW`=*w?F%w#tPYB@|_b+INCr(K*x58nUYd3XuSoKt5abx1Xj1 zhlw|TEmrS$RjqG3zH~gR%td&Fzwk!Cbg+6KbvJKOQBU~{{d;EHToB1+=6`6#pF|7c z`?@A%iKczW-4kW_4$VjXt3$eGXS%DUxoxUlK_EUctB`4KT1ue zCV?DvKh6NOf#BBnMnAp$M0a%W@|mJnW1}XWvDcE6-?=^ko`ioJg|V`xVyIu zTCFriVV3LGWQHdpsXY0M>Py7tK-xz^h|WtX%&v^E|DthR^83$rLaPj>#=dk!^bTa@ z)L|6zF_o+dFH1{!=PJKl zfZf?=sM!=p1T!b0j3?RjLoS$Ni#)coNp-bd#A^qM$!VuW3pXhNm#^FQE3gkkcYcX8 zjJzT6uc+#eSc;5*smW!=@tHr ziA;Xt8{NoIdatB-{^x1;y}axm?<9fTMXSoDU!(IVD(%SPB{p?Omd8*0vYXw^@5MiW zZ6J;gUgyq zg4WxNP&=HgrYeIM+JYwMB2p|2598m)ZDu36M?!Qlx*@NLKThbCRYVnb;YPZ;Aci&Mi$#j-6F;XTKm zn)EZIBCFMWlm*o#8D1bM3N=)0uEaU_1O99D7@2Qpot7-jCHTvIrci^Z)Yn{QFtbuc zXJ4sZy!|oOA{OJ9pZ%TWtDLgI?Ta9iNr#@~Cp7#xN`0&*(Q>0zx1nOKB3ZoQ=d z&uKdP?l1RY?^*eO(qCM;GvIf~ZE*5CR(Pc!^SY4C_g4F>$MmS0ZNvg62uk(ZI#?}M zk3UcQh0O;VpF)+W(4jtTCGPt(P7lcF!7TE=WDvZeDx&#IvPB+Q!!P(E5?xQ&i#a>o z*Z4Gw?&cvL9np+XVUFO(AKegccme5`jN5gC6|Ei0Mpbs1$qt<$vFPC2w+p}M)bu2_ zdS8Kdc;^1$LO4mVx>Og$x#wyhexJr)p&+&&#pjq$P`3ImNI@s=x)%d`5%DCStcvUO zP8q6NBBg@OqvIFVL#}Y`;BT`ZMtqJrPu+oGHw%0tD%tSx9MH=0G!6nA3=Uw}uAJ(n z!r_&5B>}M7hLWO%F@sKiOe$Eosa8*W0cm0yJOM2A7JS-0Hcd)h9C3;L0Dbn z;}rU&^d`*%5pMB;F}9Y%MsGS>~W_P@m~c`lr` z-T79$MO|3PfSB}aR{>D)P7Nb|S4Lb-ErR%=9Dr>S6}`^%KZ117r? z%Q&IYZFo4Vq(MNF<>Tx$c{ZD@vC5^Fn0NfShw_R&G^VEkPLx`>UB*HjhYoi{nFko1 zz1?R8&pt6|pY?$#v?|eSBn0&&l`-RHoc=C?&!I?a)e$v6rQ5aYw8(;z>z+7hN1jK9 zS8OxtNO?I(RUtCmc{1Zsc%}pyDI|q@n<|2Pla*toz#2{bn!0S>&6L;h9L%OT-Gs@^ z2w~+LOFt0|KA$ef0Ji<}EH_1LM-VZoAoPZVP}hmPKgc?|pfQ&@ZUU*V8GU*cQ18JT zkxb+2Y^N~n(`u7qoG6xluqnSo)ky;3v3+`^4ruQ5K?p~MXh}esg2=#lLtbrU#^PKL z+sAC}kYgP>p(~AAnD*^sQQI9Lxy2L&a5{&M-^ItHlk{4XTGAP`d2i+$8dJ44EG0NX zh{@3r9@SztC3&{ny3MM*bm4XrzCUr*@HiEFY|#*Sbba}8ONO=u{2KF^%AkdHb zr3)?*;#Jmg6{?8T`KyL&GAE!ap~PddQ97rqWFoLhQhqD2N!cW$($itLy#BZ}6+qE{ ztn|oo2{mS1hqvf8w@DP0!PmF4b0i3eBIV3|ith@y`MF2$NRR+Y=LB@4Dk_i-eZ9+?%v z*bp(Df{k2!lrGA1)-*(IiaW245s!hx@DqXLKUIQ5+j;Qk!oyAh5)>8 zsSs_V82!Me_f3+WC<=BZHdkArwbk;(;0}mB$3fa8)K4>rJ~ zDYi5dPry!@K*fTt4qmg@E;c#r2?xB|a^uV)LDQ|bCbQvL1YPS*1T0z*PENzrtg#AU zGqV7lR`-VFF*BcPvrp2c0}L^U)(qPjUU^JsX?2ra7BmOx2Q}zKbt+Is`DLsK26XZI zuNtJ$vv@Q!I65m1R6M+}@8}ux_wg97IN!DLkyFKlw{XESCGN8=f6VX}`(zQd;Hndh zKH$%SIkdr}x`s=zDgx=f!lurSbbG9{h+ybdlrQ5**nV*xH|6Z?CYy|oYxm;8;Cp`XQ_FPsy-KYtpdI0?yW#eEsdK2G1<0(%6rv*?hgUIb zXA_GlrPjPB!|ySg!q55YeX{s4b$Ts-m=$%oF-0_5k>w+oEt2z+5yvEzWLj^t_&@5v z8NP9=4#vaug68H1zE)Ci{6Z6atWINpD{iZoOd9ld-jDVkqKLSkAf$sJ%g{-5W#Q%{%&iA^7asxF*(|0CH z>d__4bk9%?ksqN$2ePs$*JrUc8HPLj;$+SJ05SgwgT5$rt}BEr@C-6CGonHwa zKlzU9N=K(e;5q}ZF-U_DGI~ANCRah4P>V_oN|1OQJ7_f)+f8&0!iBF;$kT!Umv%Eg zy0qi`xCTa}<1f{~AKo3vrFFXN0~Joie~u%{A`Ezhtuho{twqFS2ODhjhKEKv(ktqvLcAR zNYBv_p`INs_pNkX&9H7&icXR3268EiDwd!xM7&HU5#wQy2aW+7rMGhDqo+8-Kf$R{ zI-cU`nh-Aymjoiv*jxKb^`><9_>igQ&qCp7!PaQ9%4b#STU%-?xv?VRgLn~Yk`P(b zqFdo$qnc6t>aA`3`e2M@6^pZ6;yU7(j@8nn_Z6JhkIn4;dr`q~ovDYIObWr0tQw&4 z&Ps8TL1O1PN30R^#LO>1T^HqCh}drBeQEL{dnpEiMC~uy_VgoI*Y3w+c5Cl9OM4iR zJc9t|am=|O%xa}($dJW8pQlZF2rivEXJ>VX^hk5`Pysx45wP$R_F*=KT|rVBi|E z5>grteO7i&1}kv`Rvt$^G|$k6GoTj)tiJ%^%gm?u*EK^!Mq*q;)wx(yH5A~Ani0f3 znYvWN>-t?HG=TVi;x$B#x=5>>lHrRYE|xq4Mfe>5gN}H5TB_myZtIF0VSnv~W{)~k z^1yYroviPAvHpt#we?6(Ml*0mKDZe3JoPR+7mw^vk8AVrI1AgWoda7cay!@XA-RtV z@U$)Ish1WV?VjyD+XEqh@TFH6)~YORxG7jvW>BKU?brdKwGv^cxuOnN2;(ju0Cye& z*lqeU0Rbon9lWQzV2AK>UD}MX1PZa3@Fj!VlOW@VP>7JQSt@q7&>VXU@?#3NE zapip-jYVYbv){O3f5z2Q)CVg}F(Dr>5&a^QU2(Qs4Sc#reKk+qp@#pxyQ1rxlSUrw zNzrM9D{+Qt*z|y6WcfzX9iR61K5^rQE5`@(9EZ4X7c>QkuX2yICi}~M**Zqv}i4ZqL35LtEoP}_`j8&5z!xnV}y(6p?)-k7EId2AtqCaRlqOeBFH0D7c62K;mynD*7Z$rMF3OZ9Z$ z96^1gMH~MeaFABH2(=N>jVYPV1RA4%661zOxn|VL8!OaDIY)_X{3Nb$Lsxt;bp2S@ zY3WpaM^7{2LOkk4ooVb8_UVzAJ}PF4wDFv^ZMH76XgES@pm+pV#I_G2ELH^?aUZHH zWh`c3j?1Z$+dAP{`px4Ot*t#2Jaj7RhS)WFbujy2Hxs`wMKJmNaV=w3SYdCZe?4FH zQFVTgx08Hegfm*l>9=Z2~!Az#{M+h`*wpkozQr6eWvjUalL+|foQ$ix(T|5d|3B_4m5um zzotX&mru_wEf7oKQmD)xBsaT_uE2a(xcZ+Z-NTHYU80rMdJ-+7E7xheXh(`FFw!yN z&%2B|_6|hR2h>q0lX`-_t$;(MRiOsq0xIAtFK;tg^3??#g-xX2027?}Fd*)pQ&`sE zKa8L}Na(SO`|6jmcJZLEx%EjxQGKVZTx#y6+ZP|O$bgTb;|9aPsQc+Nt8y$Xh^zJ} zrl%$H`}KlYoYo>*-Z_(m?<=s_Gbl&wl$-Wc8)vUM?T|5yT_#4~)X_d*a6xnM%>$mN zIiLKA7lsZd2oo<&f1*>RzPv?L@x0D=DZC13TEA7#fz*w)T^PfYt>W)%vwO7cn9`=< z5c75>(|lUp_u;8zk&+hUavG(~>JB-AImh4=o=@yQHYBFSDLiWG29&+IQ?nH(^ol6w~dwlKA4h*);n3-tkO$?%WFrtlUIhy|2R*IMP z@I6Tjx{-psQdlOXmPU!~G?%WKkWJ1+z29N)t;(kYw3>Y%Z)PLQ1=}}`ANzhAj@q7{ zo^J$7J?VoPObe+^*O*NcEQga?1b?X?6TDzv@8^|TV;&B;iI-8P9l4wjA9-~1{ij*Y zKw!hwqgUn1I{+IOYme8j60I5U;XtYb7+nR3ARrh74uT~f%00t@CCbXvNW(aQ{~it( z4#0mtKu{=L{4Wd+6a5<#7fXznr@?_H7RXEDfD%{a`2>J}dWnH=x&MeDDlQI51S!x+ z|7C%K#UOup1EFGKpuaE}9G)2bKNx^8Fbw{uH%t@+{R@LZAcG z?Y9hKe{h39VBnuQfItxFpPdAOpfE@xmD2y11^D*-$qI&o{)z;?ZEvE*EyG{j5Fq5Q sDj=d_e@z_%gNY_qD$xl1#UKiT|CIw&TpX6TtE55<1@Q7JYA6Bz4{*_xYybcN delta 7074 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 DK7Xjv diff --git a/thesis-evaluation/figures/totalCircuitCollections.pdf b/thesis-evaluation/figures/totalCircuitCollections.pdf index 6bfeeb6b7cdbc88f84c6d53555c424946638ab2e..fa8cd6dd93271717c3028ae4ca690080f5ac19e5 100644 GIT binary patch delta 21 ccmX@y!FafXaRaLZo3Wv}v4!DgZU+k%08Zit1poj5 delta 21 ccmX@y!FafXaRaLZn~|ZJp{4O=ZU+k%08Y^c1ONa4 diff --git a/thesis-evaluation/figures/totalSingleQubitDecompositions.pdf b/thesis-evaluation/figures/totalSingleQubitDecompositions.pdf index 6200075d015c1dcbcc5265329d08b47043b4bce6..6de297ae47c44adefc43506abbb2b9f219e40d53 100644 GIT binary patch delta 7014 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 HE;9ZPhWxf{ diff --git a/thesis-evaluation/figures/totalTouchedGates.pdf b/thesis-evaluation/figures/totalTouchedGates.pdf index 43c452932e1bda5e9a7891993fa95a67e7516fe3..c4b11c7c89adc20dfcaba3c7b4fe62b02d27bece 100644 GIT binary patch delta 22 dcmcaRnepyq#tq4??8b)X#ukQVo3mVtSpZ_52Z#Uw delta 22 dcmcaRnepyq#tq4?>_&!WhL*v4!J=&#>mHsfg*ZUwKOnUF?(pTm?Xo*#+1|79s z{U1LbUS!+7N88a}k`8KLS+BbA6$wB;IK5627&2`bAs2-lEspDHsApIM=ilv_?o&oG zp7I*9DFz6BIU0i`IKQ_NE1GV2d~vb1b6_4PO>Z8QJkm~J`hAW5$HwLJ)9<_@0cTF% z*YP;#Lo4HB+1$8B?cd#3gY=3hn{*l=$u~Isoh3a_OVQzzDDfK~$k|dJ1c-guK*(+# z7#&WlW`6&CwxBs{Hr5CmU_5E^gO(xmrPm7 ztBBuz(luF<9!34a(lh9}ThlO&pffMBzox;`WurAY&7MD0&|DmIx2m@58kea=qN9~5 zd~2oz(u2O&5Qv$pTz>EVs`7YxqqcS2v53tac@6RGonW!J z{FTO?C*_?flk1$!S^Go`lg--}7BZv~`mDfflxj9O5 z@R4SsA<`!qVvwn{wT1yuT#>_V)~>HHQ8{#*U3lo_6r=LjsM2<7lg-c?(-wqjPIOl+ zx898@=urjG%f{O_&ie+|$V^vyd{y|g zvw7JkPp^;0-;xxBT)vu7IND%&rtUQ-PHgzTSu))!ooRhDKkclUd_;gURu<}f{+glB zPVMD<>l+R#2hN0}PLjcqD1X*`5|weuq{L42^?Uu$9y_8^9n-8ahbp)GW*?0fn2Nf6 zbBkSp6*MDPB;77Wi{pHD!49l9THnyyn^v{_uAyDP!-5*)5t`nePpv>O$GUNO8YxxQ zVWwoF-ZE{-$Z5a5Gl@^(5K43T%G>U3MoIQBc+VF`4wG!UNmYCfm6w)N)hEynoXYgV zUW&bj3+rjY)h0?gVT|&-G2~3C*RkY6W-9%u48{Yg{I`4`-g(z_Bg=20u3k0l=C>w-h4U7XrY=fsvbKfDVDT#9s3PRnEBBxc=ur&&m&P)<7l^CM=Y znXxJvoB;&0t{U|(OO_=fV%pGz!7I>*Q}LQ^LMUgQc1Nob^5`0UP3jq5GQDRp6WuQJ z4kR5gji;HZDtQN;qE+A1DmNyW91V+2%Z!We;%||ZvP>PFim6$Q){rmF7Ddt~SXnFP z$}ZDB^qz!@=*YX+f(#3-mNsu&E5Ye(7%C(Ao-a(B;PV#rbS?)PCyb!$zs*SS>4lUz zr)QR}PBI5~$v>INf+leeOVBkyDdI(JyFrbnE1WR_en_@j0^M} z+3@TLZx}IDtQj0|dW0y-en@D3G50hf-d`?QD%+8Cthm=V$otxD(o=~cU2qqiW!RO5 z_f039CBjzyQ8+;34e$UBxLuYM5>5bu%XJ{$Xg6{?a1z~t#+Nr}6H~#YqbNV}jo2kI z*IS>PwBk_|eC;q1wvem}AV^fga&In6VEUZsd(lzMoIxW8KWH+{g2{+qnjvfVi7X7;IP_QaDxV zremrNP$%mkZd)I>hmB5aaQpjW>zFr+xpm$H`;4RV>|~X_TL+}@XIa#|xXW#_m1J_q z9ZGe3SlT0j`reS2MbR{uG^=lw%@(9`i>F7C^s+lE1`HI>MJ(y-k#GSk5;*Glkz0bK zX$({X?E1xmn(Js^4;pUr0x<=-O2X7ld{?HW6!soGRRI!%fFIZK{kuha6{5JVvmqV| zLDJ}}qO@cV`j`vrwrPTm^RDm;Z2TggorIZQ7BO&>i@1a8x&SQjP891iJDY<}@D+Tc ztJHG}QH~`ywXYIktbp%g94Ga1Bdb}>I*gJLlLmO~=uRG^0phJ2YvhIP1*z=z1(%*3 z)(p;S79EqcRmO_f z8giW%UE%E&y&QtDuu|>~cfh7;L~ZVa;VCL$dY30A%L-AOaPA^0GNVzRoZ#JrLa}6h zL3c$9_}Z9>=5-u&2-)nFrImig_=h$zO%X0y5Z3|klI$p^tVC)ynf_wL%)ioC3Wb@DJ}p&H8_&-QcM2B#}h-f%AfEnS@W;^ z=V#|-)(VC^bYi;sgeadr*077i;(<*mB2OPbl~TJJYO-Z;%=sb~Nb3Iqs%8H29-#{U zrdXK7DFrW)MK)K~t31DK%md}C%bN8R!|!PT+3i-#E@`rNUr*;*F!2Fz>^a9IS1RFM^z}C0xy;cfVFzutShx`;0+8S_t8rAnSYq9UJWE5s5KTvCP7V8!LSSd7 zJDdHHMj$sz7CGOl48|?-SPMJld#&73_&qGno2y=pL-Rt-PY?afG;vdC9|E1d+P^!d zN%0^m8OF0`jlhuW(1)FXQ8BziqK-BDw+F(GEMNK&%(|)FHOS44=#K6$#Lya|kL>=n zfIw^(8?w+tpEqU#xkXc z5P0h_lOW%Yv_|~t@--K)Z%-e)57D=e4+5G3wRqck24kcUVW95_7z$^{T(?IblSu*z` zzAuQ{d$vV5=GmF3<(GTdxb(+K@vg;(pbI0Z6wc$x0X&?|z%8bnIe#*gFAGK8H54uq4!K!(ui zqw_xeCb4W78S5y#LC#A`T2dP{P(K;JHdNVjlxym~bP90hEXk{RJj#(;o8#6JjxHl! z+uf2_h_>B)HyjB_X32wc^~4tV^wF0FG-K6(t53LkH+WhJZ>@R{u{7^7FD6gKWDS;unBRv$yfT_?D1B$GtW;?y+4K7cKED(enfr$}OpX*$mYg z;ZrX+9=V5KX1Xl1En}O8E>_qP`+C`hHANhuE9xK- zey@U52XH0vgXQjIG5Dr0e%0w} z_l3ncqGAxMXhMb3V?O|VD9~e(#Q*T@sRh>~-l`WZj+7STiAE&WiWn@R>Mm18o!)B6 zP52HoUoyA8p*uSt`g(8qGPO3YL|j7hTzCuwL~31yZr9@oIQW?6EMyz4M&F?@6L%ff zYSZ4?*b5a#jcfQ}M_T6!Lzl?|6`{%Q`7AkCBRzy0Vo8-t{s@T>BohX%O$_+xYEw( zVD*weydc52lB{?0)AWMU;CjC8(Q{i&w80i?5+<}0KFMD_CG7aE#~^~u3tN#bza@X@H<;t&Tn870XqM1o$X)D8KV$QM@*jq2q<>e>x z8C@b?Cl`q3CMB6=|JC9$vzJwk_ zoaJqp$T1hLB31roUv62UkehAfdhjhqA~ow$x3Ll)BD)X=9~SABZ$RTuTWL@d1An%M z!?A|596)~?zO2@5tS^LM@@`qa0y?mS0e^(hx6TCz!^oHMZ|s2oU;V>jcKg2%ow(0J zZ_d;gCRB<#pRnA0JZ+$I-`B5Re_Yae=T!mSEH|{VQKgmNPaw;a$#ggaJ-7l$=1(uz z^<=wC3L;g^pyldtc*Vj->lzCsRTSsq>KJmaGI~vFCuWm?Pz3?nS!s#XNELs+@!yiJ zkENY{9BOTZOvqF0APdrp_+n|`b&TWk6t39?6nU;#8d*IDS&~RY5}iUCIhx?ef39l^ zV0kVOA84+;Z?qEKpg7&^2jmNx0`)scHU~Di%yBGT6GzT0S38CxHtJgA%@fN$nF8|OQKLr;Sugjf;5jW@_c*C z%%-AzY7F?MO<6XBzUsM_%op7Cy>%zFtCDGu$=vhmP9m3GaKIR|Vg1ilpe}Egng?lLj7qJzLs%REKPfM_-{{s=7AY ze3GrLnD3D|5xFuE7g6>8j@b`giPs(hN8jP;koV`kOYdXNLj~e4f&z|;yQJGhTctAN zJ#rYBKA@-TktEgeRCooHAk+!)@mrEQ$;XuOpV%#~JoQ1aQk@c2qf2PA+6xT!%X z$+W~wp2B{)+y~f)Gi3_-W$N;}#nbAT%LlS6qg_e)=DUA*`*H?tb&VXVXA!32{*Iou z>=C@`0Ua4@TO@op)jG@SjI|NA+y3Sb8QL#;#sYZYN>-w|lH`5M#cOJwpzBj}VqG_92rq?`VKDsJj3h8cy14X*0DYbPTcl*nzUk+V-0Rbup(8(tU4HHaP z$M2eCjgcmLRt!GHIi=Cq^#x80aKCg8Q{>PdUtoHKJFi2wsYmDdA^)`Um@>-(t@2iv zmMGdiKXsg5e@}2vUbXq$VhHoK$TyTD)Jq~hETTnQWGu1GQkWBmqLAbk_iz??f0>^~ zv$de=9yx&Oy<$RP;ZrNyzHcwCgi(VxTV#KM(42k)UL_q5x`+L&Xo&?FVC}i$Im-mx0$5eH7V(jO!K6$)sFuPi{kX3 z@QgL~;hS6y)eNej$^S_Q$xX*6I(VXSxRi1+4U@z~kr(I`jX2ZYlIQxoOO;{inQv5D zV80`ki?5b$NAKaBUdb8}f54jSzKPhfY-iMif1mp(vQW_7S-M!cVZROUI!%`R6j96= z_w{SE`{IxN1@oY@BOmENzk`*toS-w$^YxX3`bP#@*0dF>3mOC7eu|4PG1BhQW!{vE z5$Mmk$QJJJ)-5vyVYJfjNacL$f^SuZUHQw>4ZQS=^`oQnF$s==KGEQcWxApo3!O0Z zBJtx58_}0tADj^U2jmkPn)uT9d*Ma)9Z}8cmaN{o#IIirJWIibLfteVRRe}csPgB@ z<&lcZ8SA>4eanq@8Azk@({vqvQw?FN_h^{;D!pu@T_$q8%(saUR9SQ<(mf-^F#id% zcEliI^DD=>o%AjL)8YA7FVTKyJLOezS~)*}GR@i#H%B7^wk(>z;YA8aFCjgSO(onN znk|_!&nMm%^*1pxGKOjF>SRibe$k@xjqIcTvKTW*{}T0)X(Z=c&sLi*sWS0GNnhIfsP@GvLn>oSo%O8oOFlkfJiJ38><(?Ld~ z79;w6FD_^C;@giEtsIa+MBp>CApMc55dX$-b+d~vnw6h<%~r{E*_2@tFJG~R`!f9i zx+`%hNLU;^`Q|cT;N{NFvVDfpx=b~QM#6lWjo&rW>L|pq-1-!V#s-(}M=od|T&CGa z5$&FPzhlGl2LyhZrM<$lsbAB4(D68B&_=!g-f5teT~H7_XDH#ttEYSCDemTcoA5=u z#&U;}3Tw%0ln9gqlNR>IW}w*dr#>R(*(;O$<_!hjTRREmi8J|=zJ83UpQW*0sw=>&hYQ zdx<=!R&M-EF)*8o2#=uCJ}F3boI~!(%=ToG^J1Z$jcvLcis{bKx>e}fY+_p4;aocm zF0Q5FLeTtV8;Z9I82Xq$Q%Xuqr&=uH;55CKbZgPFR&SC``CPd@##dM(Z zWR@i=nP2r5-k6q!SIK1Q@&oh9v(b!X0`}Mp%;E8w*2VD;!|ar*YZsSxnb|`zCRB8U zFhOC_T3@PVs!I?-LCigTX0(t_i)#;vY+H!vCI}eBHoO8Wem7*3naL0878t_8!i9AxBZej>kpe?mr*I?_STjH%sX6U z^G$GcaKS?A8R~sP=@TEkTeqi5fd&t0j=DhdgfSry@ zeDd+w=GB3BPpPdK+--7^&l}E!uhj-#;G5s_x4{^(v)3ZoDDW}F9MqT~_6Uq5Cr#}w z4mNxY2NyL)hjSX!#!X!-#ubW>QRkt?jB)#8rg*7q&3U}>F#>!vm`z?q%y&L&j4WRZ zCS8EKmV!SDAF~LeuB{LN<71}4)R+U%3dT#22J;adiw~~#5qwBMB;e-gW{Ww2N?!&D zxBvxQfwhuC1jJ~-FFg<<3x6c7sjN4gJ$3jUM; z0{3mbZQX!S$Zx&l10WRoTlTQDwgp0k{`utzgu;GH4k%lk9*4>E6V%hk9SHpe3lQ)E zLVtt$0EK>o`q`q~fkNQgcjCeL_=1?bl8;y*U}1;|SeOSS$N>UzZ~>*H0Jd&6?p98K z2Pj)R00<}q!CaCOa1a6gjO*tEbOQo@31DGi2ri;Obue5sf9OPoa2fuo6aL$us3;VZ zE+s+;69$W7rlnZXFqkMVoqwN%35wvD`$Gp3#xeD8osf{o->4ACpLQUa@XzY}>nsQ+ zDvVVGkQ;^Q6> zVWGbqisD@U$M7FJQK6q5^7Hvm(4xXZe`X8@;X?ew4h$0f*@gc)3kIPfus<^fgM`3; z8^ZoBI!IXLFAy*o{AbW$u%O7F^n<}dg1-{M<^O+~fWe}I|3%HOP{D$N;JM+9l1S1FM$O_~MiMT#g@ zX$w*WMJWPCK)$%U-@SL`{F-;(IrBWv%sF#jrBlC;psq=wCK%sN;Ob?1@w)nBQIKXu zt4zFi4n2J$RbAxL`6Y8#D}{Kg?lkV>BPnlv z`jV?BS->v?_sHAX)mV+)lIVTGmx0mM2C=x-&Pi~rS>D2@@%}$FX14d(9C0{otqqyX zbj;;%^30KGp3vI9ywdwJA3L9y7fh@0wP5$%8rz}KhdtZYN|Jxepq)W2;%$c!Zf3c> z>mJHdMhpgkPL4-|QtJEN=S{(`VcT@AyQgZd9|BnLVp^ezsXz(>vZ|>c}^GBQz|_I9-MZ7+}iXs^+F;`vb2y ztN*T3IBassq$1GT)N-v|VIpIKEl7N5D&Flj9w{KlQJov3Q~e==SFcp6+}_4m*1>5k z6Y2IWi7?$){aT}BW^&QwK~TT7*VQkXnltY$%g{+rA`s2`cL8^Xv6bx2IjW^@vWkx; zyp2IskGtdNR~sKCcAxHJzFsp`;ags}$Ravz#{0IWXKdPg;m%dYv_B&)5fA29X7=rb z`jTZWQ=7|R){;do6`vPqnhQu*_{`3B1lhSSj|dTp$GD7Yi?Hp{98|y0-^;`55DA|{?3uX#%R<&lT{ptrHO6%|8k3Su^yqxo(PQB~=0{!*2 z)~0yXRb>T%S|4|Vho!!%(q9?Y^#=P_gxy^q=&9D(Jn3V`@$abgq0Yu8OsjaUn<%Z2 ze_ksk_=^&CB6F_L;Qcn4D>QarT=J;G6?xP#S4_-lWy+V2Q+-_Ya?nnuoe190&d1CP zN|nihbZ_I<=~S6VHw9k@zI@Co-Ilwg=75CBnc~Vw_=a|dm>=UF~HXS3V_uPG8hy#Cuf`BkSObaDaxYD?CM1>cCOF=bMH(zL}GldvaC7*-!ekhUJl= zb!OQEExJWx{T8K40eFV8(`*yIE&(x{uSoEH>7fj5(%>4EIhofStr>0Lr`UE4A#%(9 zQ&QGg>06Vjr+vkxNZlt@v+5T`#CtDiCKy2IHk<$2Zm{l6EH`n(m7oEd%i^R&WRD5CxE8)oKTFVS}JQb zLtEF)cK7E|I6uYqYYP66yH!E!6`wx0;R?<)IR1aY-9w zns&RXeI-J0n(RYPZ+=ZdHxS)4K57M zs-M>LBjDJu&+0PyVf;6d0md-PjzmP*XVctdt(dqQ9)({WrDPW58!bDZ+hQ5+VeuH3t|m(m*; z7P^IcTCWI}q#d4NbgIH_Sv+fJe?rJ7ugctv%GXx9A7c|{5hnLgIAYx_Gg<3ioX5(w zroul}U=w3G8O7jQud0@nYg6U8@N7q{9CQzdnwiL2jm5O3sCe!B-v-N^A8F8zhEpZOHvW zZ)^N)eXLjPMrvuq=@xG5um@~c$vDsd?SmX~|sNjZf zNdoh4Z^gx=!@SpQ`npd0VCS%zzB7sU`j<0eqG8Qr!sBVAN^^6 znVa=Z3r8;K4f#sy)-`68gd}dgQsNDVEZY#aV(hsx7)GzyW#=#{-eDxwvREFmzDYOD zqr=A_VRn#Qf?Yj(gCL(@R~nV+8d;j8W$?ah?b8~6uH2sr4Cg#trXOpO3_Eahd+A2o zHB+6acd_nXPHzvV_Nt7OLpN(+B9C7+OA@~ceJIx)c#*Y|Dl5CrS2+C9p~z*YBVs6m z3)L*cAHDy4gJ19J-bJCYx>c*uDco4&h6cOUi#-xH_c6dr%`p zaaDRhhIr7`AGEk6?Y3=(&B(R&&yAgfIzJIpr&KI$k1`3y}zE_f}u*-vE0dgUKM zNaBAzA?=hWj|^d`RGV~by~g#sK`B?of^Oagh)VoHaevU;C24#2e0YmS){NloFGPWT72^N6 z$@3X-sBodjV~h<_^D7>80+K!A9T!08_3Wy%$Y$i~4rNp6EgH?tZ;dA z8b@lAn&F0B3|!0T0g^CJEvP=`(1q>-st?I zHc8aa9f8n$=7f**#2C0?Z{{SCRAcndnDad}7dBeg&(ngaBp&$o1xYO#^vdT2U))FC z|GdL9#qL@SNJ7OtUxc#k5WWC%A%;g=>wu3t1K<)~tORKL)|?#cGPT2P9`XZD10(&R z4}bpL-1|n39UYw~cRyS1ZW7EC_S&XY%edTLlYNLjJjau zXSJH^IjZK*7>0jvGv8j~RPx*{&ER0MC+@1Tnx1+vLX z=6wLhA9g+Fz^&Vi+vn-~-^HG*GYO=*hoLS=pkkzFou^GIPQQ<9UUGi2J1P6*?7cTG z$jiF^oTpqybTykB2? z2v6i)cOSd(o)FD7X96SjZhOlOu7p^nO!E=_5Ng@QX(3jJTOA#I$A?9l>Mkr8=PPX2 zS|1cXe1t3;Tc{oPcPDHnN)-XOoY}Zj`3j8&UiRvJ?k|)*u&H-R`o7n(-1(l@pjJL7 zqom9Psj)b{QZ(q)P*=Yx#74x0UknZp>uS=n;(#*-NK2L5MHAi)L4yLW3}Y6wF`Xe` zF^}E%Ldd=bFM|-!m|75o{W~zndTYbN$oTc^SWh9|!sKV6*4A zJdKg8eIv#vBDn?Zntu7n2PnI&6wd}+Jy$V!qx@T^wxn zG+)o1I@7$pC~wgr8yuZKlSNw#$OLpH#x<*V%h8#v-#E0-*cmt{5b}7i;(Mdt+Rg!v z12ro)Xxd>nxbuv|HM93I&sG0#(pSY^&d+PNa6kTxBZ#e@**^U>Rw@WV*jk!qY4v3# zY-xdO;FFwLE{6DQ!XfgN!3yKj#0>L;jqhrF;PZYRbFmstchWUYVy6iMf?tDw{21Eq z&7Tk6+3sDQZ5r^794srD88$fF{xZ58)5RKakMDSVR8RwWf9gXnIC&uN_;Bb`ej3?Yl6>Lv*T|+X7|5ds2IIaQIbzOl0RRq*ta)%KTnSHx$<>U^c^NQ*3T6_M{&`3+*o0Y_PQy8!R^HUhm=L=Mg7DLAI~Hwh(ln0bkdChWo7wdmBdIXPsQx;o%eO z$7T(Pb=yOD^4ruMZz0;-g+XMe?w{0yafief)0u1|5zLJOmvRe zfHVkSPC<(qz8$_wV(*pXHBn}~8P&aw91D4AP=k2&AvI(t)n9L16m&C^WxFNE^XJ%d zVh^G5#|QazdD~kl39pTlk2YX}s|m);m2Qi=IS0J9#!1@NsZ@9x`btH$7b37z3>Wa& z!Z+)fE56Eq$6$&j`Lh!_*FUZA+FocQS>;qLv3re3P=}mES~F>aOC|O9m6E zbuy9SNCeE=LRhKtRf-G8fh~lSc;Qh!h)Bppt9hJy;^DA(UK3O|OVwu291xM_# zRt3QBtn2b%%l74aV$cz64tmP=?LESgY<4*EM9+Qn)uYF%=FVLXk>D9wZwv4*8Vmiv-l)LEWb&Dxv`Ody-IU;s;3p5rg?a6u__% zq1WSyJl6sBZ?C(aA}UEs(*eOwK(I4IeVp_uS^^O43WT93+_V|Mt}fO^{|D3u2>%O|0qk$>?g~Ue>LnFIsHvgE7R7LW2`CbZhDnM- zBm^N4L1Cb@^#9k^*WKEN0Rluw5Y3dp1O)O|=3fiw0%Z8hfFfYO)co#4QPlkALm(&& zfA^tI=0l(Ze+`9M;%LWoStS83oV7>X#Ne2oA{K~E%vNuJmYg(1-=$iO5ZzYAF|xPj!*Iq`!BCl6`)&Q zH~vEAWuqE#H*5?%Zm>y7^E`l88(0FBW^>lh5wB&Z6rVY*)SA}AwPFJA$C3vrJof}0 zY<#)&>+Ixbe|tdB!q2Z&i`?gnr=|)$=l6bfuPhyr|2z#oShPDkJ|FjYdbV9<=NC26 z;qiSj<461JFYg?DPmpUF&!*45_1tRTy%o?Mmu>p`fxF^+Ma5(M@rk?X@YBW(7OK+(SsQ%bVAu);C?TTZud3?@ic<`}R(%rsky^#u;@4Lq+tZ84i@@@sO z6qU@xev*0270F0xp2o_Ns1_{fmtRxroP6pCKk^quzu8{4vbs#37##pby<0z~Wowz? zb4}&$V6C##xlg;Y0$;vqNOG%Q@6(LgLXwi}P*|q(MJ|#Wz>V=US#Ul7|6`lH}L<33e9*U2&H?<_y zP=u6oEepdQSMz*YhE!4_12_4-=|fVmc)3sUs7M8uXs{UY@ukhPEOz?uoDRU?3af@1 z$U{ZjWs3)SBOflTUO84c0o{US0st*wXW&m_r+ePeGT0ePo(FrqHq!q zKk_qPPvd~Dv;U0P|7F0mn^nu8ZY(J25MtxOl=~urwyVR$br)NyjJ&SJM_x`QUSa(! z>)Gm8>C>&D&G>bOd}k$fpVHin&sEh>77KyE`Unf#N>vFmGht&?~*;RK7&eqhc zA+X)0LfUph8mnnC4MjpMqs7;ej>fB3MVUPN3>-s8(tU=Zbea!uP~@?+2{cAtck>6L zZwgls}bsv*(9(F;o@HrzDU3xzaM@HG?e!h;wIio)}g?=hP7)_UY zAc0&KvKv9ur{uh=B)&|Ps|B{ytw1&i3d>cKa9`!R+I4Xy&@5h*Y;0z(HmJbQQr2fQ z6`2vLSn>FfH9{tKL0a|j(#coYk+m$?SkcIy1)^`RdI(cf*}LOmn(JUeKg_jZ7l0fN zVy<CY>=Bkk>@#Ee6-3OIV~D1jC_OPZ<^j~gY!n58Rj72tHa&Aje-+Ga;~qwYh45AA6fbU<_a-qW%w^R)1qs%CB-_v_r) zvQ9-|+yelG2Y(Tp`J(4-hdMc@BL-dzN4Mzs@o}%bZOXQg-VcoMMsg1FT=R>_B!RGN zD&Ig)69y6t0ioDN{dtf1TG=BD(HA*0sdulYra{XMuK6_BHuXNv`-!IVs{n*zY{D&B zc$oDtOhUVT*(n0=F}C?vQSJliiFK%)-XzUv3hz8OZQq zJk8I{$hmT+Wf?i`HVn6Qz06`z|#lnjcGrz`ptlED?=j663N=M(&e z@J!t&XiVa@bo^Ye27@@SIEwHEZj|U08GH+FG<`j7187&V71i0F<(?Cl@<_ADieZDw zk3dY)I^2VUhn372EJSsl&0rezxWmHz^v1-XXk~hV!REA3v%rWuv<%Dt--3=2e{Z>kHdI>#l`y0~&d{dvmcU+W!GAQl4Tye)z&T?{ssrjwi{Q#4urID6^ zX;20y6I0+o=A`WbXjWtG&s}z_0~>xk5V!=T}~~Jkltz)6r?U0gCePS zOLq2tzLms~9qYf6!Xl309K1dJOoEMy&D~EdLtv;neqF{Rcprq!Ou!`=>L&28w`0@4 z&Qx)M3Q$2cm@@x6YNN5Faj3Y~9X=a3yVJ3oNQt6=YJFob;K zJe3#A9%5U@7wGN;K_#V(X=zYYB8$x-*Bz{%-mD$jd>`{z-wDEgk^P(b>I;!LZi55v>DCEtYj2TQlO7&%Ev=@}O-v(E~c$r+u1&baXcdc_l21IWJJg zgMBIH)qC9fnYr~hD5RB}3n#AC;foSV_;u;;&A(>Pqmoue${JM-FEC@j4Vnpq+t!8FjxzeSYM?+%Oy|8yAGJN2*phr9JreXWp-|fxt=z1 z4swXT1C#($?-XyquZUB(JnmVa)Jag{7}Z2;Bhh8rCXad@nyv5}`b-Z~J*2C~>qfpR zC=!z9uvW!HUEp z;mC)|K}O*()!1vWSl12(W_?ZrQ6Wjg{Hq*X)G~edD7%9x5{TneoLvfklXY~5Yh!z+ z+Ug6I$zyyULsQunmeCJ`S_U>hvHCek zLVbF^=CvU9oBMhk(!OPyg9y3!;46WmwWu=s9TsveKK5Q~^@6J-GFH$t`$?!pdH>Z*N<7TNmggtbNqH)aDyE~OAP36=B$VQR|%rn<`jlG8) ziI|~K+{3b}pYZ_h`*VuTm7@>*mMFg=3RY8cn0sEEkqB*Fki}yIa>z%_12h+Wv}Lpw zz~zI;Fg1xcx4k)GclF=Ab1>DvY}L_0rMT>HVcmfACbcZZ#eVng*SC|_O9!!|ZmkF> znQy5Ma>Xy{c!&F|+70z5dqz?}5&|gIh zNxZWglQ>=|Ol~Vno4(vn(Kn#Y6!>)-+T+^CjAZu2IXe~e;7V_Dk@2~e5R9neJ`tiX zjFywzCu|->Hj&#=4ro=VLPKYL<}96IR2Qo|?_u%~DiHI#9m6!37TM<)!Z|7v#)xY^Vx93hZ^B8?x9WIg zps8i^bRWm(Zvnms#|* zS(D~rlaa2ph%v<8)SL%FvV@*|QCuc!JjqpV|LV%bntq3@!iSv$KKvg4i=}z}yEjEd zCh^5#yTjPztV;Ys=}nxjgYg|%WHNrvgu#opEWPVvR#cqz2Z;osGOezQgd#*Kd{pfY zPa%{v5p`4`;dR}KPeYy^QU8bBAb3u>s z{&L}i8ljZC>&}rc_3RPD$tcg5UQK~99?fskzX3n1mo>dtU7;aOs51ZENlb?g@V~HwL)m#$E+^bb>kq>m9CK3u@>}V_%pJ zGpo=r2duyyRAF-mne_;P_0$va9PUWdN@v1z!X|VdaD=l@D}T^JXLsM&e9h6-Wn0Xg zE_S2V#-uDz`3mDBP_`ekP`#w&8GjWz;xj3y_aKjiWj0Mb4UJ3XNft7C^nr-{t4^^d zY2WfR+W5e18$W_EX;ZfSI50yu#G|9+l9^&coqA)wkbY(dvLuR*#xCW4lXKV6*FA&k z2V!KkPYt9~Gz+~!KAors_;*C6maAOfHOp%Bpi@<_B#+fLnT_OG)Vm-F<8&1~k0Cho zQ=u48hCf`+^Ir^&+83Ei2z^X3e{60w!sLt#&Vg~maB2|fj7IFXsN>0`krJq zH(g$20`;jdXh+7FZaJDA=O&A>&?6X^ed;%C?Gr^_=-VLHOIDD2=qTZI@S zQd?q>>SB*Tw`ep`{xE-iyYe#BP7^TWQnp?2b6u9|OzzO{3-cFYhMtQUY=dN59Hg$( z(D%Y*vgG8`&dX%!r1xjSvh3V-H3evQxI>MTy)q<{JY|74(``1fCU$F}w5*vfF zOzRGzI-HWgdup*cP8h;4)`a4Aj8}q0QY$;s66LdqF@8PoiXZJnh&RxTNHiHp(5vV= zU#N*elh)lru~JGi>pW60j`7MNUf|6$^7(?fU**t7NIqK2o&pNR7+~D8Ezw1cw-c$i zQ-wRwE+Vvwx-B!Ph9YBZ#i370D~*z}KlGuM2?|2$uFK{PuoWR;CXF|I1Fym*Bk$$Y zlyyB4)+AmSSYUw(w<2d>PC44`>%2gWda_+s7qXPPR@-Dkt*8|jGL*eE`GlGBG5^9Q z?(M5eYq-*>XWjX{FW%b9u@yq@Kgi~N!;yw`_})u+=X70D*?afov039BfrWKL*X;yA z%Cw5_d-Gb!?_~}h1jxlCfy)w)kQmD(PevYqmI}FHy*B|xX>xfxcpc_x1^oP&zGbLW ze(~~b_t&S=G@mZk8vxQ&AOU(cSBa>by08AkQWdw)m=nJztsE`$c}!L4(9fjo-lII;8~4LY8)L0WEzU+dnkrng6c(zz5A7a{#bIb;|fgumTo3iw9yHGy3Wde1>+Z6a{hQanC{M3IQ zJzt1_my=lo2k#mBh+UxQ(@Ev`4I5R)QK0)O(%L!-Q#@h7FGc6gp`*$#{EeSBbV~RN zNQ#!5)U z^_uM9z1szeOg{9|P=kjWK?R*}_^blIZ6&x5TJ3J}CeC~L@=GuKUK;U&Z5qhF!GMg6 zsAcrcmL*^^Y!A!*3*`e_22q4*23SZ<`0SGkOFKl;bHr$rAfU;*e!9tno_y{76R;1G z9t1V$AbKDBf8LuzXDib~77&|s)1mv(4n5OsL0VJDaWu%WrPOT0llo(@y!^Yt3l#F3Tt6H*GjOv~j)SjL;j`0qcf86g$9cCA?ns9PCVYtJDTcD9t8FX0 zZN2Ff=-y;tGM()~8`}p03yyI)BQg;8M?eqDRqHo4=P%)tJrA~clZoz$@TXiFN&E+X zBSj-KYB_x~ULZ_~?V;==i2nz#e2Lo0bZhzA^Jcb+ki{lgGdRCXM8E>=`T;c!r=yNb z;?SH$e$5Wer7?`Z#_f-QuunX&o>eB+N=>YF#CzSrRyNBLlV!_SNZZHYrfbwI_;2py z5V^B`n@AmcB6UKE)X^hS=h6Q~9R{mz+ZQg^ux+p20e1abAT`~G((2BF(#Q^?=E$=q z@Q%_J4tM=a74+>O{ySceB)&}MS+_;BP7p=G2hZniB^B|{+zt@uR@8_^*fq|&ANI;z zn30t@Si>{+s;4ec0ngnnhoe^}b~#^QyEt+wTsCxaY|Y}%TmeyZO8#-b@6qPF(DFvg z$`Z|s^+Q^odP*8>x4Gwg+?0wi)v7cc9FV9%b1+gfgJUqOfL&4MT-qG@g$*C6ZY5Wn z?X6;>=tTJc79D?M9lQS)ozi>IpiMg)rr)CThA2Ap^+Hu1)-tfV<_m(5g>3!lT-ZBq zG4nyhItt=T%|S(#YVI3EYWy9Ic8eUC_9c$SP zqd}KpY)s{}z;EK@cWC<;3Wrt?q5}07U7(>JyBK3$|CaUXH^ufQym^St%*XHqIg3qE zx*TX+_=SSyTH+7)-iO+}^^vV1{n@VDkGt?I^PavN1ZBnIOuhFJ5ZAji_sJMyTW@D>w{H)MQr*n||0kb74_d zVgHuV8T-g@{RpL3@8(}D6&1A!zp;At9l{i?%T=?URm1NmC+y5n^uy9p2Y6ZUk(~dK zS1gg69z<>;>zdih{#VF$XDN@b;h0t$+LViz{0ui91=1@_O zlDgi`LuoZq{|&p-{1*Rk?>09 zhyGqzGWy&Q{|(|Z=gVZYb(@{X0YLb7TB?7t@R3h?UHqXquolbV0-AnEK$3615f73CQqnWNIp_^IIg?evU{YBzV zX(R3w|0k{dt;=~1le6|AC1@kI(IneRBNyUM@rtt*G-7~D5qC=9?|rDc=n4gRUfOo} z`RajP&Nl29j$9s>tvopO>2ZRZH@=Z&ZXcNMjU2)R#Jr-s^tSCGkZ>ea9-BpU%^ zxCHr(i62@eE74u&=feJ7P9q}WU})tIVyhLWW+NZv=xF(4YP&YHUE!B_#zDYtH}}eJ z)Nx6>dO%6XM0=0l?$5B({LuD*TjVsz1{7oFp%_`JD!3R6$Wr|jAox|9>9xK94JlIO zfHOvXB{;c@W$bhnf~4+)Q5?R})Y6!&qPCrj2+?&s5or%Za%)B=X{dYalUzTUj-Hs5 z;)}>MrRW@rzUy70o~1`UCPFB7$=qR>2F}@2g*%)}Xb>o=e@wH*^6v#Q?x#t62W+in zuC;@6DKDHyGHaWC4x|vhx4v#L=i<5(MIZP4p2ih2SGu#h3S<>ONK(h|J^apoUY^xG zuZxU_x||m@KYZkG>P&qd{9Pt4*Sm+lOj_d;l_5v0VTrcreJf@T%A}I=Py6@cP5IsqSf$bT6o`C_I*ZecH)VRV zu>j-IbGjKtG(;*d&#U!(|0zZm?x3F;+7V~z2vIzDtt1@B%F}( zK?7SL{yA!jP76H&7W#amF^*aYi~GXL!Da#Ic~+pAc>+xao|D(~hln?Dm!!;RJH_Ee zPyBmL%~_zpV>F&y-%&x0eG1TJi_)w||*)o`@xWBA6MdnOwOW$(uYFbWNJkjqxyMu!^HoW%MnREl0Q~G|BvTEmt$tA1~Hf(N8rN$UAi}UlUI5vYWrl)C0WR z;r>He;cmV|I21lMTaU90!At2IBOY!g{;-%W;kd;gx>YjTh%4r*N8tEMwuP6N`zM<- zNJI5oX-f+8x+A~azs$M(^WoH_bdp~OvbnAg+<5JAm-)sy16*d)6j?X##)TU&5*}5s9MWZtlTLTvlror^2t=7=Ew1gYty>yugbQ zk8JO_N8}l;O4YlZ3jW+ylD~WKEdd!F`D=Bt-N}DfUD7j~QcdIglw|r&&1ni!dNrli zs?f;?^p;DH&nKk+N|aR*Ps}{CBs+|6H$5|dho?vAIoTUPWY;NAa7v_a^C{fzarDt% z)uqKy^TIn+9oZeVx|mKD zRejmPNh7ZASFm|%`$YHmm0u?($c*jUp6DMZ-lsqMPfL(LN=nqDPS&T6KK?pAWT)5( zq1+kI{~QKgIhKvy*XrI4SPN;qQX;WGc1w}w0C#KPaPB!b@0Q%>`dy^CEkC1k2h7eiZ z`;cNS%AbNXGnx=B9pWGQDj}*)siNK=8rTK-kl&BCbKec|&X)2+KVO>q0em5>@a1}H z(f!AIcyLT-Sm}_`P64j{bzuv2sX_Lu6~csF55V)YNT0k!@9Evn+dn|8!yRsC2&7gFTdE zhP@?c*oxP{aHIWQ&^fjm8N&_iOq%(1=^u(ZvJ-a+{foUImUg8?+&)s ztu|qukWWXLbMh znDF>XSYE(NRZ;-~%Riwsys{_itcqQ#$fa*B|@&LAN9zt3b8a-42J$z|naa~|CQAZ2A_ z{;&Z<73BXugFycD4~Bxkf8_>J1pjFVg38Lv{t*KNRrps8$p0u7OswfohhUJ5+@B7? zAUTl29}dAF1=+vt6czpq3BCHunbrp{AV&U za*F>MR{k$)fMpdF(8wH5G++Qt$D1!bfL>>(OA4&hlA1p8X_hoqn$p0|>$4(LY z7yJq`iqJn%P>@yl`wa5efPfXCQ1ibCr})221hMUZIV5uH&(;yC08-4kXro7~Ag@3R K0O*?P(f${{qws|Q delta 9924 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

orQDf8t5YQmmyp0%LZnag}!f-tynUA zD7-27ERI)t#D!xthU`#tgb-~ z=DOn$&sQ<)Z+M_zVK1Ou)nuFMZ6wH019?CblcqPT%7UfFJUpXQZC;y`hZe={(%6sX zX0sN{&5I!7_ohzHp{orw7be0{BD13ed_;TXisO@y_FgJQP(u(!j@Ix!W23bBUStbG z+*!^kN8ou8tj$LHCfOr?1B43LFW7<}Md@%X$e^4o>zUCyLt^m8-=L3i;(T`L27Sl) zeDHq`noYtYw45=WGd*jIZ5Bj1aiFZ zueb8T>$6A)I~)r!DA!@556=L9NAG8x=quRlaS9x2D1m|W zSP2aBMH63%bgms~Toq^g3ZZaC(blms-`{3!`oxb|=q5*+6gO=!a}P=SP)C+NGh?>$ zjJ(EOKE&o|RwDotFn4Jbr(J=qt-$#2+WK0xAys2zfo5# ze-R!D#PHP>Wn|~^pofSd7wO}LhHMb^4Tqcx$5+l)@xQw^*gE|k<8*b%1+7H=5Hl}# z*3C$F5>$xY?Ux~Q#eFf^ruB~aN@}J?X_x_QaTTz|9T9lmSm80^o5oO}Tv?f-e~e=Z z`U&XYFs)Aj$d!XE3yZ5ujs)Xiu|AYl5&5(&r0u?Ew7GHX({O~J1CU5Ei54uLSoyoF zh8@aF;u)$uuw6xs?JB}5!tzsWSIuvs{y%NChn+Z>{(3usyx+QfjMZgR?dx8p(_xMQ zDG}O8<`I&tp;!MI2{Tq2cO?;@v2DeVxq6j8jX+~38l9t~Aj3oXKAN#K108jlMK*L7 zF~~0+U8u|kR9|sO!&1YCveHmExLk_*zB<>;y=;25%;zGUD4{d{>5-*jE_USP{)ylE zd*rw@PNwNOYViA1&^FQ)&A+Qx=@3pw8)UM__KyM`xo%SseG-yaU*p>2p?XF_ErQr_ zVtkLKe(g8)R#@uCU-#wJS=-qfMymOZIQPP0y;2DUuy0cjV)s!L9>r>QKT(@L_tRZW|< znJ*&XPJ>AgSopS*%R&2M`qMNLHWpsHz6_GOWsX)+FHOX~v9h$bPoG+zosQ!6Cw!`mj3WBdtUz zRytdhFG2gh`qNUEe}t(pDY)1eQfOOUsj6nxW-RbuVJ=6s*>sMQYIExAtMK^+gRpbK z>R8Z7DLGF!qZTXUH{Z>uol)!Vccx_&4h36a`3ISIf`A2mjEvnNuWL zQp!a7>E!p2jaF~q33MIxf&%Exr`XSgg0}_6%__G%M*^uBs|~AB<0)3hcs(E)j_D0c zNK!$_4ydkhvwFqfV6fgwpfHH|qp64U`YNpUwY<{l|AfB_#SZ+Mb{nze!qY4fWaDC* zm!l>Bd4(+&Jl!f5eBM976Gyr;gmB+T4Z(ts!h-k4g14-U88L-lQJ{J{w&we>8%jV# z;uiKs)h`@Kx<^YmA)||5p*9q^nC!GB^m=^ghEF1%slxK*@_}UX*L&W(sog*3j4sal zkMhlT;ug#WQ60)ghug==X6gyHky-ZUZse&N@BLp*CZ^ZCeie9BgLM0K`Nd~1?)Ceh z+nb3C@5866QSDE!uEtPZfdLnn^+{jvnB2rw$pw@h&(GAvRr%UCYNLe$aA|*g_k0u2%2@BpT2Z4V%h#F5&DBF zv%=)=ul7^yywiV3X>$QY2fiVEo+}zZd?1^bhyy12sX6mNrhr4GmiG?7Rg6RGdts3R=)=!q~G=1#hd)b!fYa6U~a z7_szofmYv^d_@=mHJu&P`x9XY+2Z$1>(bxg;x0(6fvc-HRqhYoa~o$gN4O|i;)Z(q zk6tCbp7l~TWYU9tF=RjdqbpDLZi*RTm`(SRDJ<}%1o?&71hE@q!AK_mg{voNXg_L7 zd$?3we!$yy_#XND(B4ep3o?6jX#bQp&C<92C9v&CYJ_Os6K;*CrgVjDM;DR1hJDnol5zQfY|bHKpm zrnj4-MYkU6e7A?Iz2Yp`%y*;p4~i<3*GT}pD0=x&03*E{NGeGCde%AJ?Lg)sN$yhC zhNQ+Rd!zEsIWT=&Er?Fk3Wsp8Xj8;i_sMrD0>Ag!i27R5M(GL03z9GWaH!=&BL1>xm)O`zBpi zyW4wisKLZ})5?>sJvW&jsEb2Z)aBl3>0$Nzr_H0b9(`1(pI_5+Wjda+L}h;v&@0&U zh^{^D5*@ubeqQtI{JzZ3>&v;^`$0cEuUDha?_Ynve?RE*>q+Sz>#xH%xgT1`R^815 zjS|vNg1WBn32j{ctY^7l%|AMA;r&L%P}dt;G6e1e_N=Y*-wD%KyU;;N)IU=w2y{FB zSs$VEp7~Dk{Ui!!GloZIqvbgkY0}1z#en`b-Fn~q{DEjrGnSCqnws;_dv7M_xaNIH zXusd))WvV22CQ=>ZrANySSj30^+kc#j}qP1Tz74yElo*OlkmV3Q%>~9jhX|C48w(P z;_xI

HdFN19uLcLYaKv??jOKQX>CSFTN}a@xs>(&(jk{#t>aY2YtRZtSZC{)h0V z7lQqGoQm}Z`U{&c0}cl~rE6nUxtJ0IXt$nCc|@et*l2NuX};0p>`G`XQ2o;E$Y7h! zeWftZ@EBRbF2uF*6HHTg0|Nl`W|?I?lNx)Y7OBh(E;qe9Ps{Cxa=5E&mSr~l4eD~H za(x}RwvlJwdJ{vFyKVmK?6gHHt}F0S>5qZVe2+fW3=9j$(X(XKras9T&#|gbJfYu0 z%QG_3yOGCpuh*=j89gjSf~T}~3CtSe<+PUZpH?9$LT7o+vRB+E7q=rnJ$gYv#T^gX zi0idcd;Jc%9Psm9xcvLa4LPGPy>T)g!wiY7{2f*OE#3T8Vu9Aj-b0ujvXq>9SC!jm zB^|GRsgK``wLdyL9eVid`sk1eX>R_L^_El@cd8*L6TTP}+Tg?K4Y~mVi-5(UqN(3> z)ltHtB5a`FKeoqgB!B)uz+myeZ4ijqUw*=1=-)P|@ZUBV>|Zu)B9T9~)Gj@Ce26GG z^^+bK3IYTD%MXi0{BN7E*uV3F#l(gG!VD4>{R}PT4$bZ%*@(&U~;^NSM z2Lyw_qW=yE20>G2^r=xIB7Z*-1N}P{7z`Hs+a@gjZ)(9{k-wmW!J=aSrWOnViHZMS z4(cD!!4P5aUr8WhqJJF!=?4Xg{goIh4E~F6DC{qdfT5xw$Uk_*<_CemP_e&?02apD z|Ka$@PZ$dQ=ga@GiNOA1P+06gtNSNt802pjU@(;EzZC|Ci9-Kci3mveuf!sdzuy%R z5&PRu4DuKKq7bm?pT(j6w|%f00sY7LPaEv7RsoBOL;g~j*#Dyi#DvBFiYF!l`@c#@ bjO7plhCxMA9~r?(M8shvTwIC|;UxbDuRXzE From 007b575f7d2d3d7a946ed207d794620a3268585c Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 3 Feb 2026 23:43:18 +0100 Subject: [PATCH 180/237] evaluation script improvements --- thesis-evaluation/total_evaluate.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/thesis-evaluation/total_evaluate.py b/thesis-evaluation/total_evaluate.py index 3477f392d6..4b9de3dbcf 100644 --- a/thesis-evaluation/total_evaluate.py +++ b/thesis-evaluation/total_evaluate.py @@ -76,22 +76,26 @@ def define_division_metric( aliases_qiskit = { - "successfulSingleQubitDecompositions": "totalSingleQubitDecompositions", - "successfulTwoQubitDecompositions": "totalTwoQubitDecompositions", + "totalSingleQubitDecompositions": "successfulSingleQubitDecompositions", + "totalTwoQubitDecompositions": "successfulTwoQubitDecompositions", } -for name in sorted(mqt_results.keys() & qiskit_results.keys()): +aliases_mqt = { + "timeInCircuitCollection": "timeInCircuitCollectionStandalone", +} +names = sorted(mqt_results.keys() & qiskit_results.keys()) +for name in names: m = mqt_results[name] q = qiskit_results[name] for metric in m.keys() | q.keys(): if metric in m: y1[metric] = y1.get(metric, []) + [m[metric]] + if metric in aliases_mqt: + y1[aliases_mqt[metric]] = y1.get(aliases_mqt[metric], []) + [m[metric]] if metric in q: y2[metric] = y2.get(metric, []) + [q[metric]] - elif metric in aliases_qiskit: - y2[metric] = y2.get(metric, []) + [q[aliases_qiskit[metric]]] - - x[metric] = x.get(metric, []) + [name] + if metric in aliases_qiskit: + y2[aliases_qiskit[metric]] = y2.get(aliases_qiskit[metric], []) + [q[metric]] define_division_metric( "timePerSingleQubitDecomposition", @@ -106,12 +110,15 @@ def define_division_metric( name, ) -names = [] + for metric in y1.keys() | y2.keys(): + x[metric] = names + titles = { "subCircuitComplexityChange": "Complexity Improvement after Decomposition", "successfulSingleQubitDecompositions": "Number of Successful Single-Qubit Decompositions", "successfulTwoQubitDecompositions": "Number of Successful Two-Qubit Decompositions", "timeInCircuitCollection": "Sub-Circuit Collection Time [µs]", + "timeInCircuitCollectionStandalone": "Sub-Circuit Collection Time [µs]", "timeInSingleQubitDecomposition": "Total Time for Single-Qubit Decompositions [µs]", "timeInTwoQubitDecomposition": "Total Time for Two-Qubit Decompositions [µs]", "totalCircuitCollections": "Number of Sub-Circuit Collections", @@ -125,6 +132,7 @@ def define_division_metric( legend_positions = { "totalTouchedGates": "upper left", "timeInCircuitCollection": "upper right", + "timeInCircuitCollectionStandalone": "upper left", "timePerSingleQubitDecomposition": "upper left", "timePerTwoQubitDecomposition": "lower right", "totalTouchedGates": "upper left", @@ -132,6 +140,7 @@ def define_division_metric( } pruneFunctions = { "timeInCircuitCollection": lambda value: np.isclose(value, 0), + "timeInCircuitCollectionStandalone": lambda value: np.isclose(value, 0), "timeInSingleQubitDecomposition": lambda value: np.isclose(value, 0), "timeInTwoQubitDecomposition": lambda value: np.isclose(value, 0), "timePerTwoQubitDecomposition": lambda value: np.isclose(value, 0), @@ -224,7 +233,6 @@ def define_division_metric( yint.append(int(each)) plt.yticks(yint) - names = x[metric] leg = plt.legend(loc=legend_positions.get(metric, "best")) for handle in leg.legend_handles: handle.set_sizes([DEFAULT_POINT_SIZE]) From c66c4ec4dc7a8d16b981d0f80acc592355745109 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 3 Feb 2026 23:43:45 +0100 Subject: [PATCH 181/237] update evaluation figures --- .../figures/numberOfTwoQubitCreations.pdf | Bin 16543 -> 16543 bytes .../figures/subCircuitComplexityChange.pdf | Bin 21030 -> 21030 bytes .../successfulSingleQubitDecompositions.pdf | Bin 18729 -> 18729 bytes .../successfulTwoQubitDecompositions.pdf | Bin 19324 -> 19324 bytes .../figures/timeInCircuitCollection.pdf | Bin 22681 -> 22681 bytes .../timeInCircuitCollectionStandalone.pdf | Bin 0 -> 18602 bytes .../timeInSingleQubitDecomposition.pdf | Bin 18996 -> 18996 bytes .../figures/timeInTwoQubitDecomposition.pdf | Bin 19323 -> 19323 bytes .../timePerSingleQubitDecomposition.pdf | Bin 17857 -> 17857 bytes .../figures/timePerTwoQubitDecomposition.pdf | Bin 18236 -> 18236 bytes .../figures/totalCircuitCollections.pdf | Bin 17475 -> 17475 bytes .../totalSingleQubitDecompositions.pdf | Bin 19814 -> 19814 bytes .../figures/totalTouchedGates.pdf | Bin 18909 -> 18909 bytes .../figures/totalTwoQubitDecompositions.pdf | Bin 19402 -> 19402 bytes .../figures/twoQubitCreationTime.pdf | Bin 22947 -> 22947 bytes 15 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 thesis-evaluation/figures/timeInCircuitCollectionStandalone.pdf diff --git a/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf b/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf index d7f60e74f45b87e9fc2e2276d1b09987f43273ca..a470700fd83bb9600297142eafed75bee885af0d 100644 GIT binary patch delta 21 dcmbQ=$T+`|al!&-ZO-0RUQd2bcf= diff --git a/thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf b/thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf index f7b4bd2aaf9b8fe6c42f70eb29cbae67ec248e46..ca1dcd1de4fa0332407cd9526368cf3928dac78c 100644 GIT binary patch delta 21 dcmZ2EiE-s5#tj=>*o=%#j7*F+Z*wVT0RUL)2Y3Jg delta 21 dcmZ2EiE-s5#tj=>*bL2$Eey>!Z*wVT0RUMy2Z8_q diff --git a/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf b/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf index 84fbad7fb5254a0f1de115d720795fcd612839c8..fafaf6e95d502d4b1612672a0ad27a2a69772c77 100644 GIT binary patch delta 21 dcmew}jq%Sk#trY?*o=%#j7*F+e|2kT0RU>*2)qCQ delta 21 dcmew}jq%Sk#trY?*bL2$Eey>!e|2kT0RU?z2*v;a diff --git a/thesis-evaluation/figures/timeInCircuitCollection.pdf b/thesis-evaluation/figures/timeInCircuitCollection.pdf index 592f161a0bb501723dc8cf2213b2bd05f882ba93..9ad9b266999fa29d152b6d505432d17b33f6541e 100644 GIT binary patch delta 21 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 BR3rcZ literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf b/thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf index 5ddadebd356d015a63e9e8683c82542db71db0d6..4af6951c81c581568bc033e08aface67b850f5cd 100644 GIT binary patch delta 21 dcmdlog>lOi#toBQ*^G=$j7*F+&vc#50svVz2Z8_q delta 21 dcmdlog>lOi#toBQ*$mB%Eey>!&vc#50svWr2aEs! diff --git a/thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf b/thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf index 64aecaf406c1c06a80f0a7bcd0dc3bf3da862129..0911ef9558fe9b9aba314c8da551535dbd0ec2e4 100644 GIT binary patch delta 21 dcmex8jq&$1#tpaJ*o=%#j7$tSKX9AP0sv`R2%`W1 delta 21 dcmex8jq&$1#tpaJ*bL2$Eey>zKX9AP0sv{J2(17B diff --git a/thesis-evaluation/figures/timePerSingleQubitDecomposition.pdf b/thesis-evaluation/figures/timePerSingleQubitDecomposition.pdf index 983889d49e1f1d48dc014cf1534de1d7a3989da9..a7a9a3d2b124dc9873fddc335babc14ccfdf543a 100644 GIT binary patch delta 21 ccmX@u&3LezaYKwFn~|}Jk%{5vB*%Cb098o_*8l(j delta 21 ccmX@u&3LezaYKwFo1wX}g`xT8B*%Cb09Bj@;s5{u diff --git a/thesis-evaluation/figures/timePerTwoQubitDecomposition.pdf b/thesis-evaluation/figures/timePerTwoQubitDecomposition.pdf index 39e49100c66526ac503009672a6a9d4f45bb07a2..63a2523c780c33b727c4117847759b82c4fb6438 100644 GIT binary patch delta 21 ccmdnf$GE4Dal>mTHX~ybBNM~TADz5e0AA|{-v9sr delta 21 ccmdnf$GE4Dal>mTHbZk`3q$kGADz5e0AD@_>Hq)$ diff --git a/thesis-evaluation/figures/totalCircuitCollections.pdf b/thesis-evaluation/figures/totalCircuitCollections.pdf index fa8cd6dd93271717c3028ae4ca690080f5ac19e5..99d4f23350852482f5dfa2eedf1d3669ecea0018 100644 GIT binary patch delta 21 ccmX@y!FafXaRZwJn~|}Jk%`e}9tU$408YXN00000 delta 21 ccmX@y!FafXaRZwJo1wX}g`xRo9tU$408bAF3IG5A diff --git a/thesis-evaluation/figures/totalSingleQubitDecompositions.pdf b/thesis-evaluation/figures/totalSingleQubitDecompositions.pdf index 6de297ae47c44adefc43506abbb2b9f219e40d53..a7a1a4ea33fe7242b4069b2efc32c74ab2dfc90c 100644 GIT binary patch delta 21 dcmaDhi}Bek#tlb3*o=%#j7*F+pZ1u|0sv)a2wVUF delta 21 dcmaDhi}Bek#tlb3*bL2$Eey>!pZ1u|0sv*S2xb5P diff --git a/thesis-evaluation/figures/totalTouchedGates.pdf b/thesis-evaluation/figures/totalTouchedGates.pdf index c4b11c7c89adc20dfcaba3c7b4fe62b02d27bece..a3077d54667ab418ff29745b0a654c47033ce1ed 100644 GIT binary patch delta 21 ccmcaRnepyq#tkX1Y(~Z=Mka=vvs{Z=09&mGO8@`> delta 21 ccmcaRnepyq#tkX1Y=-8>7KUb#o$=Ik#tpXaY(~Z=MkYp^o!#4609Nb<_y7O^ delta 21 ccmX>#o$=Ik#tpXaY=-8>7KY}Vo!#4609QE&0ssI2 diff --git a/thesis-evaluation/figures/twoQubitCreationTime.pdf b/thesis-evaluation/figures/twoQubitCreationTime.pdf index f90be056e3a69d3234fe9d3eda2336a441db4f59..342a4231413ee5a4ec0b721677dabc624b4947c3 100644 GIT binary patch delta 21 dcmZ3ynQ`%E#trX6*o=%#j7*F+e+gO70svx}2!;Rv delta 21 dcmZ3ynQ`%E#trX6*bL2$Eey>ze+gO70svy*2#){& From 8b7f0bc5640cd93093db534bf608652c02a694c0 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 4 Feb 2026 12:39:46 +0100 Subject: [PATCH 182/237] Revert thesis evaluation changes This reverts the following commits: * c66c4ec4dc7a8d16b981d0f80acc592355745109 * 007b575f7d2d3d7a946ed207d794620a3268585c * 2ddfc6cad2bef4ac19d614fa36bde3efde8d9e66 * 5e676bf30a84e872e0193066e551e67687c9d450 * b4af378f093b69e4091dff808c6eaaccee6a90e4 * 3a8b02a3eca3dc07a00d0dd82f3aaf19ecf7bd1b * 90c46f47be72c56eb26afb22b44d96fa60e0d4d4 * a79324cc049d189cf817a5ccadf723e2c5e72c15 * c46f15970399ac5932089cebe4a1a77d2476ba57 --- mlir/include/mlir/Passes/Passes.h | 15 +- mlir/lib/Compiler/CompilerPipeline.cpp | 88 +++--- mlir/lib/Passes/GateDecompositionPass.cpp | 83 +----- .../Patterns/GateDecompositionPattern.cpp | 123 +------- thesis-evaluation/__init__.py | 0 thesis-evaluation/evaluate.py | 34 --- thesis-evaluation/evaluate_cache.json | 1 - .../figures/numberOfTwoQubitCreations.pdf | Bin 16543 -> 0 bytes .../figures/subCircuitComplexityChange.pdf | Bin 21030 -> 0 bytes .../successfulSingleQubitDecompositions.pdf | Bin 18729 -> 0 bytes .../successfulTwoQubitDecompositions.pdf | Bin 19324 -> 0 bytes .../figures/timeInCircuitCollection.pdf | Bin 22681 -> 0 bytes .../timeInCircuitCollectionStandalone.pdf | Bin 18602 -> 0 bytes .../timeInSingleQubitDecomposition.pdf | Bin 18996 -> 0 bytes .../figures/timeInTwoQubitDecomposition.pdf | Bin 19323 -> 0 bytes .../timePerSingleQubitDecomposition.pdf | Bin 17857 -> 0 bytes .../figures/timePerTwoQubitDecomposition.pdf | Bin 18236 -> 0 bytes .../figures/totalCircuitCollections.pdf | Bin 17475 -> 0 bytes .../totalSingleQubitDecompositions.pdf | Bin 19814 -> 0 bytes .../figures/totalTouchedGates.pdf | Bin 18909 -> 0 bytes .../figures/totalTwoQubitDecompositions.pdf | Bin 19402 -> 0 bytes .../figures/twoQubitCreationTime.pdf | Bin 22947 -> 0 bytes thesis-evaluation/qiskit_run.py | 167 ----------- thesis-evaluation/run.sh | 33 --- thesis-evaluation/total_evaluate.py | 274 ------------------ 25 files changed, 61 insertions(+), 757 deletions(-) delete mode 100644 thesis-evaluation/__init__.py delete mode 100644 thesis-evaluation/evaluate.py delete mode 100644 thesis-evaluation/evaluate_cache.json delete mode 100644 thesis-evaluation/figures/numberOfTwoQubitCreations.pdf delete mode 100644 thesis-evaluation/figures/subCircuitComplexityChange.pdf delete mode 100644 thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf delete mode 100644 thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf delete mode 100644 thesis-evaluation/figures/timeInCircuitCollection.pdf delete mode 100644 thesis-evaluation/figures/timeInCircuitCollectionStandalone.pdf delete mode 100644 thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf delete mode 100644 thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf delete mode 100644 thesis-evaluation/figures/timePerSingleQubitDecomposition.pdf delete mode 100644 thesis-evaluation/figures/timePerTwoQubitDecomposition.pdf delete mode 100644 thesis-evaluation/figures/totalCircuitCollections.pdf delete mode 100644 thesis-evaluation/figures/totalSingleQubitDecompositions.pdf delete mode 100644 thesis-evaluation/figures/totalTouchedGates.pdf delete mode 100644 thesis-evaluation/figures/totalTwoQubitDecompositions.pdf delete mode 100644 thesis-evaluation/figures/twoQubitCreationTime.pdf delete mode 100644 thesis-evaluation/qiskit_run.py delete mode 100755 thesis-evaluation/run.sh delete mode 100644 thesis-evaluation/total_evaluate.py diff --git a/mlir/include/mlir/Passes/Passes.h b/mlir/include/mlir/Passes/Passes.h index ec21ae3523..34e5a770ba 100644 --- a/mlir/include/mlir/Passes/Passes.h +++ b/mlir/include/mlir/Passes/Passes.h @@ -27,20 +27,7 @@ namespace mlir::qco { #define GEN_PASS_DECL #include "mlir/Passes/Passes.h.inc" // IWYU pragma: export -void populateGateDecompositionPatterns( - mlir::RewritePatternSet& patterns, - llvm::Statistic& twoQubitCreationTime, - llvm::Statistic& numberOfTwoQubitCreations, - llvm::Statistic& successfulSingleQubitDecompositions, - llvm::Statistic& totalSingleQubitDecompositions, - llvm::Statistic& successfulTwoQubitDecompositions, - llvm::Statistic& totalTwoQubitDecompositions, - llvm::Statistic& totalCircuitCollections, - llvm::Statistic& totalTouchedGates, - llvm::Statistic& subCircuitComplexityChange, - llvm::Statistic& timeInCircuitCollection, - llvm::Statistic& timeInSingleQubitDecomposition, - llvm::Statistic& timeInTwoQubitDecomposition); +void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns); //===----------------------------------------------------------------------===// // Registration diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index ae08267253..4d0edddac5 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -124,17 +124,17 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, } // Stage 2: QC canonicalization - // addCleanupPasses(pm); - // if (pm.run(module).failed()) { - // return failure(); - // } - // if (record != nullptr && config_.recordIntermediates) { - // record->afterInitialCanon = captureIR(module); - // if (config_.printIRAfterAllStages) { - // prettyPrintStage(module, "Initial QC Canonicalization", ++currentStage, - // totalStages); - // } - // } + addCleanupPasses(pm); + if (pm.run(module).failed()) { + return failure(); + } + if (record != nullptr && config_.recordIntermediates) { + record->afterInitialCanon = captureIR(module); + if (config_.printIRAfterAllStages) { + prettyPrintStage(module, "Initial QC Canonicalization", ++currentStage, + totalStages); + } + } pm.clear(); // Stage 3: QC-to-QCO conversion @@ -152,17 +152,17 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, pm.clear(); // Stage 4: QCO canonicalization - // addCleanupPasses(pm); - // if (failed(pm.run(module))) { - // return failure(); - // } - // if (record != nullptr && config_.recordIntermediates) { - // record->afterQCOCanon = captureIR(module); - // if (config_.printIRAfterAllStages) { - // prettyPrintStage(module, "Initial QCO Canonicalization", ++currentStage, - // totalStages); - // } - // } + addCleanupPasses(pm); + if (failed(pm.run(module))) { + return failure(); + } + if (record != nullptr && config_.recordIntermediates) { + record->afterQCOCanon = captureIR(module); + if (config_.printIRAfterAllStages) { + prettyPrintStage(module, "Initial QCO Canonicalization", ++currentStage, + totalStages); + } + } pm.clear(); // Stage 5: Optimization passes @@ -181,17 +181,17 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, pm.clear(); // Stage 6: QCO canonicalization - // addCleanupPasses(pm); - // if (failed(pm.run(module))) { - // return failure(); - // } - // if (record != nullptr && config_.recordIntermediates) { - // record->afterOptimizationCanon = captureIR(module); - // if (config_.printIRAfterAllStages) { - // prettyPrintStage(module, "Final QCO Canonicalization", ++currentStage, - // totalStages); - // } - // } + addCleanupPasses(pm); + if (failed(pm.run(module))) { + return failure(); + } + if (record != nullptr && config_.recordIntermediates) { + record->afterOptimizationCanon = captureIR(module); + if (config_.printIRAfterAllStages) { + prettyPrintStage(module, "Final QCO Canonicalization", ++currentStage, + totalStages); + } + } pm.clear(); // Stage 7: QCO-to-QC conversion @@ -209,17 +209,17 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, pm.clear(); // Stage 8: QC canonicalization - // addCleanupPasses(pm); - // if (failed(pm.run(module))) { - // return failure(); - // } - // if (record != nullptr && config_.recordIntermediates) { - // record->afterQCCanon = captureIR(module); - // if (config_.printIRAfterAllStages) { - // prettyPrintStage(module, "Final QC Canonicalization", ++currentStage, - // totalStages); - // } - // } + addCleanupPasses(pm); + if (failed(pm.run(module))) { + return failure(); + } + if (record != nullptr && config_.recordIntermediates) { + record->afterQCCanon = captureIR(module); + if (config_.printIRAfterAllStages) { + prettyPrintStage(module, "Final QC Canonicalization", ++currentStage, + totalStages); + } + } pm.clear(); // Stage 9: QC-to-QIR conversion (optional) diff --git a/mlir/lib/Passes/GateDecompositionPass.cpp b/mlir/lib/Passes/GateDecompositionPass.cpp index 5ac5ded2d4..ee90801549 100644 --- a/mlir/lib/Passes/GateDecompositionPass.cpp +++ b/mlir/lib/Passes/GateDecompositionPass.cpp @@ -28,46 +28,6 @@ namespace mlir::qco { struct GateDecompositionPass final : impl::GateDecompositionPassBase { - GateDecompositionPass() = default; - GateDecompositionPass(const GateDecompositionPass& other) - : impl::GateDecompositionPassBase{other}, - twoQubitCreationTime{this, "twoQubitCreationTime", - "Creation time of basis decomposers"}, - numberOfTwoQubitCreations{ - this, "numberOfTwoQubitCreations", - "Number of times basis decomposers are created"}, - successfulSingleQubitDecompositions{ - this, "successfulSingleQubitDecompositions", - "Number of times a single-qubit decomposition was applied"}, - totalSingleQubitDecompositions{this, "totalSingleQubitDecompositions", - "Number of times (only) a single-qubit " - "decomposition was calculated"}, - successfulTwoQubitDecompositions{ - this, "successfulTwoQubitDecompositions", - "Number of times a two-qubit decomposition was applied"}, - totalTwoQubitDecompositions{ - this, "totalTwoQubitDecompositions", - "Number of times a two-qubit decomposition was calculated"}, - totalCircuitCollections{this, "totalCircuitCollections", - "Number of times a sub-circuit was collected"}, - totalTouchedGates{ - this, "totalTouchedGates", - "Number of gates that were looked at (in sub-circuit collection)"}, - subCircuitComplexityChange{ - this, "subCircuitComplexityChange", - "Increase or decrease of complexity in sub-circuit"}, - timeInCircuitCollection{this, "timeInCircuitCollection", - "Time spent in circuit collection (µs)"}, - timeInSingleQubitDecomposition{ - this, "timeInSingleQubitDecomposition", - "Time spent in single-qubit decomposition (µs)"}, - timeInTwoQubitDecomposition{ - this, "timeInTwoQubitDecomposition", - "Time spent in single-qubit decomposition (µs)"} {} - GateDecompositionPass(GateDecompositionPass&& other) = delete; - GateDecompositionPass& operator=(const GateDecompositionPass& other) = delete; - GateDecompositionPass& operator=(GateDecompositionPass&& other) = delete; - void runOnOperation() override { // Get the current operation being operated on. auto op = getOperation(); @@ -75,13 +35,7 @@ struct GateDecompositionPass final // Define the set of patterns to use. mlir::RewritePatternSet patterns(ctx); - populateGateDecompositionPatterns( - patterns, twoQubitCreationTime, numberOfTwoQubitCreations, - successfulSingleQubitDecompositions, totalSingleQubitDecompositions, - successfulTwoQubitDecompositions, totalTwoQubitDecompositions, - totalCircuitCollections, totalTouchedGates, subCircuitComplexityChange, - timeInCircuitCollection, timeInSingleQubitDecomposition, - timeInTwoQubitDecomposition); + populateGateDecompositionPatterns(patterns); // Configure greedy driver mlir::GreedyRewriteConfig config; @@ -97,41 +51,6 @@ struct GateDecompositionPass final signalPassFailure(); } } - - Statistic twoQubitCreationTime{this, "twoQubitCreationTime", - "Creation time of basis decomposers"}; - Statistic numberOfTwoQubitCreations{ - this, "numberOfTwoQubitCreations", - "Number of times basis decomposers are created"}; - Statistic successfulSingleQubitDecompositions{ - this, "successfulSingleQubitDecompositions", - "Number of times a single-qubit decomposition was applied"}; - Statistic totalSingleQubitDecompositions{ - this, "totalSingleQubitDecompositions", - "Number of times (only) a single-qubit decomposition was calculated"}; - Statistic successfulTwoQubitDecompositions{ - this, "successfulTwoQubitDecompositions", - "Number of times a two-qubit decomposition was applied"}; - Statistic totalTwoQubitDecompositions{ - this, "totalTwoQubitDecompositions", - "Number of times a two-qubit decomposition was calculated"}; - Statistic totalCircuitCollections{ - this, "totalCircuitCollections", - "Number of times a sub-circuit was collected"}; - Statistic totalTouchedGates{ - this, "totalTouchedGates", - "Number of gates that were looked at (in sub-circuit collection)"}; - Statistic subCircuitComplexityChange{ - this, "subCircuitComplexityChange", - "Increase or decrease of complexity in sub-circuit"}; - Statistic timeInCircuitCollection{this, "timeInCircuitCollection", - "Time spent in circuit collection (µs)"}; - Statistic timeInSingleQubitDecomposition{ - this, "timeInSingleQubitDecomposition", - "Time spent in single-qubit decomposition (µs)"}; - Statistic timeInTwoQubitDecomposition{ - this, "timeInTwoQubitDecomposition", - "Time spent in single-qubit decomposition (µs)"}; }; } // namespace mlir::qco diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 35da3c26b1..26dd3ebe4b 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -73,49 +73,18 @@ struct GateDecompositionPattern final * a decomposition if the (sub)circuit only contains * gates available as basis gates or euler bases */ - explicit GateDecompositionPattern( - mlir::MLIRContext* context, llvm::SmallVector basisGate, - llvm::SmallVector eulerBasis, bool singleQubitOnly, - bool forceApplication, llvm::Statistic& twoQubitCreationTime, - llvm::Statistic& numberOfTwoQubitCreations, - llvm::Statistic& successfulSingleQubitDecompositions, - llvm::Statistic& totalSingleQubitDecompositions, - llvm::Statistic& successfulTwoQubitDecompositions, - llvm::Statistic& totalTwoQubitDecompositions, - llvm::Statistic& totalCircuitCollections, - llvm::Statistic& totalTouchedGates, - llvm::Statistic& subCircuitComplexityChange, - llvm::Statistic& timeInCircuitCollection, - llvm::Statistic& timeInSingleQubitDecomposition, - llvm::Statistic& timeInTwoQubitDecomposition) + explicit GateDecompositionPattern(mlir::MLIRContext* context, + llvm::SmallVector basisGate, + llvm::SmallVector eulerBasis, + bool singleQubitOnly, bool forceApplication) : OpInterfaceRewritePattern(context), decomposerBasisGates{std::move(basisGate)}, decomposerEulerBases{std::move(eulerBasis)}, - singleQubitOnly{singleQubitOnly}, forceApplication{forceApplication}, - twoQubitCreationTime{twoQubitCreationTime}, - numberOfTwoQubitCreations{numberOfTwoQubitCreations}, - successfulSingleQubitDecompositions{ - successfulSingleQubitDecompositions}, - totalSingleQubitDecompositions{totalSingleQubitDecompositions}, - successfulTwoQubitDecompositions{successfulTwoQubitDecompositions}, - totalTwoQubitDecompositions{totalTwoQubitDecompositions}, - totalCircuitCollections{totalCircuitCollections}, - totalTouchedGates{totalTouchedGates}, - subCircuitComplexityChange{subCircuitComplexityChange}, - timeInCircuitCollection{timeInCircuitCollection}, - timeInSingleQubitDecomposition{timeInSingleQubitDecomposition}, - timeInTwoQubitDecomposition{timeInTwoQubitDecomposition} { - ++numberOfTwoQubitCreations; - auto startTime = std::chrono::steady_clock::now(); + singleQubitOnly{singleQubitOnly}, forceApplication{forceApplication} { for (auto&& basisGate : decomposerBasisGates) { basisDecomposers.push_back(decomposition::TwoQubitBasisDecomposer::create( basisGate, DEFAULT_FIDELITY)); } - auto endTime = std::chrono::steady_clock::now(); - twoQubitCreationTime += - std::chrono::duration_cast(endTime - - startTime) - .count(); } mlir::LogicalResult @@ -131,23 +100,13 @@ struct GateDecompositionPattern final return mlir::failure(); } - auto collectSeries = [this](UnitaryOpInterface op, bool singleQubitOnly) { - ++totalCircuitCollections; + auto collectSeries = [](UnitaryOpInterface op, bool singleQubitOnly) { if (singleQubitOnly) { return TwoQubitSeries::getSingleQubitSeries(op); } return TwoQubitSeries::getTwoQubitSeries(op); }; - auto startTime = std::chrono::steady_clock::now(); auto series = collectSeries(op, singleQubitOnly); - auto endTime = std::chrono::steady_clock::now(); - timeInCircuitCollection += - std::chrono::duration_cast(endTime - - startTime) - .count(); - // not really accurate since it neglects the "past the series" gates that - // terminated the series - totalTouchedGates += series.gates.size(); auto&& [singleQubitGates, twoQubitGates] = getDecompositionGates(); auto containsForeignGates = @@ -169,10 +128,6 @@ struct GateDecompositionPattern final // cannot process decomposition without the matrix of the series return mlir::failure(); } - // only count the multiple decompositions as "one" since the number of - // euler bases is constant - ++totalSingleQubitDecompositions; - startTime = std::chrono::steady_clock::now(); for (auto&& eulerBasis : decomposerEulerBases) { auto sequence = decomposition::EulerDecomposition::generateCircuit( eulerBasis, *unitaryMatrix, true, std::nullopt); @@ -181,11 +136,6 @@ struct GateDecompositionPattern final bestSequence = sequence; } } - endTime = std::chrono::steady_clock::now(); - timeInSingleQubitDecomposition += - std::chrono::duration_cast(endTime - - startTime) - .count(); } else { // two-qubit series; perform two-qubit basis decomposition const auto unitaryMatrix = series.getUnitaryMatrix(); @@ -197,13 +147,9 @@ struct GateDecompositionPattern final decomposition::TwoQubitWeylDecomposition::create(*unitaryMatrix, DEFAULT_FIDELITY); - // only count the multiple decompositions as "one" since the number of - // euler bases is constant - ++totalTwoQubitDecompositions; - startTime = std::chrono::steady_clock::now(); for (const auto& decomposer : basisDecomposers) { auto sequence = decomposer.twoQubitDecompose( - targetDecomposition, decomposerEulerBases, DEFAULT_FIDELITY, true, + targetDecomposition, decomposerEulerBases, DEFAULT_FIDELITY, false, std::nullopt); if (sequence) { // decomposition successful @@ -215,11 +161,11 @@ struct GateDecompositionPattern final } } } - endTime = std::chrono::steady_clock::now(); - timeInTwoQubitDecomposition += - std::chrono::duration_cast(endTime - - startTime) - .count(); + } + + llvm::errs() << "Found series (" << series.complexity << "): "; + for (auto&& gate : series.gates) { + llvm::errs() << gate.op->getName().stripDialect().str() << ", "; } if (!bestSequence) { @@ -237,14 +183,6 @@ struct GateDecompositionPattern final return mlir::failure(); } - if (series.isSingleQubitSeries()) { - ++successfulSingleQubitDecompositions; - } else { - ++successfulTwoQubitDecompositions; - } - subCircuitComplexityChange += - series.complexity - bestSequence->complexity(); - applySeries(rewriter, series, *bestSequence); return mlir::success(); @@ -696,38 +634,13 @@ struct GateDecompositionPattern final llvm::SmallVector decomposerEulerBases; bool singleQubitOnly; bool forceApplication; - - llvm::Statistic& twoQubitCreationTime; - llvm::Statistic& numberOfTwoQubitCreations; - llvm::Statistic& successfulSingleQubitDecompositions; - llvm::Statistic& totalSingleQubitDecompositions; - llvm::Statistic& successfulTwoQubitDecompositions; - llvm::Statistic& totalTwoQubitDecompositions; - llvm::Statistic& totalCircuitCollections; - llvm::Statistic& totalTouchedGates; - llvm::Statistic& subCircuitComplexityChange; - llvm::Statistic& timeInCircuitCollection; - llvm::Statistic& timeInSingleQubitDecomposition; - llvm::Statistic& timeInTwoQubitDecomposition; }; /** * @brief Populates the given pattern set with patterns for gate * decomposition. */ -void populateGateDecompositionPatterns( - mlir::RewritePatternSet& patterns, llvm::Statistic& twoQubitCreationTime, - llvm::Statistic& numberOfTwoQubitCreations, - llvm::Statistic& successfulSingleQubitDecompositions, - llvm::Statistic& totalSingleQubitDecompositions, - llvm::Statistic& successfulTwoQubitDecompositions, - llvm::Statistic& totalTwoQubitDecompositions, - llvm::Statistic& totalCircuitCollections, - llvm::Statistic& totalTouchedGates, - llvm::Statistic& subCircuitComplexityChange, - llvm::Statistic& timeInCircuitCollection, - llvm::Statistic& timeInSingleQubitDecomposition, - llvm::Statistic& timeInTwoQubitDecomposition) { +void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns) { llvm::SmallVector basisGates; llvm::SmallVector eulerBases; basisGates.push_back({.type = qc::X, .parameter = {}, .qubitId = {0, 1}}); @@ -735,14 +648,8 @@ void populateGateDecompositionPatterns( eulerBases.push_back(GateDecompositionPattern::EulerBasis::ZYZ); eulerBases.push_back(GateDecompositionPattern::EulerBasis::XYX); eulerBases.push_back(GateDecompositionPattern::EulerBasis::ZXZ); - patterns.add( - patterns.getContext(), basisGates, eulerBases, false, true, - twoQubitCreationTime, numberOfTwoQubitCreations, - successfulSingleQubitDecompositions, totalSingleQubitDecompositions, - successfulTwoQubitDecompositions, totalTwoQubitDecompositions, - totalCircuitCollections, totalTouchedGates, subCircuitComplexityChange, - timeInCircuitCollection, timeInSingleQubitDecomposition, - timeInTwoQubitDecomposition); + patterns.add(patterns.getContext(), basisGates, + eulerBases, false, false); } } // namespace mlir::qco diff --git a/thesis-evaluation/__init__.py b/thesis-evaluation/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/thesis-evaluation/evaluate.py b/thesis-evaluation/evaluate.py deleted file mode 100644 index dda4b4fc5b..0000000000 --- a/thesis-evaluation/evaluate.py +++ /dev/null @@ -1,34 +0,0 @@ -import matplotlib.pyplot as plt -import glob -import os - -INPUT_DIR = "./tmp-output" - -def read_stats(file_name) -> dict[str, int]: - result = {} - - with open(file_name, "r") as f: - for line in f.readlines(): - line = line.lstrip() - line.removeprefix("(S)") - line = line.lstrip() - elements = line.split(" ") - elements = list(filter(lambda x: x != " " and len(x) > 0, elements)) - - metric = elements[2] - value = int(elements[1]) - result[metric] = value if value < 2**31 else value - 2**32 - - return result - -def evaluate(): - all_stats = {} - for file in glob.glob(f"{INPUT_DIR}/*.statistic"): - print(f"Processing {file}...") - name = os.path.basename(file).removesuffix(".statistic") - name = name.removesuffix(".qasm") - name = name.removesuffix(".mlir") - all_stats[name] = read_stats(file) - - print(all_stats) - return all_stats diff --git a/thesis-evaluation/evaluate_cache.json b/thesis-evaluation/evaluate_cache.json deleted file mode 100644 index 92f5381da9..0000000000 --- a/thesis-evaluation/evaluate_cache.json +++ /dev/null @@ -1 +0,0 @@ -{"mqt": {"modular_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 14.95, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 815.4999999999998}, "bmw_quark_cardinality_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 39.49999999999999, "timeInSingleQubitDecomposition": 8.250000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 13.999999999999995, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 11.999999999999996, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 839.1}, "draper_qft_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 22.699999999999992, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 755.4999999999999, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 822.0499999999998}, "vqe_two_local_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 291.70000000000005, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12806.850000000002, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 803.05}, "wstate_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -8.000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 29.499999999999996, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1395.1999999999998, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 782.05}, "bmw_quark_copula_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 149.25, "timeInSingleQubitDecomposition": 48.80000000000001, "timeInTwoQubitDecomposition": 829.85, "totalCircuitCollections": 32.999999999999986, "totalSingleQubitDecompositions": 8.000000000000002, "totalTouchedGates": 50.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 815.4499999999999}, "qaoa_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 11.600000000000001, "timeInSingleQubitDecomposition": 7.299999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 807.8500000000001}, "qftentangled_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -13.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 33.99999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1859.0000000000007, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 815.0}, "wstate_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.750000000000001, "timeInSingleQubitDecomposition": 6.049999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 795.2000000000002}, "ghz_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 22.699999999999992, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 831.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 800.55}, "qpeinexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -16.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 23.249999999999993, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1359.8000000000002, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 815.7}, "vqe_real_amp_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 14.799999999999999, "timeInSingleQubitDecomposition": 6.1999999999999975, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 807.55}, "vqe_su2_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 5.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 21.3, "timeInSingleQubitDecomposition": 7.95, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 823.3499999999999}, "qft_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 9.399999999999999, "timeInSingleQubitDecomposition": 6.749999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 811.0999999999999}, "bmw_quark_cardinality_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -55.99999999999998, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 2.999999999999999, "timeInCircuitCollection": 153.75, "timeInSingleQubitDecomposition": 27.250000000000007, "timeInTwoQubitDecomposition": 4109.2, "totalCircuitCollections": 47.999999999999986, "totalSingleQubitDecompositions": 4.000000000000001, "totalTouchedGates": 50.999999999999986, "totalTwoQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 796.4499999999999}, "bv_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 5.749999999999999, "timeInSingleQubitDecomposition": 6.049999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 806.4499999999999}, "ghz_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.5, "timeInSingleQubitDecomposition": 6.399999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 806.6500000000002}, "vqe_su2_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 626.4999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 22723.0, "totalCircuitCollections": 20.0, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 198.00000000000006, "totalTwoQubitDecompositions": 15.0, "twoQubitCreationTime": 825.6999999999999}, "qnn_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 39.550000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1033.7, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 13.000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 802.3499999999999}, "qpeexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 23.449999999999992, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 809.9, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 808.5000000000002}, "qnn_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 17.249999999999996, "timeInSingleQubitDecomposition": 7.800000000000001, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 786.6}, "vqe_real_amp_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 285.54999999999995, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12790.199999999999, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 806.9499999999999}, "qftentangled_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 10.099999999999998, "timeInSingleQubitDecomposition": 3.099999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 812.8000000000002}, "bv_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 2.0000000000000004, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 12.65, "timeInSingleQubitDecomposition": 8.500000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 2.0000000000000004, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 793.25}, "randomcircuit_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 10.4, "timeInSingleQubitDecomposition": 7.6499999999999995, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 794.25}, "qft_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -23.999999999999993, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 24.449999999999996, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1888.9499999999998, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 793.1999999999999}, "qaoa_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -6.999999999999997, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 27.500000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1481.3, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 801.7500000000001}}, "qiskit": {"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 220.62315, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 530.834}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1449.0686999999998, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 664.06875}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1400.6623, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 753.1374999999999}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1403.9076999999997, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 302.8408}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 7828.1339, "subCircuitComplexityChange": -75.0, "totalTwoQubitDecompositions": 5.999999999999998, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 2283.7117499999995}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 3831.9234500000002, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1648.71505}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1465.3955999999998, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1092.5579}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 187.1023, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 393.38485}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1550.9868500000002, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1698.9834500000002}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1430.4808500000001, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 552.24995}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1362.3799499999998, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 570.4323499999999}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1306.5448999999999, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 385.82995000000005}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1374.2908000000002, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 516.0969000000001}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1427.7414, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1202.9632000000001}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 180.8468, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 312.29975}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1457.9090999999999, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1094.43475}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 180.33175, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 282.276}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 169.9849, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 817.0377500000002}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1306.34635, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 336.2191000000001}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1333.5626999999997, "subCircuitComplexityChange": 15.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 712.1571000000001}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1426.60265, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 601.4511999999999}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1457.3285500000004, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 622.4298}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 175.84595, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 653.8033499999999}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 504.0352500000001, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1364.66555}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 165.24959999999996, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 163.43635000000003}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 165.68475, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 284.16045}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 154.48034999999996, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 365.45809999999994}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1490.2478999999998, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 576.6129999999999}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1428.98125, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 606.5438499999999}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 173.56869999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 284.78909999999996}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 169.89490000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 372.2458499999999}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1383.5119000000002, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 552.30705}}} \ No newline at end of file diff --git a/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf b/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf deleted file mode 100644 index a470700fd83bb9600297142eafed75bee885af0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16543 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 B|0Dna diff --git a/thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf b/thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf deleted file mode 100644 index ca1dcd1de4fa0332407cd9526368cf3928dac78c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18729 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 diff --git a/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf b/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf deleted file mode 100644 index fafaf6e95d502d4b1612672a0ad27a2a69772c77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19324 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 VjXE(g5FmO4KDNcBq@zsq{{UwFIxYYJ diff --git a/thesis-evaluation/figures/timeInCircuitCollection.pdf b/thesis-evaluation/figures/timeInCircuitCollection.pdf deleted file mode 100644 index 9ad9b266999fa29d152b6d505432d17b33f6541e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22681 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 BR3rcZ diff --git a/thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf b/thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf deleted file mode 100644 index 4af6951c81c581568bc033e08aface67b850f5cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18996 zcmb`v2RxPU`v9Dg9kOK|E8AJl;e@iY_m=J0D?2N@Wbcv6j*LjMv$BaIdlf=SA|&E{ zj_UjESN`v}&-;0g&+WdS>$%6Z?)$p#^>C`o$O%9Ng@`yyr$Ht4L{Ja}*ivqh4ug$0nN(D z0jQvTJV0OtCjbShAF=e0SOH}G3p&t03Bcfu!QKrGVD}w;uqN8w#na6apa+9L(7p=V z%FbNM#Rq5+0{p>IP(g$+9125#U?^ch6huTs7y&c{mIg=!>Tw65z9TB>?Cb(OK{17X zWeeu@Pd+N6oozg9LC_y<%h@>sl!L%>jsQDk(3UP%Xbb`F9&TuJCnBG$4_8!@m^$2M z=1gBG=@;qDu_!^1XTfd6!EGjuKEz@)k1E1Qu($dSj{F~e^;+twL!`BeHV565%+vNB z?a?yOG4QP4U($0q+@EQ`?eg@{ZU2S~7`*L5c26*HAM28faLu=%dvnO0tXRaX+A2Bo0eAued;awyFr&v%`6*R zQ|7h)TiXX|eVh$VkEh+n-NudNpAtJCtT(z8Bp|xPAL}pm)^@EK7a6Jg5nOx%3v?)> zY8JoZHXE=m_~lGp!^Cbie6 zB?l-^drY|#SIfJQcRyX8PmH>ar-ewTe=_OnJ!u(@6`-QmIYZ5II@XtjF0iuDacW0& zbHESp8iSHCo-ys`qUIhpgt9-S%!dqmMFU)8?iF^jMw}432Mo~gaiu7E+>Zyd?jUKaYr=QgfeT*A1ISZbv3eLddae1TER0>0egm)+EW-jwkIVj44)E zh6f^U;bh#nss|mj0A1iP6EdkJ51yK3X5E0@-%cc%)X_E{kkGG`HA&HvHS?8t;pQ?> zyLl^AnehrGM_<;k`}&*0{lHR?Nx28TdBQ|qfGIS9NnSG{qZg<6MjA6||EKg3v0OKl zcCqkgtaHMiTC|NNdhn&u4Bh*Qk=OG=yW*hr#ssZZ5>s^I`BN{QP)x(9;0|0yzxa%M zZM;;Y`4Ta%LDeFQ=~{~pmC2TOz+Kh%_2`Th1+Ah^6$E*6dg0xs(I`M**~yN6Rw%XK zD#WMa-A=_z(ZjW3o6iKLe}SdWr7p1~Jn2?cAn#A)Hp$wu>a;o^uIj+XY|cQU1OpL>gq|S05%q*l^(v;97=m#O?O|jm+kB~_ERR-q9f6GYrLiP zh1umRY!CHxtFK8nOD&aOu%bArg6Chg7r`<0E^wLO+@l75{*k--(5nXL{LjcP_{$ck)p?F^=3YkO+SF5?Utp{ ztvY-IE}oEA^{4YK+1y*|W2K6i$41}UOVj4zxt={eJl<}RoevtmonBe#?re1-BBc-A zJ}JV`N1@oI+|%icJdJPV3_IG+D;u`EY~$B@`P2}#nRsRb$~SnP6K4K~@K z^zDY{jZmg75scqx$yX+53NQ1|hQvniIVv(1(GLsK$o8kKQB!&(^aNpj77sovyrNUF z^7v+Y_$<9nPSJTR`)A)2?B{M>`eMlcj_$@M?we}&6?o^Mp$w-Wq6b`-o)#)4*)KDL z9hX9)p_yE{$0hlL1?CG^>xeY z_Q^{x$vS7_%h|~t8@8bS>h#&;b_-d=vaFik(x1ep+EQ8C$<`Jl}6hKPtJE_~;12qRtWfO?l3kF5Q( zTgH_MQIY*;9U7#Ixj7R{H*?sXI&Ky!F*nemoal5Y3LBePUE-=V`^~WQ-cD#woaIZz z^!fPda%=U-Yg9{&d$_BxfZUC|or2I@aGpFz>58(RZ_6`f4eDF=uxz=FsO@tFq23Ao zZrp;aE27sr%H1y`vf@H~*a*1UA+NWU%{sg&X&Cr$^oI&m)pUzJ-`+ZTw+1)x3y~bO!>u)!>f$Tvc8xSyG&{y}9h@2jDki^64{7)D*pWKA%a6aQbz| z#|dqVM~QtY&>a)HsU4c6Y2*5Xh4VrEPM0G`Ia<3N=Fp>^2MPpM@QEwu{KTY0RjC-> zuBfyFLzI<7j9YWOvBvCehsw-_ovrlEciOj1QHDOar!1L<>q&I5kJ+mK0vJO9wu+kP zE|K2;&eqyOf>9=Z?WWk((_86T?5wgCuIB#PJDl#xzO`nf=j5fH8QW^w+pM+DRCY&W z@owZl$J3K0-RPbVlehX9=tNv)tz=>if{j*j)|n1yM$nkHEP3$C21(mcIkPlHR5v_+ zA$dzDHIycg7Rz2vhxrXgHUclSA#hWdA&!U<@UC!*OgCK13 zF%e%Gk33)f2;bQf0%aC%!*}2qzK0^+&sq_(71*-cl5XUeZHl{JHc_nwvSnzHH}>SXBPKA}Bt zxL!%GXL8lm{q9%${Y&ys1LYey_S#&pP6?Y1UN`R%<`{Wd^eF(E3C_?_qqZb2JFgoc zO~l3}ugUgA|?32WUF6Vg*w`H0}}O1w*pjE>#n`Imkp?#x}B^9 z8#eR%mPbyP6=S*Rbw`&cnGYNHk7j0s4Iy0ZFAw||+V7DbUSymmXxER=&+3dA*L4YaXG>aQ7xY37}cD!q`WB{*WQZQ;I6WfHSo{PJQOl+!rWm)_JTB&K39 zR*Z5mto<`#&RgaYf^UgrYW~x_onlR;{e!|64_4f4L4GbG)4Hl8qF zs}iWQq#F@R+f-_>q>mR@GEPgEGwwH{#-sP5vxhft#ddI|%aVN8Q1$`mprmSzUyoc1 z^TO%pwj!;BBF}uA~|Z zQs7JNp@cI(nlxOP$kIqHV6ks~>Sft1s|tS^R0y(~D6YM4;55?4{}01Mg|T!?$Y<%e1nL8Kh%x*VnLNRLi8t+a(MgWnMh>Es<2sW14&5ks~#S zU5#Q@rb1)N68TzGp@QN={^E;X3729<`RU(35DUKiwbII8+0E9xY}^ip7rGj@stU_!_{cL>db*08wcb`lupI5i% zsW=Uf&ihtHueyi)s}&p8^gYdprDZMg?&y9h;OdrFdN*GAQW>IrpWODH6w#2ts;f*_ z42J-gd1>K7&S@Q&9#yG_@k<;9>?LiM0V!eSa;eN)=4%F2UavB{YH@V1@01-nKIa~W z6Q%H?O|{QUaFm1pi)YRI!>Gq^vxRh>+Ps2;kY zG@9qz6mef$IWM8~m9iA1j`P)cka~atRuZ{NWo61u$lIQCbvhyRINtc|qt6%S4{i}W zl$o|2yC_HXocnF)*@r#KzL45Zeghd0`zJbff<8y7C97?R%3ZbwqR|HdrGD`@qHk3{ z0VH8=8GuJ=pe?(TCFZ4|gDc;e0J+f@n4?cU#f7?kb2B}xGmZNqr*xTI7gA5nF8!M- z{odJ|#VCgE_ZxRX?EJ;-gEH{f6?by8>xdC8wfXE6Ou~sz@^z>>#EevKuW?Yt`BN*5 z7WJ|>XqYn9A8BW!xwG5k+hs@bmR`G)&z4o+8=yz~k;n_BC`>T#>yn4_gi3ZpXqTQE zIQc0PafgQU5`opf_lW_$5{v%3Iwns|RRwMf6gE`Cep(5*EU4yV*mQUQVOu26v+81@5FOIdJ2} zEc>sC#w-eK<4s%CS7z2Yrj`sEGfQR@;K&yF1DZJd_FXXt!=RQ|V3?)-2;TOEHm)y4 zN;~6KFclHUW^Vg+*_a{l-o~@8jir^p z9&~9o4y?aPaVRx=UDXW|+pns5)7fo&Mh<=WdUNT0U*Gk_#Or?FzR7xQjU5Hs2UayKKA=eyRhjiP&l9m>qcKw|YY4r>)O$f27Y_$cm60J$Aj2q_0W zZpOz@3Ayg6)}~bMIK9J*H{FOs2A>e%-Uh&mAlAybl$y4?IJsbnp{s!_2!r`_g~8I9MqUux~D%QI=@Hb*nHw|C`Y8m1nJ z)ddZ#sep_3R-fFkWw-h0jo0_Bb7>0;I#<{&G-ubyfnsQ=DVlQ;6B()BJAbL?z+<8H zjLN53i$l>EE{zK3#F&2Xb82o9KH#y*$fitV<2Yj! zrvjB%BkL@_P2~2~y#L4&;Ip}Z*{zFZFE+7mHu19uvGcPo7FplEQtE5Fpg113$6m7v zYo{&LR{Xx9AU)UawW}H(PF>vp(&znAcw9C~IshC;l@qqNHgiLGYM15u?9+hVg{fT^ z6aT<#XNiYQUD^X427asYZgw*kArzPo)5ahC!o55HczcNlS4m-TIHGs;73 zb>GWt_>*kORgZ9<`EDg1foWE$j@0w?_Seb$II%)ZH1h z@2W4j_sGI8rRH%+l5y=5?l~n?xwXH20OV8Aw{Ie2ZG~E9m9!d++wZDmpIGmddBrR@ zRXcnhVS`pL?$>)82ZX-=618_o^R8|+?^)BGHuLE5tOyQRYNn-()DDaa++V1neV>SL zXLf`A9J)vTS&3ZuIjM+V_2FA$m&K^cxVaK-LJ1Dko1?4me265g*5iun>Z#*Gl?STb z!0Uoo(Yo_`kr+O;bzkwLGA;O$Oj3O->Tw^fZ)1~)h5KrRVpm|hiMof!5nXsA_MI?r z=rw8+|B$F!D_|(F6Eym$NBt_N;s>IF@C z6E4zjkz$21_2bkj^cRZY~CGudXuO#FMq%(O`2^^#I7phzOr(jf2T4YmHn!!p5de`=9T94166O;JIho zb(fw7v;O;I$@0fa@bUU4X3Y};6A}5jJbb(`iUAsdjQ@dQ)`Wjw97PHNE296gGAbV5 zdRCg1K<1-vC1+5Ogm{mY+n44e2Z^LBR!nBQ#iGIS8aLqWHg9@Dhbx6#U8tiS2p+P3 zCQ!jnYI)Y0=|e({b>BR}QgBWuRp)+6d1lPru_jB&S9WQLGftDin`LpA+7PpO8|T)T zrSL1cJ)F^o#2NZx4q2l8dHu_Qs#D(LbY+DjrNV9#W8xS3M$}CV_rspsKK{lv25x|w zu!K{g2u|S2uM6ZqR)SHGzo0d??@SAXK>DL{E5U0m?0sz=vYx}S2!G#JGNbNVLK+g5 zyvgts9G8$vE>AOK6C7$O8hWe6DN))eMQfVo%=5*RFFCHx%H)WuDBdA^JUlfYzKpZI z%CvR@h9|65!w`u7MiHT*8GuF`MIg!6YT zCic#RJWdj8wJybzclLYk81mR&sFrqYDu*{r%p@=`fz!;6)h%yaBCG6J<0x#CCB z8`~0H<4P&rjR_8Ocvro`Mpk2JteRS6edT;aC%exxw%58$;P0Iw(`bBz3O^J*wQ(WD ze&oaR4zJj;usQeZp~Q5RU#;?r-jVLiWn@+vd zkDhsT3!ik}^5vg=gkkmoep73>5cDtjs44pLwj{y(W~~+My9Tp;*Rp!@zqlVEu|=u* zE2OYrV&iA{M6y26DF4E#1~K8<@NN=|x!2VYZ@8hI&r*wYTKJ}4b)G0yjz4^&>nRK8 zqL2Ln1GYG=U0bxW94QCR)#lOVLEj{v8*@QIPO2=N3S_yb8;sgx!Nf3mU(NQ!I|miz zr*&y_2;D7JIN0y&7QWpZhhOQWwB#ttJk7C6xmz4^)~?fXxs;O(#;CUarsHAFC&RbU z?MDwJI+3l6j$H89RI<2lXp=dFuhOb8b-d`;cW)R{*Iazsa3d+nWH&9TsD+O;tHZDj zDy?Sl*6O|_p>|o;DR}AFQ;6!Y9HF;;eG}i#_EtYf^c16)u3}*;igX>FgbJ~GD0<$p zZGn7iRy-sjUAQ?)AcQXsHD>j-l0}wbHT2EbtP-9oYR&`c6tT)T=Z@~p?eA?J9O6fF z2eO|aonJSGe)K~m^1p=xhYM*#)Q1q*6xI>viQdYk67fV_k+^s>rSCLlA68Yra(@uN zrIr>)Dpouah6o)dr_aW6#gn;?r+4m@rUO;W37BB^fqvH$i12@-BA%$^i~}W+-n|$u zNZ#^w;_g;HcYXTo^(ijS)mw}cG_v-o^~wfX%<)1=Vb@nTTr2NviaHKjThANjrbR9# zFAq^$tmpbq^vf9=aCN*3v%XNXqJg5tN2)xySHfQ~Bj`gqcK)u>&e=^&?!LnziqKgkPNgT$IPU;=PBapfl|aR_5-_qK&1zaR;fvFBN>8pXJ9R;BH*& zww-TBy5Gbf&c@&>#g*H#Zp}Y*9u+ie)Vd&RVWsL;Y8R!Wx~on#T-V-2LcZ-NE6wAc z@ammu`*UK4R7V;g#jmHC^JdN~k?KxN6{&tK6Pp;?WicG{U^#*JnBB?Wcn=f$3)3T; zpyGC#l^|^QZZ3G^!J?k{V7{h-@_O@`fkqBK!|=B_!`PIUHz=gnB2y??VE*70n12mS zbmPKAhO1hw^ObgVJKWr8UX-^_)I{3*iRPs{IS)RbL(K|mB0SJm_k43QiTmSkmY+57 z;7jwA7*1Z-e0|Q*D1>TTv6Smhw$RGi-VN_$2Y*jWwTG39+5@bH17(!wZdEw?NRISy zoUJ|+#g*#Lskw*A=1p`->I+RjWRX4zOh zq4Ww-1;j?s?wco1cac})wrIbEd(R#5kp_%mU%Y-w>ChPO1mZyep`qaX3_{YdPkqpG zYIMDkt0Viy&e51=FBO3V=aX7a3rluvF!T%R=Jc0DMBe#88#lcJezow+ZDsQcRKaPl zO}s+-7&_6HNZ~AvX}ZtzJCjJdWs%}yXJO-<X1zxnJSHY4HGl> z1UYKp1#{l;xxw)I#T40`ERx!0Agv0l<}6fWVz#H7@8ePS|b7z@An zmjqUK;cpCqxf2ZrzKJ~(N9oIKS{h$8iv%sCjBhdw=A7LS&vBqIsBHR#;?K))e~Ql% zUW*IQ!LkfcfYlYfO<|=}CGlh=HOz0Tb+BlEWB5WWX4-jQ&Ki9_>`a`M)HGetP0)Mn zM|AwjNF*i9;5p8RGd*Vv&BlruG@?TIl1B+Wa&@0Q=%258JL)AYEwDLX6!7V*t!|oG z_zR_NeIL{Ci|NG~Uh9cczOPk8+4`A@@?oL^eARx>-Z=+zr0Zd)iJx4PV>z92-h^5o zU(s?}Jx-$OwVOcC^l36zuIrOS+F7Z?-gXHJIy}?)6xWVOZO+FRU3+19XP4M=pIz(( zEKhL8{>oLB)%-r?Xx4HKmVccVzxDV5#sf0Ct^8_Wp3uzEr%$WUs<)z!lbvHMSuKD~ zy0q{>$c=-o@QVWBX|I&HE(fL3n6-o=s(X{47Ail>6;4naZm+*67|7Ka=>L?Wc?e&Kd4+>u=m#`xZpSF^oD!Nbk_;fe@K@}MI4Q)f zQ)6*BTr&0rqS7c*uedCkRFpxOte&y9V3+<72rFn}N+vFRQ&(upl`tLY7$ z?*`}iTd-dF&CCr#&w49xW82-yx64vV1m0z*NKLHT%S!T!Qa`MUh3weKy2(Y4M2TEr zO*DLo3VHJ~o7r&Tig4u{C-%xZy}5Ok*ID2zJMU?g*212hAe|E&RK#EC5F7~yTxCe5 zHUx&$fFOmiEhxjOqy@7Hemct%58P_Ad)>`|2xCW2(~-hXX#E5iP53X&C>&OU@iK(4 zC?aS8N8K9l*inC499btNNGnhBtQV__B)18pmdNEc?XC_rZOXvUWO?PE1?hL zWN=Q=^qhdt30{xLUwA#@{V0kc2*K&0yIKeAVcl%IR~e&xj&va}@R&<{vspz#>s!lS zoAuUvP=3@}c-5IjL0IIYsFGSw>^t)K>Bv*t`G)szm0m?&8W^O#L)O4VKXU?VC-^@? ze=(6bkyWYWx`MRI{P_LYFH&8J*zUHb;GL?oIm2~AvnP2v2$BExbYv5h+;BkzGP`#} z*n{ zOGLGvWjI9Izqdp$)((r}buc_PMipCK&5QQb_T+U{9!%NRnA!6(NDjD6iY6K_b!T*( z?{#XY*rG_irt6%&wB!smL&ap#2+gcSFi6lMBil*D_I;LZW9 z&x;gm3kzP$!DcSk&dN`acPFsYF@<>DxpMSobg${?YJ})uHGMhb$2Z>Tqh4L67KzmE zSW(kFMavF?hGX;>W|QDui-r3U&+E+68bXyn?p+!k{CbGP%wzra1Xlj)1ARYl`Sai; zSj*GG>y%}C^JbbIM6Yvw}|{t!1V-g119trVpq|SbjF1e0LJ;S zMIH-VVYW)=mGu76`^k(M`+@`ro^-!9)dk%*n-?|Sc0)g*Kd5{ojNErZUP`zn{AjvD z2f|`STk0*AdAW<<=gbE2CP^HP0oPnC3L5?B?qb(Ug=;j)cr@iq8n4XmB;&pA#@Qm; zJzN{>bGqs&s8*=Km6`;1o_G^4w@l4(|GdcA;`Mjs!tY6xm`R5cRrIK+1!J>t#*&K| z(-H=scT25N8srG%NHQ7SQ03;DMk^N+M=aicqu=cAtrL0<(baT*MK7;y6ESTk*?o-` zx$^v-H*>I?5izSwsH)+7{n4whmoI(TClr`6VLX9>CwMndK#u<@Fxhxf3LI7vX+PB* z_Al%>7YD6jJjh)a%W+4|mSKHRXai-)$Ye<5O2~~ToHM6Cmf_l19;_mr5fqEKoWrxnl2p+MjbYbUDT7^#V zL*TH#7{(DA4Ne^pk~PF>S_mkRb6^1^pHOrCw&-NB{BR?SjtQ*bilAo?uYvhyfwbqX zLjSwgg$JbD@eAq96Rh=Gqp5y$G^vr-6qzbB`FCB3kxx%$Qn(X;+9lZF3({zj$#(v< zOIB|{A#slCfNCYPBvs!6`%F}L*#z|)-5DPJ+LA9`9Ci4FZcu|2EmGNAJT)!;^!yVE zhFS0342X`L1F+AThl{LLMaj%xYr#?GIJ63K9K2|>_U@f5(knt_btot*==J~ymZX+Q z$;Ddpefx&D%Ng8UBB;@axzML)RNpFT&g#{54Q5Nueu0bGc*m-fW~vi>p1}s)m}!v< zyI4KgB8c-up~2fyaWBh?=P53dXs1YvsdvL-tzXw={Ge;4*IL6dK4tsGDrxU@Hl+o# zi&c)%qbK{qWK{u2Sf=!TRwsz!1ZN8d{fmApu3i^|3nl3@4nL>g;`^c4h9*j>@aVJr z-VWOf8d*(VL`EVH*fW{aKx_7bf9f;RYY|J%R!B}>S#VMX=k5^Er+~<}RJ${;Qmdqe z(ri(I#BkmR3{1mH$V^vW%KGf-cKFvbP98YAH<&ApKtuUvV^2VCU1W45tr?V2Y$S>* zct_=KaraE37%sMdlBP;XBz=oG(*L&Ue&(T899M#~%hNX|S;fEsD8|IhMs{QUNB5oYi>!`r3t?vn6F7+($koC+bna#=n0&RU-N ztEca9W3~%5w?2|3-(4$x6Y(AkTk4$k38AGG+wo~^WnQ;(G(7x z$olv#sRE++%p;t+aePzhns-zMmgv&%t@!!Jo)YhvjhTMY)khmGS1nNV!ahZW3__{( zBIRmHLU1xAp0v$nJt5+P?p|LhL|Y6hC!?v|)eNVvDlEUCOtX;;jkSVpJb7V3NHHY# zRNEYP^!8W02L~p?C-ChAw+)K?i;WlAcoYRNH^y~u3gME!60%#`SKa#cs2G3ra_%a& z5Mw=!O0O@K?Zwqfxt#hThZTOsM448aL?-q7mN!MY<(1_v6Xl;Uhq7ntR~e^gX!BNS zzJ6@i(bha+_^Mkb#x>?3aurYeK<(X?hopB^ndy+B%~gBK-aYLQil4fko^8wkiUeN^Tt>k|}p0<81gB z{6(=orcSOE0jeagerI$Tn8k%g#twlu1l`A5bavIie*|?BZo;PtY1N(kb`! z`DNw;&i-dq7b?228?7^6>8zNwG2h^c)+4SeVs_j#qD;wWDqEnRfXwzmv@CL7SDYSs zYk>|~Us^ysY`ghEBgwekbv<>!o8X1*Ap=eCyd`yF(B=D;6ld&2T5h)7H4*7B*`h>| zw%oM5lgUqFX?y;@Z){}C(A}y6^Uf}2!|g3H?N$F9CkWzK7yIW`oWHPsaO7ikNKiYK zbh*$ernfknC)9j`LktDH@&6RDY@8A)F0hZio0S{e(#5iT6}B4ge)J*ejx(jDc(`_O zT}Y0&paaewY8F=dC!cDHQ)6WfE^7N~>XP-$-ze?uW1Y_C-w09QjbK%eN>-stdWggqq`A2cK6iyC!VUwzDgJ*+|4;C5>kv@PZ@UhN6F)Lw9(xNGl8<-?_J zrZ{GOYW5tN7Ffr#m2;`j*DI`z0#of;;(Wq@ql5xo`lmG+W<);R+ReEp@Cff_Q%347 zA|~BPnn1}<*_UGicIdSAK`%ZqD!4{RswV__1y9P8p#1o~G*~{vxuKLRlMJ%clEIvb z>8Or+kI!>ohAOiU$D@UtFYvA!85&r=yvXn&xsxBZ>Z||RV{5c4lvwCv?!oda5(*)7 zOkyW~*(53=H$y|$r$##I%mB2Db&K?FJ3XP|S23#QI8I8Y)5jkF;d_OafUri_MPa3Ttu`VS$h~}AI>39vLx2YLJkC3k!KNQc9>?ytS zjFXa9`IR}l4e6x}nBZe~abeK1oPL^lytD2(I7O^bxIAn4gWLJfaV06D%jIH_ZdCgP z;5d&6otPr%n!A9L>7a^;!_b<3>E>cGv+-3yE?RIY86By%Qt7TZTCk> zb3RJT&uNAi{04e9+qBI(ZjU)jAYZ+iYrMIzAN_E7dG7$n)XeDS2|PT(4~7c=g`rf5 zS0I%JreC{RZL~#U@Rnt<<_Gn*ADhdh@jZhN7D29JVnbIs1U;-)qGS1PGcL4Lfh^y= z5IO3pz4#0&Ig=}(pXSvy(NE#t27_Whk?WZd%WWa+xGcy3GN8bL<|mR``e`mSK5wmp;K#WY?{OUfIY!SC2Kk)k=|)!J)r4{lY> z+~#wo{(SI4di}%cYId=?6UaWnw}nCf>Ual=6qt3c?NT2~a72TB)N;884x0y}U+6No zUWs^|%$l*OnN-BOq;=6ZLolm;daU1GXgT$z1zOw{)3_IJ)i{&QSXKw8Jz-sr1X*y?^GgmC%Tm%f4RYlpngOSOHtoYq zw^l)CDqS!hmaYin*vi;Oin!YO7ar8GkrG579U-MGo7i4#;Va7bhC(l46eFEyOYLzQ zMZZB*Po1K?+J|j*PcgY_@tI5I=PEqCuZLLoE^DX$$sd3f|L)s;UxGQl#|oA*cSrw7 zsc2}(=<>;+?ag&PwalH}F@vI;yN9%`xf@UztZe=x4IG;RmS2D?qB3ZAOE)`L4;MEO z^v8wQf0lDFZ2(3>)!YfVmMZmo)!)ni$6^nr2^Dh>H@oBes)7&*Mzw*ze^SOoU<`8V zz%^Rna_aG=S|YHlGjP`wbIJDK%e_BAL|_>^Yil%cMHX`>)(C{zw{&+kw*;>10-9ob z&D9p|48pAS_@dohKwuYV0Cf*_$1veF?W$s zFkl*oX#w*f0K;tDNt?UMqmPM)5P|i7CV`vIz#VKmOG#%NM>Iy^v^>yGx|oEDxz900 z06{{qU#~&`oHZJOkPyiKAA#uqYdvsasX+)(P$6LQB?1=#A>l|O6kyMUMIay;aH$#& zoQA^$1b`>zJtjXK3H=_!g)#a6-vZIk_i!LDCSdCL84z$Npd}avhzJ1zs0bwjC?oI; zC^!OWTo@A&FyN9mf(Qs8BmxOS0Lm)@EXxT4`YtR2AOHcBaO4g}zixiC;3QzuFU z1;pQBnE03&xG)hW#vp+q3KNik!ePoH0K&i50ptbRJx&9YHJGr-aX<*8j$;&HmyiG) z355X!es5O@Xcqyj{lZb0azY>k5Q_lJ!oPK1Ls-2mjQM$%UcmSeXEY!aV;|3kE3&gy{I) zj|x!0QIMYjqX^%10HX*08;*Gm=mkb6ehJ6=fYFJc;aFFG=mbU|ehnDBj-P=#FuL(G zU?@jI5P%E;TroOuLO_WCGQsEtV2OSWKXu|~jA05OccSAMll~)~3WdQ<& z45WyDj@!rMIP8bP`=7BK(1PP4mLS-%H39*HIqXNnzH z=4}84`VRhKi~uGd=duIz{d>v*1Q33VjU&*vzQde=q2x!(69mZUxQH{*g?^*}69wqg z_Xigca9Hy=1ID}o!|8Edt{|Zw^}B(9gJQ?;-7!}2dkV01LO)vgVK09W<$*Dm$2EL6 zk-#a2;}RIFdF;&tM0o<8={xk;tO9)#ljUEd^m7o$*lRhUCwy=8-}e}Fe%dTa=&xq| zpRa9zr6qw(U`ull6jL!+683**69fz=kP!mJ$64Y3XI8+X{#&Ea|CtRU2w4B)8byE_ ze<2J*$PdK-rREo{{1^D^WwFxFSsBSAc`Mk>*3+* zegO=2`sbaXi<=D(a1+VR#mdtX_`Juj#aykdK^7QC$Jy#<6+qVG2JBp%WiVew;g-1o z1Gdc}FbEt5m~tT)9|Xn)fpGEs!ZjZ^v^5bhQVJ0PChf-$uviE{*{wnUz%bq(<_B{A z0fP!7F~ z8T<`~g#N(;;KT#qP5hou1n>}kg9$@0BOm7XFJFX({-6yaEc^!@5MdF(s{K}01cJF` z@xS!}Z21Ehz^VD8U4R9@^96!}{wYfoW=Z+CHUPu<2d_{ffamhR`2YisF)aUsAK=ad zCr5vW!2m1uI}C|2F~5}+2CVXLFevm-y@z7Fo!|2b{~VD9w~ z%)`yx&Jpc){B05sPPp|*$f4pw@{anvIuKT*L=epj{Jsu7%c}0GhfDnkIViHo`41z(R5H~AF zkc0$8P%pq84H1;H^tN<%vx5j~S=yn!AaEdq4n#@{gm$&TWEB0SfTEkLHw1BT8x0ZqPSCRS_C|ZULWD7wAVFPwOB)AQI|%Z}T~9Y_U9>mE z6lhjn5nu)F?+p=Db^%zB{SnLkh?OB1e_;pqX9qBRW4QN31KfRQUr-0_<>upQ4fF@Y zKhVB9+Qz|B*3BPi5eobvP$B{-1Y8&b$0VSlq9VdTGlFtJ2Z36=AgJ$b%DB3^0aq|g zkza*^x&2d$YG_wGZ+i&rN6U&1&OpB*f{M<7FyztJZZ>EP`(ECjXiFE6f9{4^|9vJA z&&m9%<&wG?*0+fzHsf(wgo50#^{!4no1Bz$v4tEeqnZ0hxzSQn>5T+pUUp0F1M-v4 z(HFh-4Y0YXL)NeCziK(Uan8oYT7tT>$mdp^I1m4D{tTH_n1{lITxgr_guXi(C~F_cVu@fw)%;O`}@ofAFrOD zS=zfXvu2n%FPKSu)9+lQRs-|BGENq@%|+k%SCP~TIR_7lm&f7ed0vutDjS_#@6l=+ zC-OHudiap~>a#~(=Jhx4Rc3_TQ5D_5a&&yFpZ@9z=qA#k1-TUDe8XX9@Zv>M0X*3I zJc2@V&#vw3sdG0Sm#-iqTM>yG(+Y&A_DzDx3GdXakEJ=1xV$)B#bA3mC=6nTfGpm# z-|nbvC6P7{f2wDq^sE_wI>baI`$F(+5ZyFv_))8wV@t-Ui3~xHlXKZvn9TDT;+dBM zFBYo`(oNXVQwNK0u7{qXSV?fj9et@=xJX;|2#@Z$oAk)ta-VDK)wn`!iI1p_#Vg|O zkUK;Q-rK9r?q0q3FhWt{&PI&6Y?}m&l+38RLRQez8z@~(y8)NfhYw!{#&guYtV1_7 zC%oo*ZnXVsiN46>-pc790ZHP{X7>_-ynC^mS&GE-4#x8?6qR-Z>G$26qec!yXEx&p zw6d-WbV_FOhYx`temy8U%4B)y6VSG-pb;Oj(sVsn#o`VD30Y(^oqSwSOhp>wM7NVP ze1)hZ&TK>Vo&64JQ46IZx00%4Gt4zP7h%gD5n1UO&&1xK(Gh6$hK6x2p>Ial<%WIN zVGL_`=qgr7G(Bk1kYOm_2!;-Jo4Lm!eyBh3CBdg8!brc|P5NC7 zF#_jOa+RE*di$4j(<@9kk3pFZ>20UO%D^O*yJIVc#$C6RNL=O4vI_2-uPJ@0u}fqO zXHB6R?J_e3*Jz&FzngJd@iYjVm@1sx#52J)E;%E)IIAWpA^CntU*hDblLa3{B94_P z<2{jP_y+U=uV|-ICHA*&^*T6#bv20*7mlsd;n+dJjC_Bqxrth-=s*?El{^@fmB!-h zN=+fjaD6VCdu*kUCG3a=;;>j>hO8bD&A5ONT==H^qKzB+!}5qXVc6)eeC5|ZzYf;XV8)S zX*4L$eLYJ_zKtOks}Nr!?|EC5nx?1+w)8Xyg!b}j-H)WJJ$q7;6Q7T^IxeOww1sQU z@Q8?&q?Nfm@?8a`j>hGYfnt&D?nL6c7I877m6M)p6pi|gYUOyN6l%d1_9c%8s+wbD z(&;C!D|vIbR?oT}vh(eAGE%YOb3?n&&QW|6%@8`D;hD*pAv2)FSN8B7cF|adM5J5P ziit`WS@Nx#nC6c`QZANG2~+Iz?0^mz;()E6BzlsX@;#3!z5LR_K{kZhYV`HIwFQdq z3je)v9XKIsWS$y5k7<+JZ5)v zqRYqgzqD1G_+{(*(1h?{Hn+rTVo73UFR!^|SDhD^n0zS%jsWzJQwBjZ8s z*?kxl?Za=$w#FIOU)oNY1_f14YOCQeg-7zwEtJKA%Ei?-KxDAQ2C*S;dEQ8|-3{E) z=v$tpbvfPC$h!0=cjcX@o>lU==21df^Zh2deKKy1B7M}fmplzhN(*WatSsb{oai*?H?#((e3DT* zZEIzXW_H_?Bc6$r)Q-j|{ZVZ0a*J#OqH@-(mKnVADW7J216FRTunDY{Nz=p;9f+q< zBiIk|+SmHsnVzSdiyP9U^H|O9g0vtzt)410qru3^`rPaH5eZj!JgP}=NhHWm7J5a( z@WEHfgQ>coyqt)QNhTept3#_$vcv^`amIpjY9(0+kYI5c*}k7-vUYIQ_gEtdB`U+R z+?aPZo?s)6^dPJ>NhW(ET6L3h^U#vfh4)RmX+L4<=eP_W$$1{EMJdsC$E$g+>H35* z;T9}x&ua&n3CfsPVTVQpS3`1_$mffV7m_c)(7DQ&A>L%K{HK!AlHPTH6fR*p6RQ*j z^Dl1lco~mMz}}DmomC-^jf%SSxE9K^=u2})!djpZv^`Nw&rs~ylxK50S33`xiKvZSoW2X?$fusE7j6z(;J#`a5PrKa z^D3#3fYFK;o=ACdZj~PF>&l<`;(Fr~R&$2i{)k9Y)B;ngyv;1MYe{pZQFhtco~J2c zD-5o_8vteq@^#^c*3E-(C)yj!?7Aexi!9*wQRxR<&gqNw%NFZ=dHJ6b%4^J@f~k|o ztV<=IgrnYy(`9N$#rj99L|0%9yHeD>u$wbBmuFR_cAYP_$a?3&-LI`Te6}_vK7s%| zPi?wDDU~Kc){2t)X!w>~Q8d>6;|v!2qz_&91C~|(g*K0*_cfwc3W}a`^FgNx#qL9mQqi_@@7HsL)bpSvtyzacTU` z=~=Y^#^7>Wy@{;-$)-V3_|UoTg66P6Rf27naCX6KcXIfLW8_*Cv`txJBGC+qY#$rr znby}-8b++vkv+o>68B#Zt!sKXzb@JPI>6jS?-i|Rsz^IIv@WjliP|rm6kN%CXH6+9 z@4d7?LXibFw$em6Z|P|09tCzg?Y~)JLTP^}Os5d?rC`3{VHF8~Ge}e1w_;zvs|tb7 zS&Vb@>bl+TsBbPNy6qE}{B^*bGLH3r{`i?U&0FR7thEe3QdQC#%RxVd6}(~=|H@NN zPaWM(f3~XOakk|gL5Vy)r=0u#msk6A=&Vg!0ZWTTA#F8zzDZI-m~uc4rv$k)x{EE7HtxD{WTRKnV6n9H5Bk1e0^)?gm{h@&GMum zWzo`A!K+`^q9fX#%_ZKR%_QC)&I&DzE_lZKcuNdFfJX4SV=@ii3nHA+FRGKEatb`6 zcHKwHEOjJo29KhZ%cDvtM1{3|e|= z^$Olh{eZn+7kB(a(8?b3UQ1)tUG_j*-FfHb3e|yV&m||=r4MCXq;dk5OhK}es4f*r z8R77hak`S55MjjjvjMQV=6$BEHJ>WuRdU84NHXZ`|l|zm{SH*^$y>%5NC)$rqfZ;=NS-^ark?Cw+mceWuH_mJxz0uVVX_s0)7#8F-@?Fr1@L~H% z_ab^HEm;G2ouCT2{DO!;Lv8##YhJDuN1g>YC!I-_5*Oox2M5;>Ry!0u*6q=k;G8(* zLR^-jYU;(Qu-ZT#G+UkpPT={QB7=Q1KD&}q9ViBIS?(L6Tyq&78h3G^gVA-ugVFXt zV51o9BSPM5jaeh?)UV@Y>t`@5$^}3`8hAypD7QjZ>nz@08aPhASd?#|IG39Fpd?8l z&LJjlBj5p4Bb8l@Se8VK8s@-AQMm2NfCwo*^B$|mqUZErEzbKM*Y0kI`+l8#9sXHi zIFw$Wq9m#fDkX}0yenfY7?!47H5OK=U>w1MZFo+ zRMCc8>6&bVvB(}%`u(|Wut85%7q#~SkB*6Co6lQ zUR`pR;hNq!bLaIf|0Hxe1J3!)tUb-ooW0H0oVhyuYlTMCs|`C3XUUukZ;0v*ELajv z;oYoz-A$DGLVY;h(vA`|)!7QKS4JEtB5SU93sc5-?_^c*jx9*PF_3yta^OO9PTLVC zjHP=R;b;|%HcO4z>uP;$o#RgSWq6lYdfq0>==GV9P-g>wG^-le?~{-FI_GU7=B=^= z(PR}Iv8I>iT$U1i&(y##S85&%hdby5TwGtpLhr5#7q`Q6vp6NhrO!o9r=7PpC+aUG z!m_kcGM#sgGMEnllqH^n!Zimz$x zXvYc>BEJ>I%xfATD034&rDjvP1>DMSW&eu8OzJd3S={ zaNCY46K=~St2jw(4HRoY?rub)M?Ll1>t)mlkvSq_QLLoA`5iC5R>!2RNU%GO-kCY0 zv%>9F#+NI&cqrL!v+lBeQ!LKHQzhNvMN@eKW3=oC`pDK`mItz@80J%d#!*yM2_qLC6`gMD5T9$FnwQ3|<7a+vFzD4SKR?MVTr>9YAe_oTCPWoVWD| zIYH8docPGfNtHE@>2#B_FRPR@?ryooX56SvK>!DJfj)$cp(Y&Fr0BHS2b&y z!)V!Q=i$Ikii&i?TDF#=Khs;BjDBT$x9gm~L{*B`cU#ZWm1HU0qc*=o+=91FNYh)| z7k;s$pXZSs^ttK)mvyEP%x!_n$zhU8_uK;21Azr9ITHh^^pXQKO<8n}XQy5xhwfp# zDO)glG9}`u>+rE%hI^^yq$Fzwqt3f@i#4r4_E{Z!ChptX**#m~hI5qn;}rY~lkjvt ze0H2sgN*Y(tD{QOMz2%rpS!Iu``|ejmQCb+oy#4(v$7_in~Szo26&6ocSauFSE(pT zR*?q+=XUoIMXBl?gKy}#c~hGi|~=I(*R z0u_=C6s_p$)MP_Ha7CNyPm0L_-bDeh?TXjAC40}*?x!xZYJGir?lD=&rBkYPCPt(o zyCE{^Z{c^I@?a_8Y=)ycV531y19k<5Sw>m(LHh&orgqjYo0Iql_3h0xF$7)=egU!+ zmOOI4*UodQMA8(*ZH5qiLPetrtL}I6IMh!WB}TN18f0aWqCSQwG2UGqo^6iitdFXf zSfRVSUF@i~8$ZTsH2+Z3Fr<=j>rrzj41r)%DvZ!V2Z3;jiX37?U=`KcV5<)7P zb%SNGnzD0u1_1RdSUrO*OWNzUl6;)D5 ztCdqoQ-^8^lek>m*B^tGmitE=!~$=b<`g(lJ6TlH@2$1Y;&Wjyyrc6t^6nj6k&Y05 zbY?4ts>HLmM?G!I{Zr4F`mG_i$Cs`=N+Q|{%RkfCdiEKEX^%>`NIls#$F1Eb@A^eD zGmkonCpVt?%}<S{rT_FrH^fr4bD*ya517r&ac*xWs@`v>D zbF4^TV@AYB6l4`AoM~GVL&RG-|GLWh)QW`Xmo>;6fXsbNmS@+*DI-=NsqGH;+v8ps z*3mBg%o;QEc?Bo&Fzlq15kFbI%*f^mIC_Y+KllFz_(Fg_Gc9;(&M;{1W z>ZJN8NBx}L)csO#+^5#!hejfUIRPE+AQtv0XwE#M);EfH?dda`1E38oRW49Z@@#%) z{SzzK-WTVO>nnQUbMWPBMJp4nm-T*t(E0=0@uyN(s#7cW@B=)jn|X)b1xyE(ZyyG* zpR$DN3|)GA<@K%EF2fI3KJQ+-aq$r zXc>UtlZtx5R+2BnhF!VHr465b3NK0~cosL-Np7%vPTns=_x!!tuKPrpQrlaValQ-_ z=11;Kmz^JO`&ON=aqZ|CZD~o^IAk%sx_;ThJW8|sHX(}%i=W%a*$V&lmY~m?6>DQy zEXGS@weOBx%2zd0AALsv5}dAh6?F%_m zn-|Upe*P*XSq+;Mq;6{67*ehalaHMZ+}^iX4Ue(%ICMK$pRbz!)+od5&#NU)R8!)b z|L|zj_2B)y;DkTh_Vsfg63wIzzRoXA5U0ImU9TKJJSCsJG%NB@>+nm%)yqd~yRTXr zSk51g^=N*$8hrHej^;%E(bCAJ;ft9?E#_B}eHXv(*;Q@iSGN`i5`&$x7C!U(akl1H zrS4o%64yL^db&rXsYmCsQ-A6`oy%Irl5dh<`Y8o`^2EDTTzaYcs$X$S4#&;mh6#e+ zoCgwO?TpabL^^2(nR`4M<0fxOvbkfs_(wS3Rl<2r~6bb#vOi z5f=6$qk_hlFoQJ3`s-PNvj)>|Q&o*@f*%JvtoWBDa1lx-j;Rkr7~N(_S1kz9LG3I4 z0Rj)+U!>C(ABc-|)0Q>l_%On`>QD79favb+0JoTn^2{u|LRrO8^|YS_Dp7ixks4#_ zI~i3$#S>@TUffSvMVTic@|xnn;73Y#b_3LdFRu+&FN$YwJ>3psVb!)vrXD%%6Y3y& zmMs19SZw2!Gsva!ryhlck>DQp`5VY7$$J&w_CK9@HoI3fznQr&oQrF~217_O#op2T zBAVQ_*84K`W75=vMw8&|NH(O=YnZ~;c7n8>o>P>A8k>p;FPsW-iC~4Wo~%m^2VpFk zx}sv>;o`s?(?p7Y-%X5qdv<7L?_4&1;e%Q-WCWc4Wg`hAZq5g$r^Ju9gZ=fU7!-y1 zS?RJyCFeAox5cxkul92f%&V71UfcY1pn0_a&5$XwO7Mu-**57uX8q&Gr2UVTkmDJC z%&b2WDlGbQdE|I$07Eo|T>OWISrhqvc>pN{tO@+b+JIzIJEa^eq5KDf8jg?&N0-?2=# z7MRx0(7%&@FDL%y*h6cXR}PuNq%Lnlw<;6QcL={N+C06^EK5+s?d^&-AmJ0qA}qoNnd&UWrc|6_?VGS04+8(LxbBp<=ovyk$1Dy%M5qTRLvU&;0I zpmSKAqilCeSd{9x3}tT9eI?m~%#nqfE#_%Mds~y8rtv*| z!(Xn%)7m_2QwUV_7k|^s$@rwvZJc1Alw7;@J}TmyIM$|EnB&Wh!7kr~vG8fHtJjF> zYrfbNm9CQQPG{%T8h^fa7TszZ702L1_~hYkkZWvazs&0A-pdTkE87HQGuAKulp`Fo z3ig{?Ba#0tMg&4g52`gRj6-F6ofEXEm;pL>-Ar04D!u;{xF5TAKy4s|&staaYzB4` z5-xmggo5D~wmZK3ReZzKSUOJBZ6|Q@>muL}okKuH{+kua6jfJT7>V4TRD=LU+n4d1 z+r`|?S#PgSaB-}}FpkqIIA%1f8S64938jW#UD%i_qtU ztnVINeR26P4nE_kcF|d<{4$G#xxD`JGQovtHtS5frFj{m6_pNqlc;n*1zef;TXjz_ z6TP2!>)3mrpe$=p{G4s|nzxRCD_tZjbMIE^=DVWTPO?Lvt9dzglwMy)cyhhB?_PY_ zJD2qBZ9JZ8;yruT?U^dqQ2~oqT`>hK8x7A2hgf}$JuT{yrY8?cC_XtW$er^_ezkh( z$sn;)hBK|d%9m5jMN^!rWCr6Cr5Yb9CB}#MSWL#eSx%5WW}WjlEr1LCg>h3zR`)!` zN*KO(vrusJ?t-D@P_d4&+WW_(&sxv&nnWz(j^KbVY*NXsN2h~X;6Z}R@Sp~`_@>x+ zw!3DdtJxFu6NIJdj5trfxVfBPz0Uc(g1aA1quvVW2z#S#@&gNUhzF9Q?ok?h^Je-; zkEFfVnLX`n8bw9C-ZNH{XO-a7G1bogGB5kiQa&J! z<;w74)!x5*rd&+GB9&uqCuH(gx@}OPjxr_mT2Muw{QwSfIJL!&t~*DSSs}qtD63jr zS$H#KZ?68)9?~Omo9=Uj-}DhLS@0N+)Kx6iZx``T5FTdn{5Ro=ApTo#5%oe^P_1gH z?y#^BQV)7x$N(yW&{Bde;DADnn#`azjL`l8s!q6&h@90*4udQ@*$6W4*=T{k)HcvR z`*cPH7mGQXHvI&KFst>yF@!|@HA6y%-x)$ewSO=a2J>p?t9B-%Q)3E8<6F@)@zBA@^gxC5$6>#fl{3oJp_#MhzG3}m zy3yy!5G<{k27|@jsU*D$NJ$Aw_-hUdF=z|E&uiN^(T`YXYuR<4DQ1j}QSH3o!F#G( zKfMx08|>!5bIxh0U8zT$ z9i|MuKmtpvn&+szbuaXBtn5eExwUH@ka9U4$Y3COl=u2=WHMNcDHn0xQp7r;O#z0V zyW)LYl7JI+&;bhfI)h#}P~wWd%a*W;NH}+s=W@P3E#F2j(v&Mjase`{qZ8d%lAreU zyvx%xkeKarJU*(X85^*=Xn%{cP8>rTe3kjOFI#YhL~oJUvW@y+1$*}Rh~`B0XGZsi zOK)s6(tEs_2R`T44PHahH1EwvBMH>w-z=~1fgN}Rby}EA@~-$$rBBf>(NksGU1gZI zIoc=q7HPtEf&@>1Y6#R{U;{}VRo5=*V{CBi#paEn03H3Q)&n;uIh`_|)@WEaH27&! z(>v9RR2~t14d(YuC2j`^n3`k4r zvKbKsNgA64#2vw?Z}Wm7?PJxXjkA()nd-BP=VZHGwHb1#WGF3{l4(kdwepQSrnTR% z?ZHnuy2X_cTQlwu)t!=cQR>85mBx}^Pt&VRO0YW%eoQlEXT@l-^!z^dJqA7bjLYji zQDuXrajo*5Qkv>txHHuF4Z2-*87>{B#k~yjKE+cew3W6stY14!Y!n?pwTn7b`n)vi z%z9nSb#yEG9SfohPBmzuin1ukoWC$LVeCVBkEy9~&^weRebim8ZtY6%C(Uo(6DH4m zT0IF1rQfN`Y_^cyQ>^LigiGbcp9?JHt3I96s)%T}q@Wz#d(w-0`m1Lvh0q-*wmKuW z8xO4H<71q?Bp>39s(t7}jXnW)IDg0+Ex3Y?jhEovyk!$9XbiX{>@{quB{w{ z3)saanlI9<=BcfBevnMWFDAs!~!jvW$qyVc{%%lAn`Vy+ppAxl%aZVjP&# zH-410#HOqLS*KLMO7>#ZnV|y8P00c$D&v}mA5nZo*I@1U1-pGcVMP=anx5tj(=;WH2C8edxK1ii07)GjQlS}NM)5}`@T<+ z4V+aMXB%J!6~o2(c^_QvTy+ioa=u?@^K0MUXQnFhXNP9Fe@YN>0(3?F4Q7dNr)+}| z;vMP95P?8%G&xfNzY_W02E)-o5TKSsnXa;$vTpA)c>=wDzZ$pI8q2$b;D@J-!A_V8Y|JrBlM|04tFbA0CVz+0@M*P7caXDyyK zdxJmd&cEu;r6MZzS5ePsCJuaA_vqy#`0;%81#D{yD^=`Yvdl#Ah>YS#Z`utTeDcqx+fe&H~l? z{Jif{sD)bqrP4S>Z!#-AQ*7NX816BCnDfgT_3gQ2l|ti%9Xolh zbjeA;WQ;-VZ7QN?;r7Ax!6u8$mTPJs_Ro(FeffsVe9rdE38MTJIep&;`*TC9pstUV z_wmkHMK@0uh@kBEU8DRkC>#di7eXQ+FcH9Zz~Bf3X3OZ$*H0AaKc{!*;X+VdC{kFz z_Z?gWiBs#@#>ei(4k11pYawHU03J-5ZBWn&yqmO77rj?M?QSV@Wa;Uv5B;TFhW>OG9@{$#a2l*NXw z!cQXSLJyxm=_c_ONg}N=*K`637I**VLeFxwdmPE@IPg>^kNn;n#{Ayax21IZcy_j@ z4Yblx?J#4rhGegEN#?$uIXbU{ilX0^y;O3e{tCbO7U$`-6^w`T!|JrHco`;;vhD9A)!jld%y#aK{<-t`y z=1@;lVpjQU8YVN%N3XtIIKOc~#6Mxqc!B^=0tCP~{^x)xB#BeuvXaPM)+k{A%#JHH zWD7rs+;g*j?X1%_Vg$L?0uFomCMePoyJUi>d6{M>$)dHSRh(YnZeVR^i zQygOT)Kj5ux77PwJnM;ohp`)EgLf0yk!t%44ZaCXW$L7y}ZW8Jr6K^dK70oUKT z+8{Z26a-VNIrfG@AA_S8srRN{Wz@c*o?#kMMdrBkfSYekK0$mTb@9eE zh-9uYg$x&4jMYOtd&ucY+Oxs2Y$Ph`_(%6V6Z6eu&qzH1)ui4@PrJEZF?W3p8%OrE?Fp=% zfB@i#zj&cj*XnZtM(>~nBn)j^W^7?5h?Thi^{U)cqLfT~)wzPLG}DZy=sk9nTVL}# zxk!z4hb`CPXH_-dib!yLkF@kCDmiLY2FzZ#)(9yJjz1BN&vbf_uvB(A+_=bpbN=3oc zK?zurU2o$jpZE0B#VJ1EFMaNqE=mps>poBSC{GSe1LMotT`&|PF6n(5C@b6%k1EK1 z_{7};x4$;LxTI32l^l(|jC1jg*u0QRSOQkZGhOhWv|JXoVyc z6#)71ydH*eDVc@sRrEh-|8i7DFnXbI1xJXnnO6O2Aho^JN{wPc^RUx0pGt~+J8cS+ z)*b68ac(6wCF>NWdgg2FIYzY?v$FMgYISDo9J)FlkDI*em5+CiKa5_%*E`f)HA~x6 zzgCq3`$DhRDt;|a`LnsgqzP=}n>@6JVVB5Ff%8;&bWPd#qalmy1ACw`(VlHmwJ?#j z6FPbVC<5kJ|J#)D>!^BmK}aUNKo@TzJ`*fRtavy3KRl{WNmWoJv)F;XyB@6)WDIF2 z8trU|ACQiittuag7*d+(6^rUmifv5ckGL$)w*c#u=nVT%{*jq^zha=TmY`d3+>e}h z_hmSmb-s(?jU`QwCC!3SwGZpv%l^8>Ps+}|o{v3}>hj{p;}23C4!N|DUzISkpF-=f zm*p~YFR)ro{BChIqqC-$Ip-x4yCyN_A%ec$d#@>VT5zZ%nh=9tv)m&FigmQUNF_%z zXSal}Mk>P*u4alc>`D1$Y!bJM`vy2cbBVdko;op{eo65V6LJ zq9H1?GBIp-pw6};nxML*tF4<8$nfo8>-|UZE8npByn?(=7?u+Np~zpT9|D2Y#mwvB zP~EKe<~T~E7| zZ(UU0zm=V(BA=4E7F;8{bG*Q#K)`qaxsD|FcVck{wk9Z?(FsTZHPzt4E) zNBG?S+Zo!YGuAXIAs6n{P?0)_wneqwG#BkM-v*<|+M=xUa`;HB?K$rRCPcRl->faM z?CxPU`Ls>0w-OY2!a)3rGJl>E`U_u-K-OtNL!MB}-4nuMTEx{kq2?1XAq)Vd|2f18 ziK=9Hz!KwLZec=O56hAVd?mu`Xd@)g6>KdTp%>Z|Rv;AEEe)v-m;#N9e)8sDr)B_BQ}EgPrC_3+k76F1TG?T?Jmd$8`kUzh%dN@T$g9nneDS5CE|b6u&l)qwTY zhirINgRmW5uTDwFLh*t}U)cF4C3lE>_Umh^-t{siG8@sb7s$84yE>OoXAHitwl%$y z;n0@oAD+uQ%HLyjO6Sa!=tj(5K>+`K{HTZ788ILxgJ{|-G9PbUh!5VS*E51V-(XaB zkBiny4)G0rqeOzb%;&4kvV-7;ftTN$QJ9noF1;e55<vw>XQ1=Ur$B$mVLFm^X#esL(m3>|vLw zEr+fgRegN$Xl#syX(OcjguyupQ3}KUdvN%*l|6A~2wR4DcfxJ&Oj~F2MlKwU5k4ir zCykZ%HkrE>mk;4HY<-tpZvSK&0SlWx6?uhs%*ALyS`VUZttDLG`!R5b`L#zD(Lm~G zHHmEn`C;6-$>pwD7=vBI82Tl}`o${AY?;0avrY~$o!To)c0023*>Hh6FG&%|lA=+j zWsDi>@1EuF}X? zbJML+^9ONNOH2EQxR)$UqyChk@ClF;Ch`|}%7ojYTr!`s%^~9}*aix7 z+R6h*>JQ5lbcb$ll&j-$Are0MFqY-bq~hMW!gA_!4q&J9QGH}|H9fR zGT_Z{eUIjw^w~H;f6YSfXWt$_i+gUs+-`QgE{!#NODDCI^_{L%V75SR^W@loqtH@D z9luvdV|}Y&WlCGLZPc}Phpa~I90_c>vFD}Qa&*kYFH3Z}t0(cKVj5h@r>q}@X4bPV zMMJE(8Te$Ll_`K}U52=dg&za&NN+PYrh0Wtx-;lQ@v#j=8ON5#Hq#|FIiGvez+aXN z_tO)B71$*96K=l(ubBwFfK$EfrmS$pZ596t)xg36d-UVjni|-Vw4d0`zLh~B#2>73pj!d9PK?mkPH%3a0O24Vh$+(docPZ2_z`* zU~7v8ju&Ij6PrRXE0SLBme#;gXFyZz1KjP=t`N+8Od#6R4I=2~3b5|&2c*31J<&kg z7C4Cv{CDsL(p~^pf?jA}v@1vu?OV``?4$$7A(# zvGHGB_+>PYNB932IzTI&E$zGj;r-Ph%6M60PIIH+0E3toFc(v)WePz;q5ppbU|-n( zw|Wr3{EQGVft0+PgfP5W(dC ze+!_W_Ypu|Ou*FfGYBJKfU;m12hk2Gm>_Xj}vngyFzJd0`L`AV^^( zL|7Q81eib*0kmF36kq@fq%kq(J^~29DSZ*3Jf=<*VDOQm-)WfmxHAY55GKYjf$0<` zAOVHLlobX#{=E($FVOCB8hFEni-;ZvVG-1Ei~`&z5}+etaG-900rX zM1)}*(=|*CgyV6+H1|D#gnx|LKUIBC01SSw0uJ;TC?<^I3gG;w8vGotpE1BKFst)j zO2;(_iNG-3K4u@I2mjQM$%X0Du`&S)g}MHx77SBRDCqd!j|x!0R*#wfYA%gsAA%OcX0mt;wBGF_yd+i z2VV^kX{@KW-cZ*mn4j#*bYIU>T3IAA6VM^-X|{VAex!ULz!PyybcF!pbewVn9-|*Az@r^cngT7jLxhei2LXNr zu&2j4y&ytAx_0c@Ai!?0?{vV~0WR`;`n$vY(KBxduv`YvK|g#Xpfkt0Fm4lA*gj7C z0P6ZZZvl)9e-`-e%)fvBMNrkj1_N?{rxGLN z<8Pw)0M7SVEI&3v3;!x`IpDh{7%0aLU@muOH*aSLD~K;b00wl54`T1_?d~NeDCqLf zJpngQyK^8x0DRf_SOecl`L&q4jV;6qgLqtRepUfwJ#N6k%~c-wo(hCpUJMSbazo)z z1RU_^LNHz^oC^x&I`@mz{5{dOAi!J-fdIeu;|I7cVL;n$A^*@Yzz_3-xc;DFR`mc) z`HhA#pMTH*%=SA?6eE=1%8CLm>JJ)VF8`MXTp|CYVZh37`H(`W-+6%og7}>mU;_Z) zvj4X%46vGi@F$EC`n^7wun@*_VSc~J1rGf^AHeY+@`EBp{%8Y=L}8xB|EUiu0>_w( z-)IN`gZ@qv0estUG*Ku31AeE$F_snc`-f+!C<54T|2qu{xVztJC;(^wMnee+|B(-c z{A0|aC=}q||F;LA1|2gI`I0o1Jo=@b@vIrQkC*ikz2*jWLKp=s4n&0z@ z04s67(=ZV7H=2+T^beT|iTo*3Ar!E^_P4Ua0FwKiCJKfALi6^tbZ|y{9)BcD*C7xM v*b<1Kj+-0C3?BO?Wmj7_;Dq+^1z=OXye&PwkIfG7DuqOVI5-qE6+!epUsnzJ+dh)Wbge@Qpp~Xy^}H{gcR9(6QPtHG9n>Dl<$62 z|9@|JzqjA_`)0`dg*aI9V&tCqSj&G*3njT79YEP&AbmX-dv*Iw{dq8)Fy9#eNw6sa{lt>-sz7)N8b+Z4pu+6eV#+##6o-QELHC{VX!H9?7x4v zx8N1&e`?Mth@!d+?*`5!WDT{DA*5z~ez(ulwpg^byyMAOn!>=H)CM)5Y?o81X@Syb z#O~}~(-dH6>VU2(fa}XZXg-yrPkrq^4V}&3icj2L<{8V z5(e{sTg6{Zb?K6+Xw6!q@}+S;k6yyVTdLhYgLBuTcm`a{UaA+}fjfB0JS~0J;G}v5 zFJfXG2hQ+DU64ux%XLFwzn1Ric%WfX^6A#c9wseQe42H!BFatio%Q@;E%sGr0S~H5 z%$d%SsW_mXK1mm!u4nSULw4l>Tkbpbtk8T6n4~NDL1%fYk-A2uYjo;txa?vm?CQ#8 zb4nW$!dRPhM)7DQaxqG{o_^~~Lc5o z%b4N$X#Rwc9Hk?xVuowMq|Wec=JW1WXU?)%RZ8q25Yg;6&Tl2PrNrMUOn!@E~ zZ!MOwbA&2qtpkLOZ_=9J;HoW!H{YAHx+I(sa?4hia?McxizR-Z8oidJUv*@Q#p#zV zi#o(PW_`E1qpdZ(gyk}(-DDxxi#k)vYtz-UOqtj3=+IfnTLz{|nMI{DP+qefzSZ!6 zH@cM^r>xkhFaAYuuaGeeMEY2@sKN=0?;E-B?c^F>A(D{O&%y{l(|qI_bw0;M4$cT8 zygW(En&L=JTDACIDn*r!%M@k+YY{ciK}R^h?lC@V=&MVUsdqhAOgNBEoKsA~)$-6w zs5UE|Qp!|hGzbI?{7JfKd?~> zpu`@-YC&>Sjy67PXRa7@2(aMLQq{|hV=PW6K+>&HP)d>`4#OH6R8@zk94cNlo*^C% zh*s5Ck}!LtjD7m@B+2u%9RDX}pcFjAy|zACp{@CPS7@_BxYn-4Y#k zYG)2|q$Cp|f9;`eKt=@dAdfHTn0p zjXWul#jL~HXhpRboM*ec$U3K)Hgmy!j>TmK#$#s_I!5BA-K?f+oV%U8m)lj_Wei_S zb(SRg^`8xXNSw#BJl-v!twoLl2(!6KTNSo%vbI9(Sm*o71W~N{bd(7jieJJkL!f-o z(CsAx-=~FM0AWIm6&n!v?gg`3&n+BIk{GNOXgDqv1@uDh13uw9wLQM%`5~1WJQZCZ zj7rWePq4+jQ4+rz(Ar9W?(7a;-n{WsZp(3mwAWXUxr3bd+Um89Ts0+@)L|nr)2?$* zNVkYzSZ5UV%Q8f{9on#m*AuLJ2l5yCWe`2L#fN5YEsTPiLmtW|nz4GedW$UP4T?Nb zE9R>cdFC73yy}(8U;fxj{S|HZEm3_x-sEW50YQ>-uW3T^f&BWVVBES`XNd_=gvYj6 zLi{(7MEM2lmaXJG_G^Bfn`BL`8ZH^xa%pfX!P1ZBRF&DV;PY~5IdWf00}rCHgFbMm zZ>&Jq8R7{?H9E42;eY`{ZFIqfeR-3D2kN+3ZizxhGj&1ORoZIL$`U_q_a+(!W6`|- z`uZuQdBx__*Q4>%gahH40@MLXHy+%>23?SSyzIRX`A0|74Y*!WZZI+$u|Im7QgWIy zXOw z5vX@PMREbe7XtYjCC)Ccn1tXHeIdz)OsNNF`o6c5U{Vy(Zf&SdAPAs2P9$E}D*qZUkWQSQ{0 zm24sQDS#F(qwlTK@&vEnr~SqmcWS#RM?@I>Y?&^`s}BpT;M1o>)<55lh)y`q(u=l9anySr#p;69$8E+6`F`ABdHjpeqT}8HT+@JoKwo z^2sljJttayd)kdP*!p?p=)G43F|n_P1if&~n8VG%x3*5sRwO83yE;x5KMC95GDY5DMD@I`^#SNx5PyJOWOe$vb^q<}ntMWQ4 zRONR>7JTj#F50{{^&W^@tO(Yj0b}@jRp;4zcLfLAqbjc8yr|ZuFTs_@uIRTj?WUUH zl=h9^SaA%$`IMD5LsP%qO_|hl)=!^lVJk@(FST&2W4;r0G#7{eWRcqN2%l8D<<%4) z*%^E@h2bmuM`LP?Q6mOqDE`GvDo6+#xA{>%;JM}F9O4BP?-108HWWJxzE2R>bD>4E z1cV2JKh36Jb5jb13DV!j5;l?OOu9Zn%;@EY@8E;g{Y2ER!#5OvCVw}l(5tMOD*^S= zQpw-Ui8Sb&GNTq+UqzPitj(DE#S$a6O^|a?Hr?HvH_u0DmL}VEIgIS-yKJX&Kr~9BO8gJv|>KHPAObd*xoVEF85-9QOGRg z4j$j4D0UudwmID@2-R&ffIu&?@=^aq82%BW5%tGyfl)}G-MCgX_m{k#H8eu zjr6))adXoxG=k;tUnLr7d7PI^$riO3_&LjTWniV~8hGJ+;uyEQ?DP8-YWUaJYUC0= zPU#AMmi&;kKTc|AKy&3wgo?1=p^c-&zvDjg|6kl|;`24+vPvUp+%_3R+Kb*N`JWA-9>7dm*>@|KT zgOu&Y_IT#fdeCAxwOLA00~Up+tZe(_HUmgy>)~C^_hJ%WL(F7+cO}WZ5vd=-tpqo< zPnWRC#;p!Fe8FS#QJd1~7iPJ2RfB8`9yID?6fZZdRGICL0ELK7W1b^3%+p(W`Yb#rw52@>k`W) zn}`)ohlG@o$Z8N4)Uh5r;A>>lbN5Y;=*Y zblDfF8+&TT%T(Y~4iBb7++I?sFH_A-W_C+%Rk8PsddV^eJcWiE8)>7_H*p9@!gip8 zXLkqb*V=q_`*^XQF(YN~E)ylp>xPSA=Op2?G!oflfx z1_tG3Ik;zWMCB|t&B#oIUyZ%l$VCmfTN9Czjp)yX1-9=(K#Z)PN4OrH-P)M|&Eep^ zr-pTUfL{|Y;F zRqJWh>l{iFw^GOnuix%qRYy2|w~6n+^#K2)uXTF6b6;cANH+2KUE(V< zk8gZzwn=Y_v2d(ej@o$V=B*v}_4Cra5R+f4#7)~a8o8!#ULk#BtM0~o8w=IKvoSL} zuH+*-G;&vjpRIG;+-aQ(>5pXXWn-39uT zPrc!!k?K$qQkvbY4Mc{yM8$)H$1yGyAJc&91CU7nZ%2K<{Y-V6=k9UaU4}HZG8DgXlwkMnr+m0 zA^BJ7^O;{fD254|+jyPs8PxOb8$I2*;dQTip`ZTnr5K?3u2+4t=rKRrw{R1P5K*bb~3R^Pua5Yzdxh`&Vt8c$=jG~Yq3 zt%_|##iLA<0}eyecs=W6EDdf83&$s6hF6tj`EVY|!DAMdo|-b`Z4$F(Lv5*!`G`*_pk4)&!3(7+UmLge(S7* zevmh2TlL3I;ExT~<84FCRw4odLH*oyJ>GA`055{f{()h(T7TbjM8biM#{aS5C>_^A zrNBv~_|c$(Hy}V(y2r+Auj$BHHqq3E&0@DmGB8dz9M)z(*AqNa0rzmHi*_a|ul!?Tbr5ro zP+Anx2~zoW-}%P|D+>GwYb#E_Od7rYGab z9S=>$bq}iG_pvZD$EA~}XR=wCkYtEDXGhcZW);8AmebW-UxtO0bO6%M}J!n06FHg}W9K zdZ&V(Bucf|l@O@9U3=~l^u$TNvo0t?{f!(|X3b+&`Rvq@g^Dfq8E$$-k!Myp(nrx7 zJF;D`)swsH8MjWa<` zqaU7kT#gwFnL*zSCS|PnVv}3=p5pUNdPb%3UNDbs{l$oACLf}_$YX@w|g z8!y^CkRv`{nu!G~8EXVb0pC@{P^|Haaf)*6Tx@{sV$fit^&!u&q z^3$|*P<)zrQK63a^P+?UK^fl4~wLxp6iiaF+^dou_7pANqfMxXZQ zK4ElzJ#YCj50S|KJvcBJ{5)7^2!TUw7sf}jsGLF~5N0YX6Or79Pt%88*{{(bAZ)F# z&y#{3hlC=6M<|)@V|x%N-Xt(&#?o`9Z9ah#%<;-^dIE+2#qy^Vujz%)Nfff1l_R$C zaKTV|Fi+1|W4(!dpq@v_ByvH1L-mwAia!O-sk{G> zrTC1fMI!IqcEIHQWV`ErdTLbQ;Oiwl4*fXDp~O0ShRzIicBL3Ycv`un8e$`0cdn{& z7wH-MiD57F%FK}v#f>o>nVVSZ-^>V35FX~#?l<9y!~S9ztKd3dopP}L5CV=o4}J_c z0E@$PR3QsEB!NaXrr-)za9=-lI}|RiV6~FLBySsU1e*7HWI)NC>A?0txKw;4TO$|a<&8;0wR(C*m*o^gRy`I10e)#93F}+?| zB3a(5XS`O{+&E&8JygWxUIHSQ&)?q5@K9JgRHe0aR*g0=^{x5kpgxvP+p`ofj`~!C z=XsrpWZg0mbXz}31?`CQjX6S<~l0@1!Av3(VMo&MX+!G^}MgHu|Z=c!H* z80HxFcY(qGV)&$WBx4jg$yQOJVjDbL+#c-8#UgeeXCC2Z2TXTgNmjK?pKeybGiW`A`R~ zBq3-P+cg7K{>X=1G4Ek90$GCB?)lOSf9OVDWuvodfptREph4V?&%eZTFIM5uncBXZAfQ3QyW(_Y@&p7)6tED9HGzf zVA42S@&;yh*) zPC_DSpvKQ}Kals3n^=q$vFJtx2_?NE_RcYAf7m};x%ld`xPr*mY~hW~FAfH&7NIZH zcZ_^5h03HArC(l8koSA5DaqB(PLcsG31Uct_{uh)GDswfPc(DSMj|dfj4)Vm$<(wCbud2Yxc2Ih_&VIqONC zSlNcRULrk{_@_MhZ;lV0&rBJ);ux=XL0~eE`sxvdJzrem)fd)xcS)@exTQ{@@&qsO zue=*2z3*OPlfFlw>f6+~Pfs4!rcK|xct_*qz$~$ai*KKPflY6D4KFv(Sd#V)E`^eU zL%0_YSHYedG5+P`*e+-FlG|(Xg|zp^H|Hz1l?&d`ne42;$REhj9q8>wbyH?%(bYb_ zHcIhk!_QQ;_G8ijCu`r4PN1;ulotuF+{K|QYg99**d7H51zDCmiw1wNg^nJ538y#x zcw^V9OEiGKWT~-*0;})=%@h1?rg7ll-T~`EZIoJT?v|h1qRlcM-dqt#6Gn)V)dq+U@itS;^z$l%`dUc)pQ8gi!S_V`g$}`xFu=P3SDXh! zk-A_c9LI_#lvY9XKG9GAM)slCv-`bh3n0Uo9ehR#=n2?Q@U6uE!s@`F)fl4;$EFUW z2Yf3#f@9yRu_&@eUX($Vti2bzk}Ti}Z)Q-0eLxz>m9hgwuUL^D$)w1Zi5=gEvB|s> z^gSogbApE@@fS{lbU%tZ08E5Gl%;>j9n#IU>&Y7Bdt?B9LBL+@cb`)txVEMAtwnFG zH_b==`InuU)Wn6p=QLAlN&QBjG>$ep%+{?gs=tgpJ21#__f#Dl)6@y1o#4N~#s9Z~ zK2cODlm?;<8p4GAI4@E>NVu|Ek_oVC?8*60Kzo9h0!2vtZ@fzJ>RxytBE{XTAnt&} z1*ybywvd`5B>8h2$$L8fm1B0 z-=6Lq>9lHXyYOs8lAwd-xf!a+#xpnC=e&=ghsI#?j_%a{W#gn9DipRPuS?LZF0;L^ zZH)ZNcS_eOb|XRB{Q4>#A3$$52>EZ{NG=xgxa;!yg*uo5@2fmb+Cr4#Irx<65$ngfD`~GTcU`hdphIIv zO$rn)Ig6T%F`b!CgmoekG1Pa7CVt zza{>7vit&=!-k>cid2S5m#{DS2I&@AEWI)RObiMV{Wxo(Yq{Jbn(TEn%~YzO;_f)> zz3%$yLWX@jdpl+Won%xC#Mrbt9xV`Oe%UKS@AdWE$myc>_m9L^$<)~?h7vRlY3W2` zGI7U}3RzR*2cLJ#uhAH1i)72OT@2Sc%|B_YQA8THaA(e_34P^4Ff*d7fp6I`w{;6K z=_uEIl>xc@{QVX7K(C9WoQlC(CbP9iFTbdq{cu1mGGWeof&fqOxgf~Dn4U^;lGM1I zWD3``vbp!Tab*VWpaRHUck91pwaQ5$mO8$D&8slkEM9_)`u%dHw5(p zo=<>5-n>JS-i?|L$7JtK2P9gEw3LR;j>0b~m+3y`8M^5YISXC|>(BQUJa#VaG+9ld z?nDOm^s*5Ryw{)HM@r_@*7khM>zXVX^A}pXVCiVA-bY-0x6V$Mcv2rVUMziwrJAy&~VrfL^*12|o$<`3~3}BEy@4!4rHNN`h9m(+`)scFR3+B*#Q&FEDL1W9U zd5rNS_jU^&DG8hVZTjDY6kOgBne`xl6Pm6rkIz zc;9Vv_f)MhwJbC3A?mH1ge{_ zGYP+mH_2S}GA224yMe=O87i?>8KpRTwHa4~=bKG{%b?7~wfFCpke9MWJ8oVzzy$6%BzY?QEk)cDhJ%s}b zpK4YPk$F1UEQ(vDR(Hkv+%*?)UOQcvxa6G|D-^wx z_i3!y-EFcjKCU_tKUI0-2>TM#HJcNL;siGf3i*q9E3H#=8xKO(XBNtA)a>`6$eun* zz2InDb^kNh3wk9zK}33jfS6Abud)90nd>R-6j#IEx!E9j1(n1S%XxQ)NH%XoF4FE! zy-cZ8fTud3{7GSg4_Vko)R7q;f;6@FC);3O$X&g04Z_(gE`o;gEXJxpUR|dc$y(BB zqPWP;X%ZYg@`}A@9>pTlwy8iHpFpvQI5PUWg(ff4kL8bdb8nn8&n%+ArD9FUsOL5_ zst~27azw~!4mO*J1k_cVe`9&VT}vkWIkMd|LoX(~$=M@+Q0yJQ{_7J&bb^ofH+EAg zx(>5fsd%lx0?(iCvy5(1>vR)2C9m5v{wZlwHsW*vH&~kjK^B>Cn3-qbb_$r(R7EG| z?pm(p%f|P3w|5HkK7Blul(kke7q*IxBhPI2Cu#5#92_X@FJ9C%b$VQZ(L1OkL4cc= zS?kydqof{xy{XVkoRI3EK9{|fbTOsZc9$FF-c$QdAzUljam#IJpseCsE*Z|{Bc1b< zrMwmA`b~%3D&T5jZ`veor#e54St_~~Vw~%{G5>l*AaF$*+G1DPfC3jwKc|S(de&L? zenc{v2RE`d?vs2z$twFOZ%(YxM4H}REs=MOsrQzzU5~+%?wGzk`J$_jAzJyVNZ|{o zWQkK?8vPf^p2hKjNi+ls_9}*O(){jTKY2v!ZB%x8Lz{;MZeL|cUVf=w{V7}QWgN5d zGxP9sK`~gZGpD0>z7RY-G#5WXZYQ{Az?RnkHvA%b>RufnvI#Va*?rg^;eynPcdc*3 zQB^{sk}`$GHsoDcaY^0J>*?nsnTyS2_Y%al ze4p{i+l%Fb=Yy<%#GR;Wn3~;cqiRKZ>s2#}%5P-)c2BSJ4|?T{77=}{uvZ@%LOsfA zNHKQszC%-=7c$tXrLEOy2wUYu(GeF}nHaV>(xzJxPf%ad*VWJPWBPWmwZ19o{|#Fh zeI0$mu$ySCHQLdWDAJ2B@@8Ir)(n4XUiP-MaeCiO zqI&)io2J$>c1LfA+I=&%{rl-@=M)oCS8r6vZ-+G}NXe)`dIHvvk*+!Td`s;4y#4L8 zXUe;9>g_UKURbthWe*pKHYBYqWOv!RNRyn$Ryxo020Yyd*0;)jTaG`vXk{C;{%#&o z)*A6aH_@!kV?AZy3egLPZ!Gk^v(|J80V)qFsL35Anj@OC%q2R^KhdBlnj@_5W(bp6 zJMca5i-~L=%Bsw_?CfGU+4*$p{L1z469(c0fBf%WoREfKEO|3^ZhULd#ALS~XGdIL z_b6U}|GOaKMGs@f;0y0Om}DK(a#$acJ%VE$givstfc*rw{BKMW4EaO{9MDFq0QAhZ zh^u!(&nI}P5ZK>1S+VLAc)%IkZe~tQa~H>wCv+tgee@yVt{aWDbm;lOnxJfHQD@w{ zbR3*aRh!jCDKSdMGUxsD3{LgThL?2qaZcVB-Uw0?4CB;^N@I=}v308sm_U+0ew?hI zA5$Kc3y%H##D$LiQqDy#pM{U^!e)Zz_H_O6>PmfeVsJ6o|C z+R?tuobr6V-0q@(ieq!EZ%C%lE0HcEd_9&ai4V7Sv#*LgCWvTAPq{_HW)Mm5FSmJL z<@SxwjOUF&FFvrUc|=F*#0Ojs99JbnT@${n%drhR4WU^cXHlAz3*=2mLv_@8Z_n%v zRowsfI$FHxjNr;e6JzUP8I})8ox;!+Kcj8$Pp`UyN#P%J4wqh%QNwL-Cv*~)j-$eI z(sd1ds}&N-2OwRXpD420n267Pk)myi<)vXu>Ykw>&%SwyJvTZNIgPWJbJmrPkYcW~ z&AcX%?i_=2!Y+1^#&Y1wQQ79x#<4LDwhsZFCk)OBJ}wmT7w29gx@umyazu55LfavB z4`!@Wg~At(#)x_e3CN>ly-nusN8bYp8`i&zFLr2~fkA?1@WcIu#$1gSWY2@ttaT9C zmpA>k*mbu@ctp9V9M;u4BoTtZMUDw^KXl$B8q(>Pye*N9L~$ z^lY`Bx9GSt<|d1LIX6=uF@F$UwzRZ=h?nZ@oKd@@el9;}v}imiQOcS$;@ zIdYAtqa52X;A?GHpnB`R`N%@5Ag?t|_*US1E`4-5`j%c| zA?G`N8NYPV%-YGZekb@+$`cWEz_Y4)vC@R*NV|yO7RR(_*cnpT3S%z{brl%cheq@D zPnS>P$=s@TJ2hqfG%&S_b14#Jb(%?7ZlFkshTe6MKM&Cac;eHkAEvtX^E*=*0|~GV zBv{9m$2O9swfSCn(?Lgz5q*rrG)i1j`!NMCX;w|(!%*tcPO1_o+U;)Ks*{|We39Al-z*uGE7|EzwvxOK_p^|j&^poKvWzi4DKQb zbDo9vu(Spu^Z-q9xa#3x>juK?p`l$Y(GDOnKU*(%keIt0z=8J_pych~Web$;fH*qf zzvE?~jCS-TK>#h>9D!O$;2lsABo5qaf+T=tOBaNB=g|jZ`9LV(*2>G$+S%6I#n#Rn zgaRJ?xc73jcK`rDTPqSVYap%;sA>yjKn;5!11NI@GN4mukeCZd%oRWe5CszR06{Pc zjt1xgiTQva7>uqyz{fE_WSy9yqm8!%Ae8T+iT}nf9*@({W2%4k;Fpm+7W;o0HegI# zEbY;N;Qnd=<3hr(zz`hzYwY7sr|TjJ2?qcFNF@KS{lEZs3l5AO90>-7T>^xJ zAxTgOFi2bi41xlYlrZ319ZW(1bYh-k>cfzb?>S5yQ~&>#NPa$t0d+A6)5p(*fI$Ei z!3aPC4kVx>K%Kqt#N&*Gs-(i^im>HNj2`0x7fnf@hkbuHr+9CkPzxM&u1@Io1ftN+7 zxWsWnh@*~k6kwT<02~Q{0t|k~3kUEbfCT}D!nA{f5I`;g2n&-RCm=!L=> zdjTNudlyiEW1txVLlr>zPc`^CTt9PwT43McyOfT5fQv&gY#)=4(Sv{b$JD~`bgWE( zLSgRz=>_-PE;3I*b38M(#bpWFW|Cx?u4d?|%Cw@uC`hd}ipXpdve&_^7 zAAU_3x{mLGJ}|oRGhsMKf)T(N0<>au;Dm&d0LBEP7r?6ZYx=1ZKXZ&wfN>`|&N1a* z((zb-e~zj1Pr~R0W>hixzh4Lb>gy{4wgqD#Fl#d)#N(1K2!e4N0GK}L_?s)BbRGnZ z>@my=1Z=MYC6b>D_3^?D4EOh^-xu@mi#PO#^|J;6T5$Z>76d)EM?dTY^cXkEchdp= zf!-bv`S;pCOwaMz8$g32phe#wIAd(oafdE|zGF%xfB^s)KZXITJ;3vE(G6Hjev~kl z2$&kjV0R#_`$q`_{ejK{&@Bv<1TY@JXJyAV0Ye4?me%j(WAg|9!RZeR`2(jn##SEJ z`fd&3$7o584J2SXkDvH}0QEU89a~V~3?FmzuMzsWXk#q2GNAe2JN)-K2Gvg+B?tM{ zkpB}>FQy;|R1&kc1VJzzi^)O%do@wO`voe3f&92C?7vn8Ea<;I!vAYEuqa@xk9(8= zdi;ek3?n}X|CgIzwD4abMpA#&#EAbV-`{QX_a7h-Q+Kq%_$$C9!wB*?Cf^6JrN^xQ z_+}QESpPOw0OIN~UfF5Dee!T|_jYl#0$m2e`vJm*K@Q&D9_TY-Vy^!@6Lt5p7a$P> z{3shAYamYl*Jd6zb|5Q^-{NNTvkRc=F#<<-H$}|P8=O`=0|icf!B8*^3Ycd&LyKQN3Bhq*v*KVT3f0;9mc!7%5If53oc z`ga%{uzG*MP$%HPEDf0JAG|;r`FmS17zTV>_gg�s&|@=K7^A6#P4VV1U!#VGy|Z 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 QgkeHR5Eqx+6?xGA1MFV@e*gdg diff --git a/thesis-evaluation/figures/totalCircuitCollections.pdf b/thesis-evaluation/figures/totalCircuitCollections.pdf deleted file mode 100644 index 99d4f23350852482f5dfa2eedf1d3669ecea0018..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17475 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 diff --git a/thesis-evaluation/figures/totalSingleQubitDecompositions.pdf b/thesis-evaluation/figures/totalSingleQubitDecompositions.pdf deleted file mode 100644 index a7a1a4ea33fe7242b4069b2efc32c74ab2dfc90c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19814 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 diff --git a/thesis-evaluation/figures/totalTouchedGates.pdf b/thesis-evaluation/figures/totalTouchedGates.pdf deleted file mode 100644 index a3077d54667ab418ff29745b0a654c47033ce1ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18909 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 bfUflLv~>48Q6~@)5m7NhPEG|aMZ*6F9i7gS diff --git a/thesis-evaluation/figures/totalTwoQubitDecompositions.pdf b/thesis-evaluation/figures/totalTwoQubitDecompositions.pdf deleted file mode 100644 index 5a16eb0a1509748e8e00fe5385738481931be925..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19402 zcmb_^1z1&E6DS>rP*PBVLpPk@9J;&V&?y~~QUZbk0@5H20!m6qNlBC%NQodI zp#qBX_CdYZ>*asf?|tv^E!Ntz*IF}cX7lU)Rsw7R)be>1FBcW)J4qvb4AL1jB#|D6oVC$kx>cT~X+l1`2MjUSRl11%5+K ztIM|5USPrRm-4<|iaK7FUbbL??*J`JFE3jUS1wFe`A-1Ts?*0J>h zn*eC#6aZ3eeZ9c^$}Rv2GCy+JAGtEv>=$yN{~iFEZZ!2Cwg7eC$>&Gedb)XgSOeoh z(+}WRx3zJ!lyUO~FhYPoxS$Z95JCVhhycTc1^I*_LP9{%4B!SZM4%r}u<&<6rCnX! zfGa4v(XVVl-~P!*HCtDEF9$I62W|yNXJF)Deg$WM9dfqTZZ@{)0eE_O*jl=Pe6u#q z)N4!>UFY@;+#UJ7Ud%J7ifZU0^psP?N_&`6!dB(=m%H_yr_x$?L64Zz_-dgaU5(9#T;ThD;dTl8rJM zx*y+JUq`mDVnH{I6hw4+_@AlNq6ab%3v#& z>DLvyZ>#u6hhKSw0=``S`UZnzCbS|Uj@6ZO$bQyS72YMJWZb52LbA$!%}Lzruq1rk zLzMb832T~ffXM4rl+5~$;qHV=*4GbT<}{}4`tvnX>*jjb#+qxRJFob=8xSmdynkG^ z*7(s}HGOvh_hEQ>7qOC}cM+fcxJ!ySod?AuORwt>T^k1|VNN_IJ=OJ=&Z{jc8TNdk zuuGyzW|cMVXE;s85*@8frPp5;!#l4uC(NW@y2L>0UO6siB^uZC;!y0SwA_2oWu?)~ z1}*FOk3u%nB-MCR7lTCN^OqVnW6Ijn#@}!-WpCrsk2h^tSV$3y=}i$gQw10ol4W@% z+y~jeT1;=N&dkA$&b_NBD?QkR+fNvCM+E*NW2L?q5?|zSo~8XDHtOz$wRVifG{dsz z9wn_5#%rO~rp+kRyV31&T)NaR1oq0o-Znl4;Y+*Vw4s`Nmh`Dw6veJ4N{3=Z#+a%4 zs*H~9_In(T5f3K7^=HFX+;W50pQJzsi_c$=OTU}WYRz2&y58+0>e*h|p_x+m$V52M zKdp!)UrghS1h=yx4JWAm@_CDA&-{2OS*~;GQ1@$RJFfS8nAje8%^LF{?5@Kw?GwJ`?{B|1TGg)W1+HyJ@y#)@~t805BMNf^_qQb`cz%021yM!o5Lw0^-C zC!Y{ct}P79g1n_m9GL*s8RoeZwe;bmSOlgHG<3rGa;xdI$l}`T*&q;hkM-{p7R z=gfJ+L$Q7xXuCA?fD0whaGNyqEnBsl;UM*4@DAvFX>v$77DQV18EROofy03V_o6^T zS-ln>IZ`r;>@Dx0T{2Uh^^tK4#@T{{XU2jS;*|kdiHfOQtj1J9bRoE@x19D%{c(iI za7;!=+JZFMLM+DU6)!~A=(@r9BVsHj2u#lTq?% z>6O5oVpjF$DznK^l*%1-DADO;ZjA)%`M@6+MN?vHRMD1B6{K^CW=0Wou{kLO^xmJo zwVJ^g0)`E04`vU9-JNe!38dscAYAMX9j#6MePd%e^GbnZFDoZt`K6axD<;t+CWUE?t z-8aqk;xX7eEw0|tNw{J79-WOQRd6=`2VeyAsH~#MJ5apvXweVAqtvl6Fi_ zTE<+O#XLGTdA*bcqa}ojr`*RodNBlLVWrd+?vR?H9<{a&l}=NJ(mBT%FUm))NplrR z5E~A0-woQjT_}>G2Xj-fkX{)v)}R6~hY(K@EiCmYBm~>UHr{g9guC?m6z4=SW+zgx zO7+}FMcjX{R-Ait{ENsKC&Nc5{Ak z#8`ig(mTuwmVByf`8j!6HL#H2%Z%q@aP#Tn4BFW(d~Hfld3u=XWLi}M#_ReYIUdD< z39r2t(7g2c3XU@7nn;+~;aMJBiyY4Er+NN4Nxn*?iyC!j2j(e3IjvTU&KWXh&nNOM z7eFZD$tY~i~tW|ZYAi~dL#4&TxEN_VenrnWo-CPV$f=_Qb;@0IOC8vXUuZ3kXzX&MxKpu^(lldsBNUHWE|fTvi@xUXcL z>~%*1%S^5W-xEhoLJ@oV+xt+%1P>ebm9w1*p&!YrzSasuSRb&R?bM;;yfQUA{RwuyDNMiEsctNL6o9 z%`uR`>|u^h<|@Z&jcLW|%deZ|qQ}+1dB#U0@xoyr>U+6;;$>D(QAkrs5xL)mB)P%> zI=coap%zqWK$nqe_HQLnPZ`bB#jrc%GBjTAE(|yhC^cMluJi;u3L}=&40z&iJrvL2U)4z= z8Hsr_bEsRB^)kDZG2B2K(vdi@zgIG0tCp>w%NWrSt*WO|z>NWy0;S^V%;i}cfchz) zwcV{|+mjD$Yjb0@-%}6d@{lo^X;Fd#hC9x}PU%Vumjq@fv(wtLcSHXSqUqkQzqtnR z_}PaDOp*3%jcAgszqdvuk!aI}?L$3cc@V;m)!XNL!}cs6`(sVHs+#HN=HBXv z?kP;7G1#;Fhuwj&TC9$v)t`2RD?|C#+=yJq`EwLrN{`2z39r7`DgBJ23koeBc}+5e5QI`1e0pS8V2G()(Yc^hn~S}cKyL>N zBS$Jc$yxKn+BYdubLtrr2GO2a`$j>YJuHj3mRiCKoE>t($qB*nELy>;UWEBzInd%31=3x^;lj^e!Ph#~g$ zn!B#e;iY&hTkB$T(Y9-|1CgK<<~#w;&bWfB-E<`ZO{uEj0T7@UY)}1Qw z_SaC$VUdsxi>qsI<+}FzpG`daIC_6TZ2Kw^Be8^3Uv;mx`JF63j}EI=VvTeAIo9(` zwa7s+^dn++nS8g-iM*0%xbu-t)== zdPulwA_uwS=Rt!~tm}}5;CGe1rZ_A1P^n~81*88gPnDQyU-_c;>riUvSmp_45Cf;> zn^}9M#P(@(9Xmgbhnh0pYIA>t)qASkE45*gA>7P+Qa_y{vtn zE9d=eQtkUs%N~+Ft0nX)Wk;ot`dGJCMUO$)Q|B>1`4tlgjC!xFUSY)-6)yHF*7X7x z$}XsVS_{=4i+$^x zBWhHy8KIHp$Z3uLf(ZG7O^a$urY_ytxGh=EA{DE*h_&BaEc3py>RG;B|QyZWLE0gXjLLMt(DB>Z8w{wRIWB= z{m*)$q?buMjx9z}<$b9N#^fHlY&)RW1-i`Qn6Dq)v*5hRQ~9Xbk<4N=(U8DeAt@C{ zWsC7nt?qL1Iq6L%-V`o912;Bas;;s{3N1i9oCrBi40@6;i99u+PR)3B3^^GviU5}KSSJe69o_+2Y@0g5Z$JC3s>fH@q zuP#)HG{Cj!vIt(;F?Q994=IyPnd~~2wYL-Vwm{jl;oNdR`t;)SKt6nLB*SkmgHtfs z-j7sfdO*p_9s*W+}|o zigoSPW+|wSQ(xWl%Y%?{1ru+D3mz|ATxVZMDEfPRltpoGuV)FUw_l59^*(p$3(D+% zne5Q0;^t6a)xtIDg4;$F#9eFeCgv3T-sIcvJ+w`V)?fD+Mhp;lihDaEDp^Q4^!`!ulPXv1!W^ za916FySKm4hhmvRZFTsqK@u>sf@r^t@beIy8Fh(Ae^4_rv(Q?b@ z%Hvb2E2c}r%l0uyWSy-PH~~ThQBKZh{Z5sJ&k5~BY;L1eAfQ$j8eCOE z1+<5sBz!ZHarm~sr2#%BcXrdHAft#kj#9doVN~v{OHRSrJeO2T8C?e%f=B}b?Ltaf z%Am;av6(znjuXNQnJ()ds;F0(Xz~a1hP;6EIEXg{;3ake?=R-S`^!z-)Gnt+J#kPL zO`f`sU6jL3CeBfF1z1UJJl?Czi7cA6cM=2ia~A;5-CLN%0eEg-;EkbBME)l5#z2_I z8+o*`Zf;Xv_F@D){6tCS4qfF#O{q^noZhlcjRD63r7E0+DFEj`7)gBQVF*l(m*_jb zba$n%a`$k)PE1fE+$HOqw(8+N&%E~2g0JU<(sc7)?Mb#02JReuSy;VUYw{%F1`)+V z<(a9b{TwZYeD}n$$fdFPTb1({&A;i0J$Db-`zoCYpFiqan2);@${&AxJz(#CyW|t$ z7Ky9`_q+6ruP5qE2&xjuF$%~a9{b>kNrGDO2(p9^Y*@sX6qmU@+Dr3k=Rp;l;UREDDufn&av?|Dkzg`P(UA#k6RW0k$`3kA* zHcLlSdQ-a6_X07?lSt{Ubn9%ZFDwnnt=8d-#QxDQExy#@EACQnU6imxIvS+GLLC!iTG|mE*QC_&Ltj0DgOvg<$faB# zz?!H^*fP!@Ax!it@4E+dN~yl(2OjI?dh8UYz^*kq$9NNHuT8wBd%^K_{$a&OvTO_G zNgW#8Xt(_IQ98Z%u=jE*O-B~}Ne4xKq3ogFVhLflnze*R5}#NKasWu=lU?J3r@)?z ze3Tk31(jDwK;-iZw+jpJS=n}fenb>T0bOgBIl)1B_!&%!Lntit1V4-dwnoHx0@ZE2 zD1JGKU+KL`o<@Ixn>aDL{m3$&EADH)&71r8+(X#tDlSm6Jc+gH_A|+X-*TTMSJvoX zzWVNpi|XPGxX6_CqPW137XyghZj>`uT6%GY)hA)*GL_9Z*_Lr}Ph^JISzE1y17sA( zwK1rLc4>$5SO+DUptN5WF&q765X4bHB zGx7(WvI$j^tvB8SeG+94iu$tTx~-wsEn69MrN2(U6`CvPXe)VDv1-2|-F}!N{_fU& zhWLYnXt!72w&yNg|FU;gGSGi#>C4^gU%ZaqEbY|Y)Yr78DOZ_O@AdIlc=b3*(k;5w zhfLwthg_3puCLb3FAKtGB;8Du@+k^FR~oeEFG|+)(7mb~8k&i{?HG798d|=1p{UwI zJM6|QyogmB;m7T-FQfQ&NXFDPFeN>^q>JpIMKxtwviRuWJ%7~uAT2diz*QYy*=w-p zQT8yUEK*_d&KsSq?!^YXJ0^x@hnd=Zrs{&^^TgQcYUHYeTmJ)-9SoV?^>xE||D~ zG!=7mXtHF?IvSfS>S<(PU%KC+g#pak~?3z_~ZF)cve_(cJE$4Sj3g{5dCn-=(q9F?MC47 zyPZ1?nhZCN-pAj4b^Q6;lIC5A{;j|V=GXNGD?_d|gsYh!f6}PeQ(nwsC+4&BOo6u>8}r7OBu#wsJ`?2CCZpK~8g2P_X4-dwH&TO2wj<}X zcJL{;J#e>ujy4c?L3P}>$6eoxpEjNVR!wy^xgj3kDlIpf0X8S ziFZxl#m$W?Kg%PQx42#7i<-`F3F9tk!$=s?hSvN0GiQcu zdYBlS%X{foHu)Fg6mKh=YjW@lI`Ir(zj#|>a?2}ED|O3igEjW+*o8?AGpSI7sD|zQ z@x~Q-l@vX!R%R&+@7nRQH)O*RL95EOPwu~bn*G|^C;R$t_6Tu9^xTc>U&CdOK3-s1 z!QXqM6vFmCk^9ifm5(tNX;Xg7{rX|I1iUKVA$R}f#(1*Rt3o>)+e}vv#*6)LtU_0& z5;HP(r(2QIqMGW?SWPh-0vOAn{HdFRK9#E*rZljRHak_HkcDExEv=VaXj4Kx9p{RKlW`TV~2ffN81JpN<{j1kCX0D3UloyRZ5cHUu6c_0m*D^Q$5<2Km`;~o?zZq)I z5Jo9Xa0*v`UH|&A&?5}_gR#c-k+ebxWZ$bjBUsSE`l73Mrsvyac!2-2GbY`S2&st~ z@~6X6u-!tc*}W~y%(1CtsA+AMXGG6MtJqOL&15T~7-hNgN-kGaT_yWW?byuguvP4j zYjhi@PvHE0 z^+O{6n~iX|fG$L97=cA@7tRKHqmT;X3^$dMh)C(frRc+~=~wFy;<47zVM)bIK*A6= z$IjB;!*s`!yMbqL9s}h>*?I~k=+({NBnpT8fu4ycs=8uBiDmaC!uZa%exAzNE8u8K ze|2Mqopmkh;uN*~<xLYG7^OD z=j8Ekmo6KK4;7${)V7|I479Lt8;8BY9>bzg+$NXZh)khifCccsg#|RgM7KqzGTb#E zxtg}yw!sNz|VZQ9)+p#hvCYHI}@E zS~%})5<>Y=(lWhs+W>Linaa`tCck~7C-brIAZo75s}Y!-QL6Ch(|L_v90{IGG%bu zg1Jvf--YM4lB95kmNfmrg6B!Z-SS9rF*4Xo*0UmzW<2kgcGHnh7#C`oQ3DF8W0T|` zM!9f$b?WEWLdadpJ_GX$L@r;ZDKvbptt+okM69MhXtiCUO6oMk6W4B3g$5?9JLj_J zws73N=`3+MLN1NN{&nQDXB@+%8;zUxi+YR>W^?yltcw*?y2hT8sH-Ou9Lf>e*9&Y> zf4edC_{rp#Ga8J#WT!9;y=CKfh6xD&H^aoWMC0TbiQfr_@o%&2GP~0&l=0fVpRd5q z?q(afOcn(m^gk2fwNZq;O(8;;1(&oG zw2o_)hvH?ec@>G{vk8A`1BH5Cu-(vCVvj6kid%=raprJcyX#BMv)PR_VNVoa1}~sc zkv)ZXlY1pydXqsSb|W}E1)uO2)7c4MEyk&7SIOlO>Z-J3jRj%JmH zJk9{%zli4OSXPPp*|aUjdNVJ+Y9TtsT_RXE zmt65c=}7K@OllZ@g2r|(QCpz!wOggAQ^ilmZt)<a{A}-RGrUkQGh?QJWDpQ-`hQY)>!-|g^@yo*!v=>aY5T7C#&{F;}NmgS=TQLcD8JxB=I&fvot{w;CEOLX?T*6 zND7$IAoeCn4~enGWXT2X=n(GY2|}+t{f^T9*EMe@d<12AcV8C=?tFI8PqPRcQT=G> zdnrsJy(GhDD^bROL0y!opB_{I6XoTuyVkMp8vI$Z54HWZXa9h%^31@IY1SzmJSFlL z{u8ep`;4p=On`H&D@_OjO>43x0e&U+EG)ocm2@iD_-jH zf(LKUEbbY1_8Zf~x>>1XR~>IF>v7H&koz5z+Os7T`;AyfyX2drv?I8JG_$s_DM-(qYU3UCE|`=%o^gLTGp(P2>YOPu*=y_FtuED zMkK8qT^8o;zSk<)7_2kYJ*UufN*F5y{{!FFFH9Z;A;2BZ(K%uc?Pl7)axvQXSRXQi zM_=ZDk5TAm)3fpgi{2(LiuXE8FP>+S6BhfbsHZj&`H$B=8GqvNy7}E3)fbVH14HMs z&otA~&YeQqDN&xl9|YK^vMTkgKHoVt9{hf+kyLjOQ_iy#Jd8$r682Njo)#Y=g#Mc) z^0!qzaKHp|`#B-ZK}XACNh-F`#$zPOpbgR|t@vxG3Ud)T&nl%KB(P_9z@~ofs<6K3 z1zbvbj`SYAx7Ao>qBG9&k;p5{g;i5xzv=tZM zFJ4Wb@aei_l}P1@89mEcyz0bfJV`6^Dhb}TT=XS;u+bu|`KH?YL&=Gu&)=}=Iqg25 z!pdJGr|-LKe{PfI*YURUI@xcl;O5~1=9l@t7nK(ZfkDB%0!TO*DhSvPC=3oqZ$tg@ zss|Sa{pYO85=;Q113@D6x>sO=NUR!RIP--CM>1XPcb&>Or&0;p-zqTQ=zNQoP2Jm?b=jW?>OR|bt;L-(X#9l*uxuf}PS zW@-_9n8O0!nrl@Em8ctP<-@L5ZoX=*a+qbq`2+_E`c$k#*Sl@C(Xa3J@~G==ubsAY zLdwUHHPYVMdlXjmZZ^3l59_}Oo~a2u#=Jy(&E^zQoECLJ|G?UaYc79e+?d{KPauEsx?tWF&I(dndCR>AVsNNbMl?3tw@yL9%km^CwlY?hk`@ z0wdp0?$5nQt&tT-a}d5x1m`NfKsTm}%yj3XXu3Ds4*yKz;)Sh$i@w?fJX~NgSr7K; zIzvPJEQ2DNiC9G)@3_Jv{;qlS1&Q_@S<2gqq;C+%hF^C*Nh)>X*>AhLJy|r*Dj~%t zyO@~S!fa+(%|}k=h>%tvYBlB!YHl$9cA=KJiCF4WWXF|ER9x;;C-=f3{uOqem#47k zl(6DYT$FrFGkQW#?pl!r&ULm=657daubz^eWp#bTJ|}KUN0`Cs3U8Oi%OU0oH@g!2 zAr(Sos;Cv0y^(ME;>kKr?8hS1-upAjIU5fa!{1?I$(*YxTGQ z()-d3LO@#IUTmhvj~09Q^@eOOVPcwt>SFG0vPo*M?LM=xTTj!9>@AHH$6eRqfy(M{ z`NUX0$6C5)%UP>c`b|e&s|A$#r`m;L)12z!R!gph8s+p;)6@$bjtWew_L1y>`~+zp6-$6x@q#Z`pwEj=w}+;7SWqA z$_M81v&PWPZ*q`o+E0XT@@%-Fk<}$rPlhbQ`}aYULS1_#Y9WH}P8sMa;T13!`QKiJ z7p3aa2_~NL1ex7~AK)*Gt$8*1K0dBbOp;e1wfF#C36E3>FakH^PjocI_Dh8=RF?IJ z4JplZi$wG#L_bR84Z9}CvkdJJ>j-&YwnI;U_@KY12LCz#)YUWGpTzzEyicjdvIY}u4h(wd0f!DazCdZaVNR~u59=2iBfe&o1d9b%{O8l zyE;GiA&D_Nv3f!WB7>Q;i(F?ohN8gbqE|(A0Qh0Ds3QOhs5?Wo+=wkut-I zaEAPnjD^OTj--KR6tz14Q8vEb^kF@KdIddHRES zn28cnFY?I}Swmc`oC_@Mtnh4%z))}#`kf#>lLGq`YEFqb;lPT)e}*0iV0r`>SR5GQ zd?Z!4tvR??S{nTXm>!{cOr-{~SmNY28dZHV&N@@rzHGv<&sr_>Dt=2fho{m=tB0bn5ZxpoT%(Isl&Q`dNl+1cBve9uhz@Loo`id792? zC{haO%9m)TAg}r$I##(0Rk-7CtZYNJR+bQzZ4sN=NoMWtTd4zA2}T^gU7+rLZB3OJ zqm1{-xs&!RgR)&Iti{80gBwF~#rd4Dv#A&uY3p|yN>bzGjU;saQTk_kUf+7~ypM7A z9?y1&GFLdGR&@IL+q|}}4M8(Vl7|mdbPD6DqVpl~pK6_{=r84&FnK3R-t6GwpIvIA z>n@yVew7#fGEFYHeAPCrgRrNJd=nL}RNSJ^_~LyAtg-=Nj{|J zzS0vS*Gg6~Kbo!Ch*QIyvDaHnVqR}-3k8EC4c8`hFx*g;bJgr15e2vFP zo8beT14{9B`hxtdbTDgTx^QQc*N6Fo;p%(eUd9MM72#SlF*dRumAJ5({G122=5P4H zYj2|KCXvAVyrb0@#N-0Dv5C*|%cq6I^D?yceH&zxNCu!?jC-Ux?X-j{pT#Jj#z^KfX$JnpUQ+>sQw^A3Yx?&HJjZ4pNUTT^s1xZPT^ryff)4g?zC%-x9I(C8lz9_3#M$ zl7&gcDLgzS8Bk~wNoYDI)Bt|*vY`eTMUcxnw(Uw%(UsluXWdCJ>)zn&E&!9*3E&Sz)t&w&S zH=jACKf=ru!<3yIDb|)fM?XAXsKZe;iz5-$;Cg1xx-K}ao^dr2Y{fy#BRxWJ#BxK)U~zc^tt71FI(z+>sY#aq78$Grx4H|C+o(;{d#vhNTN|Bi`6*xzXK0y8V!R?IgJI0?p;X=xrtH!05q3fO*vh3jqc~7c2|_Ey4U&V18@h zm^5$$-6c>0a61ASaMuaM?+oS#CcRt%>|lO(Fcjb`KY$aE1%SXCxCS1&tH2H;;FvVO zfuoI=12FLK=gj{;h<+lApNndL%la2-ok;J0hz5|lv!%T!z??r4lC-Ba`ZT#P3?K}R z0e!&>LofHpTDmKJ=L`hIZ}_taoK*)-(K}j8yV^V3qSZ>r%hp97T~N35JsA-&AOYB~ z2ibpyOJ%;K;p=;wVnP#2xhef&%aI22G4Gy?!_LJ9#L2@3&w23!Mbi~x`e zq7woJ9MeaDfCNS&kYEG?=meOn6a=(dPzWFZ0+i7?`aT>;z*hnUf%fP=g#m+(6#5QB z=O=@K3xd!&nh5lu&J50--+6)-*v|&v{q^@R%oxumk@EL&t9DAK3K=3@QXcEABsGf`Vwy{SHF{ z?EVcVjAqL3Fu{NKCk(73{<9u%h5Q`{1Oi7tIREGmf)qrXm4CtjMgWh?KVe9;m;NUV zii81C;qNdA;3a>9ArOEy{S79Fo+JJz44?&V5YWHB_y<7>3;xbih#(YgME+S%5C&NP z-(hIW{X0w$@jL$@LQu5*`e%Pa0%-H_8%z+`&-@!q7y^XIzrzqf82&p<5ZHn7PZ;1t z|1~yXaTsm((7(TU0ENJTVElI&0`M%q!;t^hA&4LlVf|JQ3PrmY^zUyRP#6$3{SHIN z3BSXH(as0``@1a+hJLVrgTaAK%fG{rKt%OBO!(hz;pn*Jw|a1NsQNoh=wGsg!iE3+ zya_EU%^VxU4WBn-sLDzB*k`ace3pHKh* diff --git a/thesis-evaluation/figures/twoQubitCreationTime.pdf b/thesis-evaluation/figures/twoQubitCreationTime.pdf deleted file mode 100644 index 342a4231413ee5a4ec0b721677dabc624b4947c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22947 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 list[QuantumCircuit]: - subcircuits = [] - - # Start with an empty circuit that has the same registers - current = QuantumCircuit(*qc.qregs, *qc.cregs) - - for instr in qc.data: - if instr.name == "barrier" or instr.name == "measure": - # Finish current subcircuit if it has content - if current.data: - subcircuits.append(current) - # Start a fresh one - current = QuantumCircuit(*qc.qregs, *qc.cregs) - else: - current.append(instr.operation, instr.qubits, instr.clbits) - - # Append the last chunk if non-empty - if current.data: - subcircuits.append(current) - - return subcircuits - - -def circuit_complexity(qc: QuantumCircuit) -> int: - num_one_qubit_gates = qc.size( - lambda instr: len(instr.qubits) == 1 and instr.name != "barrier" - ) - num_two_qubit_gates = qc.size( - lambda instr: len(instr.qubits) == 2 and instr.name != "barrier" - ) - num_other_gates = qc.size( - lambda instr: len(instr.qubits) != 1 and len(instr.qubits) != 2 - ) - - return num_one_qubit_gates + 10 * num_two_qubit_gates - - -def evaluate(): - stats = {} - - otherCX = QuantumCircuit(2) - otherCX.cx(1, 0) - otherCXGate = otherCX.to_gate() - oneQubitDec = OneQubitEulerDecomposer("ZYZ") - start_time = time.perf_counter_ns() - dec = TwoQubitBasisDecomposer(CXGate(), euler_basis="ZYZ") - dec2 = TwoQubitBasisDecomposer(otherCXGate, euler_basis="ZYZ") - end_time = time.perf_counter_ns() - creation_time_us = (end_time - start_time) / 1000 - print(f"Python Basis Decomposition Creation: {creation_time_us}µs") - - print(f"Processing '{MQT_BENCH_DIR}/{MQT_BENCH_PATTERN}'...") - for file in glob.glob(f"{MQT_BENCH_DIR}/{MQT_BENCH_PATTERN}"): - name = os.path.basename(file).removesuffix(".qasm") - print(f"Decomposing {name} ({file})") - with open(file, "r") as f: - content = f.read() - try: - if is_qasm3: - content = "\n".join( - filter(lambda line: not "measure " in line, content.splitlines()) - ) - qc: QuantumCircuit = qasm3.loads(content) - else: - qc: QuantumCircuit = qasm2.loads(content) - except Exception as e: - print(f"FAILED: {name} ({e})") - continue - - # respect barriers - start_time = time.perf_counter_ns() - subcircuits = split_by_barriers(qc) - end_time = time.perf_counter_ns() - collection_time_us = (end_time - start_time) / 1000 - - decomposition_times_1q = [] - decomposition_times_2q = [] - complexity_changes = [] - num_two_qubit_decompositions = 0 - num_single_qubit_decompositions = 0 - for subcircuit in subcircuits: - m = Operator(subcircuit) - if len(qc.qubits) == 1: - start_time = time.perf_counter_ns() - decomposed_circuit = oneQubitDec(m) - decomposed_circuit2 = None - end_time = time.perf_counter_ns() - decomposition_time = (end_time - start_time) / 1000 - decomposition_times_1q.append(decomposition_time) - - num_single_qubit_decompositions += 1 - elif len(qc.qubits) == 2: - start_time = time.perf_counter_ns() - decomposed_circuit = dec(m) - decomposed_circuit2 = dec2(m) - end_time = time.perf_counter_ns() - decomposition_time = (end_time - start_time) / 1000 - decomposition_times_2q.append(decomposition_time) - - num_two_qubit_decompositions += 1 - else: - raise RuntimeError("Invalid circuit size!") - - before_complexity = circuit_complexity(subcircuit) - after_complexity = circuit_complexity(decomposed_circuit) - - if decomposed_circuit2: - after_complexity2 = circuit_complexity(decomposed_circuit2) - if after_complexity2 < after_complexity: - print( - f"Choose alternative decomposition ({after_complexity2} vs {after_complexity})!" - ) - decomposed_circuit = decomposed_circuit2 - after_complexity = after_complexity2 - - print(subcircuit) - print(f"{before_complexity} -> {after_complexity}") - print(decomposed_circuit) - - print(Operator(subcircuit).data) - print("vs") - print(Operator(decomposed_circuit).data) - - complexity_changes.append(before_complexity - after_complexity) - - stats[name] = { - "timeInSingleQubitDecomposition": sum(decomposition_times_1q), - "timeInTwoQubitDecomposition": sum(decomposition_times_2q), - "subCircuitComplexityChange": sum(complexity_changes), - "totalTwoQubitDecompositions": num_two_qubit_decompositions, - "totalSingleQubitDecompositions": num_single_qubit_decompositions, - "twoQubitCreationTime": creation_time_us, - "timeInCircuitCollection": collection_time_us, - } - - print() - print(stats) - print(f"Total benchmarks: {len(stats)}") - return stats - - -if __name__ == "__main__": - evaluate() diff --git a/thesis-evaluation/run.sh b/thesis-evaluation/run.sh deleted file mode 100755 index 43f070b123..0000000000 --- a/thesis-evaluation/run.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh - -OUTPUT_DIR="tmp-output" - -MQT_CORE_ROOT_DIR=.. -MQT_BENCH_BENCHMARK_DIR="../../mqt-bench/generated_benchmarks/v3_qasm3" -MQT_BENCH_PATTERN="*.qasm" - -mkdir -p "${OUTPUT_DIR}/broken" - -benchmark_count="$(eza -1 ${MQT_BENCH_BENCHMARK_DIR}/${MQT_BENCH_PATTERN} | wc -l)" -i=0 -success=0 - -for benchmark_path in ${MQT_BENCH_BENCHMARK_DIR}/${MQT_BENCH_PATTERN}; do - i=$((i + 1)) - benchmark="$(basename "${benchmark_path}")" - echo "${i}/${benchmark_count}: ${benchmark}" - result="$("${MQT_CORE_ROOT_DIR}/build/mlir/tools/mqt-cc/mqt-cc" --mlir-timing --mlir-statistics "${benchmark_path}" 2>&1)" - if [ "${?}" -eq 0 ]; then - echo "${result}" | rg 'Total Execution Time' -A 8 >"${OUTPUT_DIR}/${benchmark}.timing" - echo "${result}" | rg '\(S\) ' >"${OUTPUT_DIR}/${benchmark}.statistic" - echo "${result}" | rg 'module \{' -A 99999999999999 >"${OUTPUT_DIR}/${benchmark}.mlir" - echo "${result}" >"${OUTPUT_DIR}/${benchmark}.all" - echo "SUCCESS" - success=$((success + 1)) - else - echo "${result}" >"${OUTPUT_DIR}/broken/${benchmark}.error" - echo "FAILED" - fi -done - -echo "COMPLETED: ${success}/${i}" diff --git a/thesis-evaluation/total_evaluate.py b/thesis-evaluation/total_evaluate.py deleted file mode 100644 index 4b9de3dbcf..0000000000 --- a/thesis-evaluation/total_evaluate.py +++ /dev/null @@ -1,274 +0,0 @@ -import evaluate -import qiskit_run - -import matplotlib.pyplot as plt -from matplotlib.ticker import MaxNLocator -import math -import json -import os -import numpy as np -import subprocess - -OUT_DIR = "./figures" -if not os.path.exists(OUT_DIR): - os.makedirs(OUT_DIR, exist_ok=True) - -CACHE_FILE = "./evaluate_cache.json" -if os.path.exists(CACHE_FILE): - with open(CACHE_FILE, "r") as f: - cache = json.load(f) - mqt_results = cache["mqt"] - qiskit_results = cache["qiskit"] -else: - ITERATIONS = 20 - all_mqt_results = [] - all_qiskit_results = [] - for _ in range(ITERATIONS): - # evaluate.evalue() will only process statistics files; need to re-run mqt-cc using run.sh - subprocess.run(["./run.sh"]).check_returncode() - all_mqt_results.append(evaluate.evaluate()) - all_qiskit_results.append(qiskit_run.evaluate()) - - def average_results( - all_results: list[dict[str, dict[str, int | float]]], - ) -> dict[str, dict[str, int | float]]: - result = {} - for r in all_results: - for benchmark_name, measurements in r.items(): - if not benchmark_name in result: - result[benchmark_name] = {} - for metric_name, value in measurements.items(): - if not metric_name in result[benchmark_name]: - result[benchmark_name][metric_name] = 0.0 - result[benchmark_name][metric_name] += value / ITERATIONS - return result - - # [benchmark_name -> [metric_name -> value]] - mqt_results = average_results(all_mqt_results) - qiskit_results = average_results(all_qiskit_results) - - with open(CACHE_FILE, "w") as f: - cache = { - "mqt": mqt_results, - "qiskit": qiskit_results, - } - json.dump(cache, f) - -print("In MQT, but not Qiskit: ", mqt_results.keys() - qiskit_results.keys()) -print("In Qiskit, but not MQT: ", qiskit_results.keys() - mqt_results.keys()) - - -x: dict[str, list[str]] = {} -y1: dict[str, list[int | float]] = {} -y2: dict[str, list[int | float]] = {} - - -def define_division_metric( - new_metric: str, old_metric: str, divisor_metric: str, benchmark_name: str -): - y1[new_metric] = y1.get(new_metric, []) + [ - m[old_metric] / m[divisor_metric] if m[divisor_metric] > 0 else float("nan") - ] - y2[new_metric] = y2.get(new_metric, []) + [ - q[old_metric] / q[divisor_metric] if q[divisor_metric] > 0 else float("nan") - ] - x[new_metric] = x.get(new_metric, []) + [benchmark_name] - - -aliases_qiskit = { - "totalSingleQubitDecompositions": "successfulSingleQubitDecompositions", - "totalTwoQubitDecompositions": "successfulTwoQubitDecompositions", -} -aliases_mqt = { - "timeInCircuitCollection": "timeInCircuitCollectionStandalone", -} -names = sorted(mqt_results.keys() & qiskit_results.keys()) -for name in names: - m = mqt_results[name] - q = qiskit_results[name] - - for metric in m.keys() | q.keys(): - if metric in m: - y1[metric] = y1.get(metric, []) + [m[metric]] - if metric in aliases_mqt: - y1[aliases_mqt[metric]] = y1.get(aliases_mqt[metric], []) + [m[metric]] - if metric in q: - y2[metric] = y2.get(metric, []) + [q[metric]] - if metric in aliases_qiskit: - y2[aliases_qiskit[metric]] = y2.get(aliases_qiskit[metric], []) + [q[metric]] - - define_division_metric( - "timePerSingleQubitDecomposition", - "timeInSingleQubitDecomposition", - "totalSingleQubitDecompositions", - name, - ) - define_division_metric( - "timePerTwoQubitDecomposition", - "timeInTwoQubitDecomposition", - "totalTwoQubitDecompositions", - name, - ) - - for metric in y1.keys() | y2.keys(): - x[metric] = names - -titles = { - "subCircuitComplexityChange": "Complexity Improvement after Decomposition", - "successfulSingleQubitDecompositions": "Number of Successful Single-Qubit Decompositions", - "successfulTwoQubitDecompositions": "Number of Successful Two-Qubit Decompositions", - "timeInCircuitCollection": "Sub-Circuit Collection Time [µs]", - "timeInCircuitCollectionStandalone": "Sub-Circuit Collection Time [µs]", - "timeInSingleQubitDecomposition": "Total Time for Single-Qubit Decompositions [µs]", - "timeInTwoQubitDecomposition": "Total Time for Two-Qubit Decompositions [µs]", - "totalCircuitCollections": "Number of Sub-Circuit Collections", - "totalSingleQubitDecompositions": "Total Number of Single-Qubit Decompositions", - "totalTouchedGates": "Total Number of Gates in Collected Sub-Circuits", - "totalTwoQubitDecompositions": "Total Number of Two-Qubit Decompositions", - "timePerTwoQubitDecomposition": "Time / Two-Qubit Decomposition [µs]", - "timePerSingleQubitDecomposition": "Time / Single-Qubit Decomposition [µs]", - "twoQubitCreationTime": "Time for Creation of Two-Qubit Basis Decomposers [µs]", -} -legend_positions = { - "totalTouchedGates": "upper left", - "timeInCircuitCollection": "upper right", - "timeInCircuitCollectionStandalone": "upper left", - "timePerSingleQubitDecomposition": "upper left", - "timePerTwoQubitDecomposition": "lower right", - "totalTouchedGates": "upper left", - "twoQubitCreationTime": "lower right", -} -pruneFunctions = { - "timeInCircuitCollection": lambda value: np.isclose(value, 0), - "timeInCircuitCollectionStandalone": lambda value: np.isclose(value, 0), - "timeInSingleQubitDecomposition": lambda value: np.isclose(value, 0), - "timeInTwoQubitDecomposition": lambda value: np.isclose(value, 0), - "timePerTwoQubitDecomposition": lambda value: np.isclose(value, 0), - "timePerSingleQubitDecomposition": lambda value: np.isclose(value, 0), - "twoQubitCreationTime": lambda value: np.isclose(value, 0), -} -# modifications = { -# "subCircuitComplexityChange": lambda value: -1.0 * value, -# } -for metric in x.keys(): - plt.title(titles.get(metric, metric)) - # x_values = x[metric] # use for benchmark names on x axis - x_values = [str(i) for i in range(len(x[metric]))] - - # if metric in modifications: - # for y in y1[metric]: - # y = modifications[metric](y) - # for y in y2[metric]: - # y = modifications[metric](y) - - DEFAULT_POINT_SIZE = 100 - scale1 = [] - scale2 = [] - num_erased_y = 0 - for i in range(len(y1[metric])): - if metric in y1: - y1i = y1[metric][i - num_erased_y] - else: - y1i = None - if metric in y2: - y2i = y2[metric][i - num_erased_y] - else: - y2i = None - if ( - (y1i == None and y2i == None) - or (y1i != None and y2i != None and math.isnan(y1i) and math.isnan(y2i)) - or ( - y1i != None - and y2i != None - and pruneFunctions.get(metric, lambda _: False)(y1i) - and pruneFunctions.get(metric, lambda _: False)(y2i) - ) - ): - x_values.pop(i - num_erased_y) - if metric in y1: - y1[metric].pop(i - num_erased_y) - if metric in y2: - y2[metric].pop(i - num_erased_y) - num_erased_y += 1 - elif ( - y1i == None - or math.isnan(y1i) - or pruneFunctions.get(metric, lambda _: False)(y1i) - ): - scale1.append(0) - scale2.append(DEFAULT_POINT_SIZE) - elif ( - y2i == None - or math.isnan(y2i) - or pruneFunctions.get(metric, lambda _: False)(y2i) - ): - scale1.append(DEFAULT_POINT_SIZE) - scale2.append(0) - else: - scale1.append(DEFAULT_POINT_SIZE) - scale2.append(DEFAULT_POINT_SIZE) - - ymin = float("inf") - if metric in y1: - mqt_scatter = plt.scatter( - x_values, y1[metric], color="blue", s=scale1, alpha=0.4 - ) - mqt_scatter.set_label("MQT") - ymin = min(ymin, min(y1[metric])) - if metric in y2: - qiskit_scatter = plt.scatter( - x_values, y2[metric], color="red", s=scale2, alpha=0.4 - ) - qiskit_scatter.set_label("Qiskit") - ymin = min(ymin, min(y2[metric])) - - # plt.xticks(x_values) # does not work for strings - if ymin > 0: - # let matplotlib handle non-positive values automatically - plt.ylim(bottom=0) - - yint = [] - locs, labels = plt.yticks() - for each in locs: - yint.append(int(each)) - plt.yticks(yint) - - leg = plt.legend(loc=legend_positions.get(metric, "best")) - for handle in leg.legend_handles: - handle.set_sizes([DEFAULT_POINT_SIZE]) - plt.savefig( - f"{OUT_DIR}/{metric}.pdf", format="pdf", bbox_inches="tight", pad_inches=0 - ) - # plt.show() - plt.clf() - -for i, name in enumerate(names): - if "_indep_1_none_O0" in name: - print( - i, " & \\code{", name.removesuffix("_indep_1_none_O0"), "} & 1\\\\", sep="" - ) - elif "_indep_2_none_O0" in name: - print( - i, " & \\code{", name.removesuffix("_indep_2_none_O0"), "} & 2\\\\", sep="" - ) - else: - print("UNKNOWN") - -for metric in x.keys(): - print() - print( - f"Average MQT {metric}:", - np.average(np.ma.masked_invalid(y1[metric])) if metric in y1 else "-", - ) - print( - f"Average Qiskit {metric}:", - np.average(np.ma.masked_invalid(y2[metric])) if metric in y2 else "-", - ) - print( - f"Median MQT {metric}:", - np.median(np.ma.masked_invalid(y1[metric])) if metric in y1 else "-", - ) - print( - f"Median Qiskit {metric}:", - np.median(np.ma.masked_invalid(y2[metric])) if metric in y2 else "-", - ) From 595edb9ddfba961fc678810e72b91ce9a5c6185d Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 4 Feb 2026 12:49:04 +0100 Subject: [PATCH 183/237] :memo: Add PR to CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cfb73e2bcb..a9ccf77601 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Added -- ✨ Add initial infrastructure for new QC and QCO MLIR dialects ([#1264], [#1402], [#1426], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**]) +- ✨ Add initial infrastructure for new QC and QCO MLIR dialects ([#1264], [#1206], [#1402], [#1426], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**]) ### Changed @@ -395,6 +395,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool [#1210]: https://github.com/munich-quantum-toolkit/core/pull/1210 [#1209]: https://github.com/munich-quantum-toolkit/core/pull/1209 [#1207]: https://github.com/munich-quantum-toolkit/core/pull/1207 +[#1206]: https://github.com/munich-quantum-toolkit/core/pull/1206 [#1186]: https://github.com/munich-quantum-toolkit/core/pull/1186 [#1181]: https://github.com/munich-quantum-toolkit/core/pull/1181 [#1180]: https://github.com/munich-quantum-toolkit/core/pull/1180 From b506244f8a1e8fa9a25a9731e802bcf1eee68e44 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 4 Feb 2026 12:50:47 +0100 Subject: [PATCH 184/237] fix typo in comment --- mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 26dd3ebe4b..006ec98156 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -93,10 +93,10 @@ struct GateDecompositionPattern final if (op->getParentOfType()) { // application of pattern might not work on gates inside a control // modifier because rotation gates need to create new constants which are - // not allowed inside a control body; also, the foreign gate dection does - // not work and e.g. a CNOT will not be recognized as such and thus will - // be further decomposed into a RX gate inside the control body which is - // most likely undesired + // not allowed inside a control body; also, the foreign gate detection + // does not work and e.g. a CNOT will not be recognized as such and thus + // will be further decomposed into a RX gate inside the control body which + // is most likely undesired return mlir::failure(); } From a418cc1458c95f7667d970f6317a15fad26f2915 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 4 Feb 2026 12:58:40 +0100 Subject: [PATCH 185/237] improve naming in getDefaultNbasis --- .../Passes/Decomposition/BasisDecomposer.h | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index b6500d9365..5a664d5848 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -227,19 +227,22 @@ class TwoQubitBasisDecomposer { fp actualBasisFidelity = getBasisFidelity(); auto traces = this->traces(targetDecomposition); auto getDefaultNbasis = [&]() { - auto minValue = std::numeric_limits::min(); - auto minIndex = -1; + // determine smallest number of basis gates required to fulfill given + // basis fidelity constraint + auto bestValue = std::numeric_limits::min(); + auto bestIndex = -1; for (int i = 0; std::cmp_less(i, traces.size()); ++i) { - // lower fidelity means it becomes easier to choose a lower number of - // basis gates + // lower basis fidelity means it becomes easier to use fewer basis gates + // through a rougher approximation auto value = helpers::traceToFidelity(traces[i]) * std::pow(actualBasisFidelity, i); - if (value > minValue) { - minIndex = i; - minValue = value; + if (value > bestValue) { + bestIndex = i; + bestValue = value; } } - return minIndex; + // index in traces equals number of basis gates + return bestIndex; }; // number of basis gates that need to be used in the decomposition auto bestNbasis = numBasisGateUses.value_or(getDefaultNbasis()); From 4b1a6075e334fd1061476d660053f6593bf9dd72 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 5 Feb 2026 20:01:06 +0100 Subject: [PATCH 186/237] coderabbit feedback and cleanup --- .../Passes/Decomposition/BasisDecomposer.h | 14 +-- .../mlir/Passes/Decomposition/EulerBasis.h | 5 +- .../Passes/Decomposition/EulerDecomposition.h | 13 +- .../mlir/Passes/Decomposition/Helpers.h | 111 +----------------- .../Passes/Decomposition/UnitaryMatrices.h | 5 +- .../Passes/Decomposition/WeylDecomposition.h | 11 +- mlir/lib/Compiler/CompilerPipeline.cpp | 1 - .../Patterns/GateDecompositionPattern.cpp | 12 +- 8 files changed, 31 insertions(+), 141 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index 5a664d5848..2047aa1e0a 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -414,10 +414,9 @@ class TwoQubitBasisDecomposer { [[nodiscard]] llvm::SmallVector decomp2Supercontrolled( const decomposition::TwoQubitWeylDecomposition& target) const { if (!isSuperControlled) { - // TODO: make fatal error? check in constructor? - llvm::errs() - << "Basis gate of TwoQubitBasisDecomposer is not super-controlled " - "- no guarantee for exact decomposition with two basis gates\n"; + llvm::reportFatalInternalError( + "Basis gate of TwoQubitBasisDecomposer is not super-controlled " + "- no guarantee for exact decomposition with two basis gates"); } return { q2r * target.k2r, @@ -441,10 +440,9 @@ class TwoQubitBasisDecomposer { [[nodiscard]] llvm::SmallVector decomp3Supercontrolled( const decomposition::TwoQubitWeylDecomposition& target) const { if (!isSuperControlled) { - llvm::errs() - << "Basis gate of TwoQubitBasisDecomposer is not super-controlled " - "- no guarantee for exact decomposition with " - "three basis gates\n"; + llvm::reportFatalInternalError( + "Basis gate of TwoQubitBasisDecomposer is not super-controlled " + "- no guarantee for exact decomposition with three basis gates"); } return { u3r * target.k2r, diff --git a/mlir/include/mlir/Passes/Decomposition/EulerBasis.h b/mlir/include/mlir/Passes/Decomposition/EulerBasis.h index 63f3aa9c3a..8ed5135029 100644 --- a/mlir/include/mlir/Passes/Decomposition/EulerBasis.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerBasis.h @@ -14,6 +14,7 @@ #include #include +#include namespace mlir::qco::decomposition { /** @@ -63,11 +64,11 @@ getGateTypesForEulerBasis(EulerBasis eulerBasis) { case EulerBasis::ZSXX: [[fallthrough]]; case EulerBasis::ZSX: - return {qc::RZ, qc::X}; + return {qc::RZ, qc::SX}; case EulerBasis::PSX: [[fallthrough]]; case EulerBasis::U1X: - break; + return {qc::RZ, qc::P}; } throw std::invalid_argument{ "Unsupported euler basis for translation to gate types"}; diff --git a/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h index 8db67d6571..12ffc0b984 100644 --- a/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h @@ -22,15 +22,8 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include namespace mlir::qco::decomposition { @@ -64,7 +57,6 @@ class EulerDecomposition { return decomposeKAK(theta, phi, lambda, phase, qc::RX, qc::RY, simplify, atol); default: - // TODO: allow other bases throw std::invalid_argument{"Unsupported base for circuit generation!"}; } } @@ -168,6 +160,9 @@ class EulerDecomposition { qc::OpType aGate, bool simplify, std::optional atol) { fp angleZeroEpsilon = atol.value_or(DEFAULT_ATOL); if (!simplify) { + // setting atol to negative value to make all angle checks true; this will + // effectively disable the simplification since all rotations appear to be + // "necessary" angleZeroEpsilon = -1.0; } diff --git a/mlir/include/mlir/Passes/Decomposition/Helpers.h b/mlir/include/mlir/Passes/Decomposition/Helpers.h index eecea033da..23aa144a96 100644 --- a/mlir/include/mlir/Passes/Decomposition/Helpers.h +++ b/mlir/include/mlir/Passes/Decomposition/Helpers.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -27,7 +28,7 @@ #include #include #include -#include // TODO: unstable, NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) namespace mlir::qco { using fp = qc::fp; @@ -50,101 +51,6 @@ constexpr qfp M_IM{0., -1.}; namespace mlir::qco::helpers { -std::optional mlirValueToFp(mlir::Value value); - -template -[[nodiscard]] std::optional performMlirFloatBinaryOp(mlir::Value value, - Func&& func) { - if (auto op = value.getDefiningOp()) { - auto lhs = mlirValueToFp(op.getLhs()); - auto rhs = mlirValueToFp(op.getRhs()); - if (lhs && rhs) { - return std::invoke(std::forward(func), *lhs, *rhs); - } - } - return std::nullopt; -} - -template -[[nodiscard]] std::optional performMlirFloatUnaryOp(mlir::Value value, - Func&& func) { - if (auto op = value.getDefiningOp()) { - if (auto operand = mlirValueToFp(op.getOperand())) { - return std::invoke(std::forward(func), *operand); - } - } - return std::nullopt; -} - -[[nodiscard]] inline std::optional mlirValueToFp(mlir::Value value) { - if (auto op = value.getDefiningOp()) { - if (auto attr = llvm::dyn_cast(op.getValue())) { - return attr.getValueAsDouble(); - } - return std::nullopt; - } - if (auto result = performMlirFloatUnaryOp( - value, [](fp a) { return -a; })) { - return result; - } - if (auto result = performMlirFloatUnaryOp( - value, [](fp a) { return a; })) { - return result; - } - if (auto result = performMlirFloatUnaryOp( - value, [](fp a) { return a; })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return std::max(a, b); })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return std::max(a, b); })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return std::min(a, b); })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return std::min(a, b); })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return std::fmod(a, b); })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return a + b; })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return a * b; })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return a / b; })) { - return result; - } - if (auto result = performMlirFloatBinaryOp( - value, [](fp a, fp b) { return a - b; })) { - return result; - } - return std::nullopt; -} - -[[nodiscard]] inline llvm::SmallVector -getParameters(UnitaryOpInterface op) { - llvm::SmallVector parameters; - for (std::size_t i = 0; i < op.getNumParams(); ++i) { - if (auto value = helpers::mlirValueToFp(op.getParameter(i))) { - parameters.push_back(*value); - } - } - return parameters; -} - [[nodiscard]] inline qc::OpType getQcType(UnitaryOpInterface op) { try { auto type = op.getBaseSymbol(); @@ -157,14 +63,6 @@ getParameters(UnitaryOpInterface op) { } } -[[nodiscard]] inline bool isSingleQubitOperation(UnitaryOpInterface op) { - return op.isSingleQubit(); -} - -[[nodiscard]] inline bool isTwoQubitOperation(UnitaryOpInterface op) { - return op.isTwoQubit(); -} - // NOLINTBEGIN(misc-include-cleaner) template [[nodiscard]] inline Eigen::Matrix4 @@ -182,8 +80,9 @@ template } template -[[nodiscard]] bool isUnitaryMatrix(const Eigen::Matrix& matrix) { - return (matrix.transpose().conjugate() * matrix).isIdentity(); +[[nodiscard]] bool isUnitaryMatrix(const Eigen::Matrix& matrix, + fp tolerance = 1e-13) { + return (matrix.transpose().conjugate() * matrix).isIdentity(tolerance); } // NOLINTEND(misc-include-cleaner) diff --git a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index 2d829f4706..4004fdab1b 100644 --- a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -165,7 +165,7 @@ inline matrix2x2 getSingleQubitMatrix(const Gate& gate) { qc::toString(gate.type) + ")"}; } -// TODO: remove? only used for verification of circuit +// TODO: remove? only used for verification of circuit and in unittests inline matrix4x4 getTwoQubitMatrix(const Gate& gate) { using helpers::kroneckerProduct; @@ -188,15 +188,12 @@ inline matrix4x4 getTwoQubitMatrix(const Gate& gate) { } } if (gate.type == qc::RXX) { - // TODO: check qubit order? return rxxMatrix(gate.parameter[0]); } if (gate.type == qc::RYY) { - // TODO: check qubit order? return ryyMatrix(gate.parameter[0]); } if (gate.type == qc::RZZ) { - // TODO: check qubit order? return rzzMatrix(gate.parameter[0]); } if (gate.type == qc::I) { diff --git a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index 5350868015..bdd5acb7b4 100644 --- a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -36,7 +36,6 @@ #include #include #include -#include // TODO: unstable, NOLINT(misc-include-cleaner) #include namespace mlir::qco::decomposition { @@ -172,7 +171,9 @@ struct TwoQubitWeylDecomposition { // verification matrix4x4 temp = dReal.asDiagonal(); temp *= IM; - temp = temp.exp(); + // since the matrix is diagonal, matrix exponental is equivalent to + // element-wise exponential function + temp.diagonal() = temp.diagonal().array().exp().matrix(); // combined matrix k1 of 1q gates after canonical gate matrix4x4 k1 = uP * p * temp; @@ -616,9 +617,6 @@ struct TwoQubitWeylDecomposition { // This gate binds 0 parameters, we make it canonical by setting: // // :math:`K2_l = Id` , :math:`K2_r = Id`. - a = qc::PI_4; - b = qc::PI_4; - c = qc::PI_4; if (c > 0.) { // unmodified global phase k1l = k1l * k2r; @@ -634,6 +632,9 @@ struct TwoQubitWeylDecomposition { k2l = IDENTITY_GATE; k2r = IDENTITY_GATE; } + a = qc::PI_4; + b = qc::PI_4; + c = qc::PI_4; } else if (newSpecialization == Specialization::PartialSWAPEquiv) { // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4)` // Thus, :math:`U \sim \text{SWAP}^\alpha` diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index 4d0edddac5..8a09fe56d3 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -167,7 +167,6 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, // Stage 5: Optimization passes addOptimizationPasses(pm); - addCleanupPasses(pm); if (failed(pm.run(module))) { return failure(); } diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 006ec98156..4c480e9e7a 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -230,7 +230,7 @@ struct GateDecompositionPattern final TwoQubitSeries result(op); while (auto user = getUser(result.outQubits[0], - &helpers::isSingleQubitOperation)) { + &UnitaryOpInterface::isSingleQubit)) { if (!result.appendSingleQubitGate(*user)) { break; } @@ -253,7 +253,7 @@ struct GateDecompositionPattern final // collect all available single-qubit operations for (std::size_t i = 0; i < result.outQubits.size(); ++i) { while (auto user = getUser(result.outQubits[i], - &helpers::isSingleQubitOperation)) { + &UnitaryOpInterface::isSingleQubit)) { foundGate = result.appendSingleQubitGate(*user); if (!foundGate) { // result.outQubits was not updated, prevent endless loop @@ -264,7 +264,7 @@ struct GateDecompositionPattern final for (std::size_t i = 0; i < result.outQubits.size(); ++i) { if (auto user = - getUser(result.outQubits[i], &helpers::isTwoQubitOperation)) { + getUser(result.outQubits[i], &UnitaryOpInterface::isTwoQubit)) { foundGate = result.appendTwoQubitGate(*user); break; // go back to single-qubit collection } @@ -341,11 +341,11 @@ struct GateDecompositionPattern final * Initialize TwoQubitSeries instance with given first operation. */ explicit TwoQubitSeries(UnitaryOpInterface initialOperation) { - if (helpers::isSingleQubitOperation(initialOperation)) { + if (initialOperation.isSingleQubit()) { inQubits = {initialOperation.getInputQubit(0), mlir::Value{}}; outQubits = {initialOperation.getOutputQubit(0), mlir::Value{}}; gates.push_back({.op = initialOperation, .qubitIds = {0}}); - } else if (helpers::isTwoQubitOperation(initialOperation)) { + } else if (initialOperation.isTwoQubit()) { inQubits = {initialOperation.getInputQubit(0), initialOperation.getInputQubit(1)}; outQubits = {initialOperation.getOutputQubit(0), @@ -470,7 +470,7 @@ struct GateDecompositionPattern final }; while (auto* op = inQubits[qubitId].getDefiningOp()) { auto unitaryOp = mlir::dyn_cast(op); - if (unitaryOp && helpers::isSingleQubitOperation(unitaryOp) && + if (unitaryOp && unitaryOp.isSingleQubit() && !isBarrier(unitaryOp)) { prependSingleQubitGate(unitaryOp); } else { From f5b2ff19868d98e780cd034b807b067556cddd8a Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 5 Feb 2026 20:01:49 +0100 Subject: [PATCH 187/237] remove broken unittest; need to think of ways to verify optimizations --- .../Compiler/test_compiler_pipeline.cpp | 53 ------------------- 1 file changed, 53 deletions(-) diff --git a/mlir/unittests/Compiler/test_compiler_pipeline.cpp b/mlir/unittests/Compiler/test_compiler_pipeline.cpp index 9f2ff99466..65b3510195 100644 --- a/mlir/unittests/Compiler/test_compiler_pipeline.cpp +++ b/mlir/unittests/Compiler/test_compiler_pipeline.cpp @@ -3736,57 +3736,4 @@ TEST_F(CompilerPipelineTest, Bell) { }); } -TEST_F(CompilerPipelineTest, TwoQubitDecomposition) { - ::qc::QuantumComputation comp; - comp.addQubitRegister(2, "q"); - - auto buildCircuit = [](auto& circuit, auto&& qubitGetter) { - auto constant0 = 2.5; - auto constant1 = 1.2; - auto constant2 = 0.5; - circuit.h(qubitGetter(0)); - circuit.cx(qubitGetter(0), qubitGetter(1)); - circuit.rzz(constant0, qubitGetter(0), qubitGetter(1)); - circuit.ry(constant1, qubitGetter(1)); - circuit.ry(constant1, qubitGetter(0)); - circuit.cx(qubitGetter(1), qubitGetter(0)); - circuit.rz(constant2, qubitGetter(0)); - circuit.rxx(constant0, qubitGetter(0), qubitGetter(1)); - circuit.ryy(constant2, qubitGetter(0), qubitGetter(1)); - // make series longer to enforce decomposition - circuit.rxx(0.1, qubitGetter(1), qubitGetter(0)); - circuit.rzz(0.1, qubitGetter(1), qubitGetter(0)); - circuit.rxx(0.1, qubitGetter(1), qubitGetter(0)); - circuit.rzz(0.1, qubitGetter(1), qubitGetter(0)); - circuit.rxx(0.1, qubitGetter(1), qubitGetter(0)); - circuit.rzz(0.1, qubitGetter(1), qubitGetter(0)); - circuit.rxx(0.1, qubitGetter(1), qubitGetter(0)); - circuit.rzz(0.1, qubitGetter(1), qubitGetter(0)); - }; - - buildCircuit(comp, [](auto&& index) { return index; }); - - const auto module = importQuantumCircuit(comp); - ASSERT_TRUE(module); - ASSERT_TRUE(runPipeline(module.get()).succeeded()); - - const auto qco = buildQCOIR([&](qco::QCOProgramBuilder& b) { - auto reg = b.allocQubitRegister(2, "q"); - buildCircuit(b, [®](auto&& index) { return reg[index]; }); - }); - - const auto optimizedQco = buildQCOIR([&](qco::QCOProgramBuilder& b) { - auto reg = b.allocQubitRegister(2, "q"); - buildCircuit(b, [®](auto&& index) { return reg[index]; }); - }); - - verifyAllStages({ - .qcImport = nullptr, - .qcoConversion = qco.get(), - .optimization = optimizedQco.get(), - .qcConversion = nullptr, - .qirConversion = nullptr, - }); -} - } // namespace From fd4f99b685b7851ade64ccdca382a3af81fee992 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 5 Feb 2026 20:38:16 +0100 Subject: [PATCH 188/237] remove helpers::kroneckerProduct, IDENTITY_GATE and fix unitary matrices --- .../Passes/Decomposition/BasisDecomposer.h | 2 +- .../mlir/Passes/Decomposition/EulerBasis.h | 2 - .../Passes/Decomposition/EulerDecomposition.h | 3 ++ .../mlir/Passes/Decomposition/GateSequence.h | 3 +- .../mlir/Passes/Decomposition/Helpers.h | 6 --- .../Passes/Decomposition/UnitaryMatrices.h | 48 +++++++++---------- .../Passes/Decomposition/WeylDecomposition.h | 35 +++++++------- .../Patterns/GateDecompositionPattern.cpp | 8 ++-- .../decomposition/test_basis_decomposer.cpp | 19 ++++---- .../test_euler_decomposition.cpp | 2 +- .../decomposition/test_weyl_decomposition.cpp | 22 +++++---- 11 files changed, 72 insertions(+), 78 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index 2047aa1e0a..7293157cc4 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -500,7 +500,7 @@ class TwoQubitBasisDecomposer { auto circuit = EulerDecomposition::generateCircuit( targetBasis, unitaryMat, simplify, atol); assert(circuit.getUnitaryMatrix().isApprox( - helpers::kroneckerProduct(IDENTITY_GATE, unitaryMat), + Eigen::kroneckerProduct(matrix2x2::Identity(), unitaryMat), SANITY_CHECK_PRECISION)); auto error = calculateError(circuit); if (error < minError) { diff --git a/mlir/include/mlir/Passes/Decomposition/EulerBasis.h b/mlir/include/mlir/Passes/Decomposition/EulerBasis.h index 8ed5135029..8b8ee93f58 100644 --- a/mlir/include/mlir/Passes/Decomposition/EulerBasis.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerBasis.h @@ -24,8 +24,6 @@ static constexpr auto DEFAULT_ATOL = 1e-12; /** * EulerBasis for a euler decomposition. - * - * @note only the following bases are supported for now: ZYZ, ZXZ and XZX */ enum class EulerBasis : std::uint8_t { U3 = 0, diff --git a/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h index 12ffc0b984..f6e9b67334 100644 --- a/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h @@ -30,6 +30,9 @@ namespace mlir::qco::decomposition { /** * Decomposition of single-qubit matrices into rotation gates using a KAK * decomposition. + * + * @note only the following euler bases are supported for now: + * ZYZ, ZXZ, XYX and XZX */ class EulerDecomposition { public: diff --git a/mlir/include/mlir/Passes/Decomposition/GateSequence.h b/mlir/include/mlir/Passes/Decomposition/GateSequence.h index f7cd6458b7..bf8aa05c4e 100644 --- a/mlir/include/mlir/Passes/Decomposition/GateSequence.h +++ b/mlir/include/mlir/Passes/Decomposition/GateSequence.h @@ -74,8 +74,7 @@ struct QubitGateSequence { * Calculate overall unitary matrix of the sequence. */ [[nodiscard]] matrix4x4 getUnitaryMatrix() const { - matrix4x4 unitaryMatrix = - helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); + matrix4x4 unitaryMatrix = matrix4x4::Identity(); for (auto&& gate : gates) { auto gateMatrix = getTwoQubitMatrix(gate); unitaryMatrix = gateMatrix * unitaryMatrix; diff --git a/mlir/include/mlir/Passes/Decomposition/Helpers.h b/mlir/include/mlir/Passes/Decomposition/Helpers.h index 23aa144a96..eda36fe998 100644 --- a/mlir/include/mlir/Passes/Decomposition/Helpers.h +++ b/mlir/include/mlir/Passes/Decomposition/Helpers.h @@ -64,12 +64,6 @@ namespace mlir::qco::helpers { } // NOLINTBEGIN(misc-include-cleaner) -template -[[nodiscard]] inline Eigen::Matrix4 -kroneckerProduct(const Eigen::Matrix2& lhs, const Eigen::Matrix2& rhs) { - return Eigen::kroneckerProduct(lhs, rhs); -} - template [[nodiscard]] inline auto selfAdjointEvd(Eigen::Matrix a) { Eigen::SelfAdjointEigenSolver s; diff --git a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index 4004fdab1b..805e5ceadd 100644 --- a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -14,6 +14,8 @@ #include "Helpers.h" #include "ir/operations/OpType.hpp" +#include + namespace mlir::qco::decomposition { inline constexpr auto SQRT2 = @@ -21,7 +23,7 @@ inline constexpr auto SQRT2 = inline constexpr auto FRAC1_SQRT2 = static_cast( 0.707106781186547524400844362104849039284835937688474036588L); -[[nodiscard]] inline matrix2x2 uMatrix(const fp lambda, const fp phi, +[[nodiscard]] constexpr matrix2x2 uMatrix(const fp lambda, const fp phi, const fp theta) { return matrix2x2{{{{std::cos(theta / 2.), 0.}, {-std::cos(lambda) * std::sin(theta / 2.), @@ -32,7 +34,7 @@ inline constexpr auto FRAC1_SQRT2 = static_cast( std::sin(lambda + phi) * std::cos(theta / 2.)}}}}; } -[[nodiscard]] inline matrix2x2 u2Matrix(const fp lambda, const fp phi) { +[[nodiscard]] constexpr matrix2x2 u2Matrix(const fp lambda, const fp phi) { return matrix2x2{ {FRAC1_SQRT2, {-std::cos(lambda) * FRAC1_SQRT2, -std::sin(lambda) * FRAC1_SQRT2}}, @@ -41,26 +43,26 @@ inline constexpr auto FRAC1_SQRT2 = static_cast( std::sin(lambda + phi) * FRAC1_SQRT2}}}; } -inline matrix2x2 rxMatrix(fp theta) { +[[nodiscard]] constexpr matrix2x2 rxMatrix(fp theta) { auto halfTheta = theta / 2.; auto cos = qfp(std::cos(halfTheta), 0.); auto isin = qfp(0., -std::sin(halfTheta)); return matrix2x2{{cos, isin}, {isin, cos}}; } -inline matrix2x2 ryMatrix(fp theta) { +[[nodiscard]] constexpr matrix2x2 ryMatrix(fp theta) { auto halfTheta = theta / 2.; auto cos = qfp(std::cos(halfTheta), 0.); auto sin = qfp(std::sin(halfTheta), 0.); return matrix2x2{{cos, -sin}, {sin, cos}}; } -inline matrix2x2 rzMatrix(fp theta) { +[[nodiscard]] constexpr matrix2x2 rzMatrix(fp theta) { return matrix2x2{{qfp{std::cos(theta / 2.), -std::sin(theta / 2.)}, 0}, {0, qfp{std::cos(theta / 2.), std::sin(theta / 2.)}}}; } -inline matrix4x4 rxxMatrix(const fp theta) { +[[nodiscard]] constexpr matrix4x4 rxxMatrix(const fp theta) { const auto cosTheta = std::cos(theta / 2.); const auto sinTheta = std::sin(theta / 2.); @@ -70,7 +72,7 @@ inline matrix4x4 rxxMatrix(const fp theta) { {{0., -sinTheta}, C_ZERO, C_ZERO, cosTheta}}; } -inline matrix4x4 ryyMatrix(const fp theta) { +[[nodiscard]] constexpr matrix4x4 ryyMatrix(const fp theta) { const auto cosTheta = std::cos(theta / 2.); const auto sinTheta = std::sin(theta / 2.); @@ -80,7 +82,7 @@ inline matrix4x4 ryyMatrix(const fp theta) { {{0., sinTheta}, 0, 0, cosTheta}}}; } -inline matrix4x4 rzzMatrix(const fp theta) { +[[nodiscard]] constexpr matrix4x4 rzzMatrix(const fp theta) { const auto cosTheta = std::cos(theta / 2.); const auto sinTheta = std::sin(theta / 2.); @@ -90,26 +92,24 @@ inline matrix4x4 rzzMatrix(const fp theta) { {C_ZERO, C_ZERO, C_ZERO, {cosTheta, -sinTheta}}}; } -inline matrix2x2 pMatrix(const fp lambda) { +[[nodiscard]] constexpr matrix2x2 pMatrix(const fp lambda) { return matrix2x2{{1, 0}, {0, {std::cos(lambda), std::sin(lambda)}}}; } -const matrix2x2 IDENTITY_GATE = matrix2x2::Identity(); -const matrix4x4 SWAP_GATE{ +constexpr matrix4x4 SWAP_GATE{ {1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}; -const matrix2x2 H_GATE{{1.0 / SQRT2, 1.0 / SQRT2}, {1.0 / SQRT2, -1.0 / SQRT2}}; -const matrix2x2 IPZ{{IM, C_ZERO}, {C_ZERO, M_IM}}; -const matrix2x2 IPY{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; -const matrix2x2 IPX{{C_ZERO, IM}, {IM, C_ZERO}}; +constexpr matrix2x2 H_GATE{{1.0 / SQRT2, 1.0 / SQRT2}, + {1.0 / SQRT2, -1.0 / SQRT2}}; +constexpr matrix2x2 IPZ{{IM, C_ZERO}, {C_ZERO, M_IM}}; +constexpr matrix2x2 IPY{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; +constexpr matrix2x2 IPX{{C_ZERO, IM}, {IM, C_ZERO}}; [[nodiscard]] inline matrix4x4 expandToTwoQubits(const matrix2x2& singleQubitMatrix, QubitId qubitId) { if (qubitId == 0) { - return helpers::kroneckerProduct(decomposition::IDENTITY_GATE, - singleQubitMatrix); + return Eigen::kroneckerProduct(matrix2x2::Identity(), singleQubitMatrix); } if (qubitId == 1) { - return helpers::kroneckerProduct(singleQubitMatrix, - decomposition::IDENTITY_GATE); + return Eigen::kroneckerProduct(singleQubitMatrix, matrix2x2::Identity()); } throw std::invalid_argument{"Invalid qubit id for single-qubit expansion"}; } @@ -146,7 +146,7 @@ inline matrix2x2 getSingleQubitMatrix(const Gate& gate) { return matrix2x2{{0, 1}, {1, 0}}; } if (gate.type == qc::I) { - return IDENTITY_GATE; + return matrix2x2::Identity(); } if (gate.type == qc::P) { return pMatrix(gate.parameter[0]); @@ -158,7 +158,7 @@ inline matrix2x2 getSingleQubitMatrix(const Gate& gate) { return u2Matrix(gate.parameter[0], gate.parameter[1]); } if (gate.type == qc::H) { - return matrix2x2{{FRAC1_SQRT2, FRAC1_SQRT2}, {FRAC1_SQRT2, -FRAC1_SQRT2}}; + return H_GATE; } throw std::invalid_argument{ "unsupported gate type for single qubit matrix (" + @@ -167,10 +167,8 @@ inline matrix2x2 getSingleQubitMatrix(const Gate& gate) { // TODO: remove? only used for verification of circuit and in unittests inline matrix4x4 getTwoQubitMatrix(const Gate& gate) { - using helpers::kroneckerProduct; - if (gate.qubitId.empty()) { - return kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); + return matrix4x4::Identity(); } if (gate.qubitId.size() == 1) { return expandToTwoQubits(getSingleQubitMatrix(gate), gate.qubitId[0]); @@ -197,7 +195,7 @@ inline matrix4x4 getTwoQubitMatrix(const Gate& gate) { return rzzMatrix(gate.parameter[0]); } if (gate.type == qc::I) { - return kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE); + return matrix4x4::Identity(); } throw std::invalid_argument{"unsupported gate type for two qubit matrix (" + qc::toString(gate.type) + ")"}; diff --git a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index bdd5acb7b4..40cb0986be 100644 --- a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -36,6 +36,7 @@ #include #include #include +#include #include namespace mlir::qco::decomposition { @@ -198,10 +199,10 @@ struct TwoQubitWeylDecomposition { auto [K1l, K1r, phase_l] = decomposeTwoQubitProductGate(k1); // decompose k2 = K2l ⊗ K2r auto [K2l, K2r, phase_r] = decomposeTwoQubitProductGate(k2); - assert(helpers::kroneckerProduct(K1l, K1r).isApprox( - k1, SANITY_CHECK_PRECISION)); - assert(helpers::kroneckerProduct(K2l, K2r).isApprox( - k2, SANITY_CHECK_PRECISION)); + assert( + Eigen::kroneckerProduct(K1l, K1r).isApprox(k1, SANITY_CHECK_PRECISION)); + assert( + Eigen::kroneckerProduct(K2l, K2r).isApprox(k2, SANITY_CHECK_PRECISION)); // accumulate global phase globalPhase += phase_l + phase_r; @@ -278,9 +279,9 @@ struct TwoQubitWeylDecomposition { .unitaryMatrix = unitaryMatrix, }; // make sure decomposition is equal to input - assert((helpers::kroneckerProduct(K1l, K1r) * + assert((Eigen::kroneckerProduct(K1l, K1r) * decomposition.getCanonicalMatrix() * - helpers::kroneckerProduct(K2l, K2r) * std::exp(IM * globalPhase)) + Eigen::kroneckerProduct(K2l, K2r) * std::exp(IM * globalPhase)) .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); // determine actual specialization of canonical gate so that the 1q @@ -314,9 +315,9 @@ struct TwoQubitWeylDecomposition { decomposition.globalPhase += std::arg(trace); // final check if decomposition is still valid after specialization - assert((helpers::kroneckerProduct(decomposition.k1l, decomposition.k1r) * + assert((Eigen::kroneckerProduct(decomposition.k1l, decomposition.k1r) * decomposition.getCanonicalMatrix() * - helpers::kroneckerProduct(decomposition.k2l, decomposition.k2r) * + Eigen::kroneckerProduct(decomposition.k2l, decomposition.k2r) * std::exp(IM * decomposition.globalPhase)) .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); @@ -496,7 +497,7 @@ struct TwoQubitWeylDecomposition { // transpose with complex conjugate of each element const matrix2x2 rTConj = r.transpose().conjugate(); - auto temp = helpers::kroneckerProduct(IDENTITY_GATE, rTConj); + matrix4x4 temp = Eigen::kroneckerProduct(matrix2x2::Identity(), rTConj); temp = specialUnitary * temp; // [[a, b, c, d], @@ -607,9 +608,9 @@ struct TwoQubitWeylDecomposition { c = 0.; // unmodified global phase k1l = k1l * k2l; - k2l = IDENTITY_GATE; + k2l = matrix2x2::Identity(); k1r = k1r * k2r; - k2r = IDENTITY_GATE; + k2r = matrix2x2::Identity(); } else if (newSpecialization == Specialization::SWAPEquiv) { // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, -\pi/4)` // Thus, :math:`U \sim \text{SWAP}` @@ -621,16 +622,16 @@ struct TwoQubitWeylDecomposition { // unmodified global phase k1l = k1l * k2r; k1r = k1r * k2l; - k2l = IDENTITY_GATE; - k2r = IDENTITY_GATE; + k2l = matrix2x2::Identity(); + k2r = matrix2x2::Identity(); } else { flippedFromOriginal = true; globalPhase += qc::PI_2; k1l = k1l * IPZ * k2r; k1r = k1r * IPZ * k2l; - k2l = IDENTITY_GATE; - k2r = IDENTITY_GATE; + k2l = matrix2x2::Identity(); + k2r = matrix2x2::Identity(); } a = qc::PI_4; b = qc::PI_4; @@ -652,7 +653,7 @@ struct TwoQubitWeylDecomposition { k1l = k1l * k2l; k1r = k1r * k2l; k2r = k2lDagger * k2r; - k2l = IDENTITY_GATE; + k2l = matrix2x2::Identity(); } else if (newSpecialization == Specialization::PartialSWAPFlipEquiv) { // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4)` // Thus, :math:`U \sim \text{SWAP}^\alpha` @@ -673,7 +674,7 @@ struct TwoQubitWeylDecomposition { k1l = k1l * k2l; k1r = k1r * IPZ * k2l * IPZ; k2r = IPZ * k2lDagger * IPZ * k2r; - k2l = IDENTITY_GATE; + k2l = matrix2x2::Identity(); } else if (newSpecialization == Specialization::ControlledEquiv) { // :math:`U \sim U_d(\alpha, 0, 0)` // Thus, :math:`U \sim \text{Ctrl-U}` diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 4c480e9e7a..fabeb47620 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -274,7 +274,7 @@ struct GateDecompositionPattern final } [[nodiscard]] std::optional getSingleQubitUnitaryMatrix() { - auto unitaryMatrix = decomposition::IDENTITY_GATE; + matrix2x2 unitaryMatrix = matrix2x2::Identity(); for (auto&& gate : gates) { if (auto gateMatrix = gate.op.getUnitaryMatrix()) { unitaryMatrix = *gateMatrix * unitaryMatrix; @@ -288,8 +288,7 @@ struct GateDecompositionPattern final } [[nodiscard]] std::optional getUnitaryMatrix() { - matrix4x4 unitaryMatrix = helpers::kroneckerProduct( - decomposition::IDENTITY_GATE, decomposition::IDENTITY_GATE); + matrix4x4 unitaryMatrix = matrix4x4::Identity(); matrix4x4 gateMatrix; for (auto&& gate : gates) { if (gate.op.isSingleQubit()) { @@ -555,8 +554,7 @@ struct GateDecompositionPattern final createGate(rewriter, location, sequence.globalPhase); } - matrix4x4 unitaryMatrix = helpers::kroneckerProduct( - decomposition::IDENTITY_GATE, decomposition::IDENTITY_GATE); + matrix4x4 unitaryMatrix = matrix4x4::Identity(); for (auto&& gate : sequence.gates) { // TODO: need to add each basis gate we want to use if (gate.type == qc::X && gate.qubitId.size() > 1) { diff --git a/mlir/unittests/decomposition/test_basis_decomposer.cpp b/mlir/unittests/decomposition/test_basis_decomposer.cpp index 7d92cee7e4..5c20ee0320 100644 --- a/mlir/unittests/decomposition/test_basis_decomposer.cpp +++ b/mlir/unittests/decomposition/test_basis_decomposer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include using namespace mlir::qco; using namespace mlir::qco::decomposition; @@ -161,10 +162,10 @@ INSTANTIATE_TEST_CASE_P( EulerBasis::XZX}, llvm::SmallVector{EulerBasis::XZX}), // targets to be decomposed - testing::Values(helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE), - helpers::kroneckerProduct(rzMatrix(1.0), ryMatrix(3.1)), - helpers::kroneckerProduct(IDENTITY_GATE, - rxMatrix(0.1))))); + testing::Values(matrix4x4::Identity(), + Eigen::kroneckerProduct(rzMatrix(1.0), ryMatrix(3.1)), + Eigen::kroneckerProduct(matrix2x2::Identity(), + rxMatrix(0.1))))); INSTANTIATE_TEST_CASE_P( TwoQubitMatrices, BasisDecomposerTest, @@ -183,11 +184,11 @@ INSTANTIATE_TEST_CASE_P( ::testing::Values( rzzMatrix(2.0), ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0), canonicalGate(1.5, -0.2, 0.0) * - helpers::kroneckerProduct(rxMatrix(1.0), IDENTITY_GATE), - helpers::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * + Eigen::kroneckerProduct(rxMatrix(1.0), matrix2x2::Identity()), + Eigen::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * canonicalGate(1.1, 0.2, 3.0) * - helpers::kroneckerProduct(rxMatrix(1.0), IDENTITY_GATE), - helpers::kroneckerProduct(H_GATE, IPZ) * + Eigen::kroneckerProduct(rxMatrix(1.0), matrix2x2::Identity()), + Eigen::kroneckerProduct(H_GATE, IPZ) * getTwoQubitMatrix( {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * - helpers::kroneckerProduct(IPX, IPY)))); + Eigen::kroneckerProduct(IPX, IPY)))); diff --git a/mlir/unittests/decomposition/test_euler_decomposition.cpp b/mlir/unittests/decomposition/test_euler_decomposition.cpp index ee33bf1d6e..d6a5e8de7b 100644 --- a/mlir/unittests/decomposition/test_euler_decomposition.cpp +++ b/mlir/unittests/decomposition/test_euler_decomposition.cpp @@ -107,5 +107,5 @@ INSTANTIATE_TEST_CASE_P( SingleQubitMatrices, EulerDecompositionTest, testing::Combine(testing::Values(EulerBasis::XYX, EulerBasis::XZX, EulerBasis::ZYZ, EulerBasis::ZXZ), - testing::Values(IDENTITY_GATE, ryMatrix(2.0), + testing::Values(matrix2x2::Identity(), ryMatrix(2.0), rxMatrix(0.5), rzMatrix(3.14), H_GATE))); diff --git a/mlir/unittests/decomposition/test_weyl_decomposition.cpp b/mlir/unittests/decomposition/test_weyl_decomposition.cpp index 2978bca2f8..6c2883e0cf 100644 --- a/mlir/unittests/decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/decomposition/test_weyl_decomposition.cpp @@ -20,6 +20,7 @@ #include #include #include +#include using namespace mlir::qco; using namespace mlir::qco::decomposition; @@ -67,11 +68,11 @@ class WeylDecompositionTest : public testing::TestWithParam { } [[nodiscard]] static matrix4x4 k1(const TwoQubitWeylDecomposition& decomposition) { - return helpers::kroneckerProduct(decomposition.k1l, decomposition.k1r); + return Eigen::kroneckerProduct(decomposition.k1l, decomposition.k1r); } [[nodiscard]] static matrix4x4 k2(const TwoQubitWeylDecomposition& decomposition) { - return helpers::kroneckerProduct(decomposition.k2l, decomposition.k2r); + return Eigen::kroneckerProduct(decomposition.k2l, decomposition.k2r); } }; @@ -119,23 +120,24 @@ TEST(WeylDecompositionTest, Random) { INSTANTIATE_TEST_CASE_P( SingleQubitMatrices, WeylDecompositionTest, - ::testing::Values(helpers::kroneckerProduct(IDENTITY_GATE, IDENTITY_GATE), - helpers::kroneckerProduct(rzMatrix(1.0), ryMatrix(3.1)), - helpers::kroneckerProduct(IDENTITY_GATE, rxMatrix(0.1)))); + ::testing::Values(matrix4x4::Identity(), + Eigen::kroneckerProduct(rzMatrix(1.0), ryMatrix(3.1)), + Eigen::kroneckerProduct(matrix2x2::Identity(), + rxMatrix(0.1)))); INSTANTIATE_TEST_CASE_P( TwoQubitMatrices, WeylDecompositionTest, ::testing::Values( rzzMatrix(2.0), ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0), canonicalGate(1.5, -0.2, 0.0) * - helpers::kroneckerProduct(rxMatrix(1.0), IDENTITY_GATE), - helpers::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * + Eigen::kroneckerProduct(rxMatrix(1.0), matrix2x2::Identity()), + Eigen::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * canonicalGate(1.1, 0.2, 3.0) * - helpers::kroneckerProduct(rxMatrix(1.0), IDENTITY_GATE), - helpers::kroneckerProduct(H_GATE, IPZ) * + Eigen::kroneckerProduct(rxMatrix(1.0), matrix2x2::Identity()), + Eigen::kroneckerProduct(H_GATE, IPZ) * getTwoQubitMatrix( {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * - helpers::kroneckerProduct(IPX, IPY))); + Eigen::kroneckerProduct(IPX, IPY))); INSTANTIATE_TEST_CASE_P( SpecializedMatrices, WeylDecompositionTest, From ad50befc881018c561ad1b33dbb69efd5135b594 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Thu, 5 Feb 2026 22:10:16 +0100 Subject: [PATCH 189/237] more fixes --- mlir/include/mlir/Passes/Decomposition/Helpers.h | 2 +- .../mlir/Passes/Decomposition/UnitaryMatrices.h | 4 ++-- .../mlir/Passes/Decomposition/WeylDecomposition.h | 11 +---------- .../Passes/Patterns/GateDecompositionPattern.cpp | 13 +------------ mlir/unittests/CMakeLists.txt | 2 +- mlir/unittests/Passes/CMakeLists.txt | 9 +++++++++ .../Decomposition}/CMakeLists.txt | 0 .../Decomposition}/test_basis_decomposer.cpp | 0 .../Decomposition}/test_euler_decomposition.cpp | 0 .../Decomposition}/test_weyl_decomposition.cpp | 0 10 files changed, 15 insertions(+), 26 deletions(-) create mode 100644 mlir/unittests/Passes/CMakeLists.txt rename mlir/unittests/{decomposition => Passes/Decomposition}/CMakeLists.txt (100%) rename mlir/unittests/{decomposition => Passes/Decomposition}/test_basis_decomposer.cpp (100%) rename mlir/unittests/{decomposition => Passes/Decomposition}/test_euler_decomposition.cpp (100%) rename mlir/unittests/{decomposition => Passes/Decomposition}/test_weyl_decomposition.cpp (100%) diff --git a/mlir/include/mlir/Passes/Decomposition/Helpers.h b/mlir/include/mlir/Passes/Decomposition/Helpers.h index eda36fe998..a633b32236 100644 --- a/mlir/include/mlir/Passes/Decomposition/Helpers.h +++ b/mlir/include/mlir/Passes/Decomposition/Helpers.h @@ -67,7 +67,7 @@ namespace mlir::qco::helpers { template [[nodiscard]] inline auto selfAdjointEvd(Eigen::Matrix a) { Eigen::SelfAdjointEigenSolver s; - s.compute(a); // TODO: computeDirect is faster + s.compute(a); auto vecs = s.eigenvectors().eval(); auto vals = s.eigenvalues(); return std::make_pair(vecs, vals); diff --git a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index 805e5ceadd..c91588247e 100644 --- a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -128,7 +128,7 @@ fixTwoQubitMatrixQubitOrder(const matrix4x4& twoQubitMatrix, throw std::invalid_argument{"Invalid qubit IDs for fixing two-qubit matrix"}; } -inline matrix2x2 getSingleQubitMatrix(const Gate& gate) { +[[nodiscard]] inline matrix2x2 getSingleQubitMatrix(const Gate& gate) { if (gate.type == qc::SX) { return matrix2x2{{qfp{0.5, 0.5}, qfp{0.5, -0.5}}, {qfp{0.5, -0.5}, qfp{0.5, 0.5}}}; @@ -166,7 +166,7 @@ inline matrix2x2 getSingleQubitMatrix(const Gate& gate) { } // TODO: remove? only used for verification of circuit and in unittests -inline matrix4x4 getTwoQubitMatrix(const Gate& gate) { +[[nodiscard]] inline matrix4x4 getTwoQubitMatrix(const Gate& gate) { if (gate.qubitId.empty()) { return matrix4x4::Identity(); } diff --git a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index 40cb0986be..c80a73df78 100644 --- a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -347,15 +347,6 @@ struct TwoQubitWeylDecomposition { } protected: - static constexpr fp SANITY_CHECK_PRECISION = 1e-12; - - // https://docs.rs/faer/latest/faer/mat/generic/struct.Mat.html#method.self_adjoint_eigen - template static auto selfAdjointEigenLower(T&& a) { - auto [U, S] = helpers::selfAdjointEvd(std::forward(a)); - - return std::make_pair(U, S); - } - enum class MagicBasisTransform : std::uint8_t { Into, OutOf, @@ -438,7 +429,7 @@ struct TwoQubitWeylDecomposition { randB = dist(state); } const rmatrix4x4 m2Real = randA * m.real() + randB * m.imag(); - const rmatrix4x4 pReal = selfAdjointEigenLower(m2Real).first; + const rmatrix4x4 pReal = helpers::selfAdjointEvd(m2Real).first; const matrix4x4 p = pReal; const diagonal4x4 d = (p.transpose() * m * p).diagonal(); diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index fabeb47620..096016e109 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -405,18 +405,7 @@ struct GateDecompositionPattern final if (it == outQubits.end()) { return false; } - // TODO: this only works because parameters are at end of operands; - // use to-be-implemented getInputQubits() instead - auto getInputQubits = - [](UnitaryOpInterface op) -> llvm::SmallVector { - if (auto&& ctrlOp = llvm::dyn_cast(*op)) { - auto&& range = llvm::concat(ctrlOp.getTargetsIn(), - ctrlOp.getControlsIn()); - return {range.begin(), range.end()}; - } - return op->getOperands(); - }; - auto&& opInQubits = getInputQubits(nextGate); + auto&& opInQubits = nextGate.getInputQubits(); // iterator in the operation input of "old" qubit that already has // previous single-qubit gates in this series auto it2 = llvm::find(opInQubits, firstQubitIt != outQubits.end() diff --git a/mlir/unittests/CMakeLists.txt b/mlir/unittests/CMakeLists.txt index 46a46bd865..f8ab7bf8eb 100644 --- a/mlir/unittests/CMakeLists.txt +++ b/mlir/unittests/CMakeLists.txt @@ -6,7 +6,7 @@ # # Licensed under the MIT License -add_subdirectory(decomposition) +add_subdirectory(Passes) add_subdirectory(Compiler) add_subdirectory(Dialect) diff --git a/mlir/unittests/Passes/CMakeLists.txt b/mlir/unittests/Passes/CMakeLists.txt new file mode 100644 index 0000000000..c64c6a6c7e --- /dev/null +++ b/mlir/unittests/Passes/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM +# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +add_subdirectory(Decomposition) diff --git a/mlir/unittests/decomposition/CMakeLists.txt b/mlir/unittests/Passes/Decomposition/CMakeLists.txt similarity index 100% rename from mlir/unittests/decomposition/CMakeLists.txt rename to mlir/unittests/Passes/Decomposition/CMakeLists.txt diff --git a/mlir/unittests/decomposition/test_basis_decomposer.cpp b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp similarity index 100% rename from mlir/unittests/decomposition/test_basis_decomposer.cpp rename to mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp diff --git a/mlir/unittests/decomposition/test_euler_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp similarity index 100% rename from mlir/unittests/decomposition/test_euler_decomposition.cpp rename to mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp diff --git a/mlir/unittests/decomposition/test_weyl_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp similarity index 100% rename from mlir/unittests/decomposition/test_weyl_decomposition.cpp rename to mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp From 0b09375974d126c1bb13ddedaa2b3513e657cb13 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 6 Feb 2026 11:46:16 +0100 Subject: [PATCH 190/237] relax isUnitaryMatrix() default tolerance to Eigen's default value --- mlir/include/mlir/Passes/Decomposition/Helpers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/include/mlir/Passes/Decomposition/Helpers.h b/mlir/include/mlir/Passes/Decomposition/Helpers.h index a633b32236..79e7b4a5ab 100644 --- a/mlir/include/mlir/Passes/Decomposition/Helpers.h +++ b/mlir/include/mlir/Passes/Decomposition/Helpers.h @@ -75,7 +75,7 @@ template template [[nodiscard]] bool isUnitaryMatrix(const Eigen::Matrix& matrix, - fp tolerance = 1e-13) { + fp tolerance = 1e-12) { return (matrix.transpose().conjugate() * matrix).isIdentity(tolerance); } // NOLINTEND(misc-include-cleaner) From bc905ed817f5533e02e89ae731b7d24b594bf90b Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 6 Feb 2026 11:47:23 +0100 Subject: [PATCH 191/237] fix mysterious segfault in Eigen::kroneckerProduct (still no idea why...) --- mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h | 7 ++++--- mlir/include/mlir/Passes/Decomposition/Helpers.h | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index 7293157cc4..232fd9cae0 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -499,9 +499,10 @@ class TwoQubitBasisDecomposer { for (auto targetBasis : targetBasisList) { auto circuit = EulerDecomposition::generateCircuit( targetBasis, unitaryMat, simplify, atol); - assert(circuit.getUnitaryMatrix().isApprox( - Eigen::kroneckerProduct(matrix2x2::Identity(), unitaryMat), - SANITY_CHECK_PRECISION)); + // check top-left 2x2 matrix of generated circuit since the circuit + // operates only on one qubit + assert((circuit.getUnitaryMatrix().block<2, 2>(0, 0).isApprox( + unitaryMat, SANITY_CHECK_PRECISION))); auto error = calculateError(circuit); if (error < minError) { bestCircuit = circuit; diff --git a/mlir/include/mlir/Passes/Decomposition/Helpers.h b/mlir/include/mlir/Passes/Decomposition/Helpers.h index 79e7b4a5ab..021704cb5e 100644 --- a/mlir/include/mlir/Passes/Decomposition/Helpers.h +++ b/mlir/include/mlir/Passes/Decomposition/Helpers.h @@ -65,8 +65,8 @@ namespace mlir::qco::helpers { // NOLINTBEGIN(misc-include-cleaner) template -[[nodiscard]] inline auto selfAdjointEvd(Eigen::Matrix a) { - Eigen::SelfAdjointEigenSolver s; +[[nodiscard]] inline auto selfAdjointEvd(const Eigen::Matrix& a) { + Eigen::SelfAdjointEigenSolver> s; s.compute(a); auto vecs = s.eigenvectors().eval(); auto vals = s.eigenvalues(); From ac7fb91fc20d59a5f195e7089100e18d7b1f99cf Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 6 Feb 2026 11:49:52 +0100 Subject: [PATCH 192/237] reduce random test time limit to 3s This should still be sufficient for proper testing but will reduce the amount of wasted CPU and development time when not necessary. When doing active development on the decompositions, this can still be manually increased again. --- mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp | 2 +- .../unittests/Passes/Decomposition/test_euler_decomposition.cpp | 2 +- mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp index 5c20ee0320..7831bf5956 100644 --- a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp +++ b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp @@ -116,7 +116,7 @@ TEST_P(BasisDecomposerTest, TestApproximation) { } TEST(BasisDecomposerTest, Random) { - auto stopTime = std::chrono::steady_clock::now() + std::chrono::seconds{10}; + auto stopTime = std::chrono::steady_clock::now() + std::chrono::seconds{3}; auto iterations = 0; const Gate basisGate{.type = qc::X, .parameter = {}, .qubitId = {0, 1}}; diff --git a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp index d6a5e8de7b..24438cdc0a 100644 --- a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp @@ -79,7 +79,7 @@ TEST_P(EulerDecompositionTest, TestExact) { } TEST(EulerDecompositionTest, Random) { - auto stopTime = std::chrono::steady_clock::now() + std::chrono::seconds{10}; + auto stopTime = std::chrono::steady_clock::now() + std::chrono::seconds{3}; auto iterations = 0; auto eulerBases = std::array{EulerBasis::XYX, EulerBasis::XZX, EulerBasis::ZYZ, EulerBasis::ZXZ}; diff --git a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp index 6c2883e0cf..ae7bd6a25a 100644 --- a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp @@ -98,7 +98,7 @@ TEST_P(WeylDecompositionTest, TestApproximation) { } TEST(WeylDecompositionTest, Random) { - auto stopTime = std::chrono::steady_clock::now() + std::chrono::seconds{10}; + auto stopTime = std::chrono::steady_clock::now() + std::chrono::seconds{3}; auto iterations = 0; while (std::chrono::steady_clock::now() < stopTime) { auto originalMatrix = randomUnitaryMatrix(); From 80fa001e31e68e056136bbb4fbbd0e62b98f81a0 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 6 Feb 2026 12:52:45 +0100 Subject: [PATCH 193/237] add C_ prefix to complex IM, M_IM --- .../Passes/Decomposition/BasisDecomposer.h | 24 +++++------ .../mlir/Passes/Decomposition/GateSequence.h | 2 +- .../mlir/Passes/Decomposition/Helpers.h | 7 +--- .../Passes/Decomposition/UnitaryMatrices.h | 4 +- .../Passes/Decomposition/WeylDecomposition.h | 14 +++---- .../Patterns/GateDecompositionPattern.cpp | 2 +- .../Decomposition/test_basis_decomposer.cpp | 42 ++++++++++++++++++- .../test_euler_decomposition.cpp | 2 +- .../Decomposition/test_weyl_decomposition.cpp | 2 +- 9 files changed, 68 insertions(+), 31 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index 232fd9cae0..887a662be3 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -104,21 +104,21 @@ class TwoQubitBasisDecomposer { auto b = basisDecomposer.b; auto temp = qfp(0.5, -0.5); const matrix2x2 k11l{ - {temp * (M_IM * std::exp(qfp(0., -b))), temp * std::exp(qfp(0., -b))}, - {temp * (M_IM * std::exp(qfp(0., b))), temp * -std::exp(qfp(0., b))}}; - const matrix2x2 k11r{{FRAC1_SQRT2 * (IM * std::exp(qfp(0., -b))), + {temp * (C_M_IM * std::exp(qfp(0., -b))), temp * std::exp(qfp(0., -b))}, + {temp * (C_M_IM * std::exp(qfp(0., b))), temp * -std::exp(qfp(0., b))}}; + const matrix2x2 k11r{{FRAC1_SQRT2 * (C_IM * std::exp(qfp(0., -b))), FRAC1_SQRT2 * -std::exp(qfp(0., -b))}, {FRAC1_SQRT2 * std::exp(qfp(0., b)), - FRAC1_SQRT2 * (M_IM * std::exp(qfp(0., b)))}}; + FRAC1_SQRT2 * (C_M_IM * std::exp(qfp(0., b)))}}; const matrix2x2 k32lK21l{{FRAC1_SQRT2 * qfp(1., std::cos(2. * b)), - FRAC1_SQRT2 * (IM * std::sin(2. * b))}, - {FRAC1_SQRT2 * (IM * std::sin(2. * b)), + FRAC1_SQRT2 * (C_IM * std::sin(2. * b))}, + {FRAC1_SQRT2 * (C_IM * std::sin(2. * b)), FRAC1_SQRT2 * qfp(1., -std::cos(2. * b))}}; temp = qfp(0.5, 0.5); const matrix2x2 k21r{ - {temp * (M_IM * std::exp(qfp(0., -2. * b))), + {temp * (C_M_IM * std::exp(qfp(0., -2. * b))), temp * std::exp(qfp(0., -2. * b))}, - {temp * (IM * std::exp(qfp(0., 2. * b))), + {temp * (C_IM * std::exp(qfp(0., 2. * b))), temp * std::exp(qfp(0., 2. * b))}, }; const matrix2x2 k22l{ @@ -133,14 +133,14 @@ class TwoQubitBasisDecomposer { FRAC1_SQRT2 * std::exp(qfp(0., b))}, }; const matrix2x2 k31r{ - {IM * std::exp(qfp(0., b)), C_ZERO}, - {C_ZERO, M_IM * std::exp(qfp(0., -b))}, + {C_IM * std::exp(qfp(0., b)), C_ZERO}, + {C_ZERO, C_M_IM * std::exp(qfp(0., -b))}, }; temp = qfp(0.5, 0.5); const matrix2x2 k32r{ {temp * std::exp(qfp(0., b)), temp * -std::exp(qfp(0., -b))}, - {temp * (M_IM * std::exp(qfp(0., b))), - temp * (M_IM * std::exp(qfp(0., -b)))}, + {temp * (C_M_IM * std::exp(qfp(0., b))), + temp * (C_M_IM * std::exp(qfp(0., -b)))}, }; auto k1lDagger = basisDecomposer.k1l.transpose().conjugate(); auto k1rDagger = basisDecomposer.k1r.transpose().conjugate(); diff --git a/mlir/include/mlir/Passes/Decomposition/GateSequence.h b/mlir/include/mlir/Passes/Decomposition/GateSequence.h index bf8aa05c4e..1f10d53482 100644 --- a/mlir/include/mlir/Passes/Decomposition/GateSequence.h +++ b/mlir/include/mlir/Passes/Decomposition/GateSequence.h @@ -79,7 +79,7 @@ struct QubitGateSequence { auto gateMatrix = getTwoQubitMatrix(gate); unitaryMatrix = gateMatrix * unitaryMatrix; } - unitaryMatrix *= std::exp(IM * globalPhase); + unitaryMatrix *= std::exp(C_IM * globalPhase); assert(helpers::isUnitaryMatrix(unitaryMatrix)); return unitaryMatrix; } diff --git a/mlir/include/mlir/Passes/Decomposition/Helpers.h b/mlir/include/mlir/Passes/Decomposition/Helpers.h index 021704cb5e..419d1de0a2 100644 --- a/mlir/include/mlir/Passes/Decomposition/Helpers.h +++ b/mlir/include/mlir/Passes/Decomposition/Helpers.h @@ -16,17 +16,14 @@ #include // NOLINT(misc-include-cleaner) #include // NOLINT(misc-include-cleaner) -#include #include #include -#include #include #include #include #include #include #include -#include #include #include // NOLINT(misc-include-cleaner) @@ -44,8 +41,8 @@ using rdiagonal4x4 = Eigen::Vector; constexpr qfp C_ZERO{0., 0.}; constexpr qfp C_ONE{1., 0.}; constexpr qfp C_M_ONE{-1., 0.}; -constexpr qfp IM{0., 1.}; -constexpr qfp M_IM{0., -1.}; +constexpr qfp C_IM{0., 1.}; +constexpr qfp C_M_IM{0., -1.}; } // namespace mlir::qco diff --git a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index c91588247e..eaddfcc32a 100644 --- a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -99,9 +99,9 @@ constexpr matrix4x4 SWAP_GATE{ {1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}; constexpr matrix2x2 H_GATE{{1.0 / SQRT2, 1.0 / SQRT2}, {1.0 / SQRT2, -1.0 / SQRT2}}; -constexpr matrix2x2 IPZ{{IM, C_ZERO}, {C_ZERO, M_IM}}; +constexpr matrix2x2 IPZ{{C_IM, C_ZERO}, {C_ZERO, C_M_IM}}; constexpr matrix2x2 IPY{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; -constexpr matrix2x2 IPX{{C_ZERO, IM}, {IM, C_ZERO}}; +constexpr matrix2x2 IPX{{C_ZERO, C_IM}, {C_IM, C_ZERO}}; [[nodiscard]] inline matrix4x4 expandToTwoQubits(const matrix2x2& singleQubitMatrix, QubitId qubitId) { diff --git a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index c80a73df78..09f09b612b 100644 --- a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -171,7 +171,7 @@ struct TwoQubitWeylDecomposition { // parameters of the canonical gate which is later used in the // verification matrix4x4 temp = dReal.asDiagonal(); - temp *= IM; + temp *= C_IM; // since the matrix is diagonal, matrix exponental is equivalent to // element-wise exponential function temp.diagonal() = temp.diagonal().array().exp().matrix(); @@ -281,7 +281,7 @@ struct TwoQubitWeylDecomposition { // make sure decomposition is equal to input assert((Eigen::kroneckerProduct(K1l, K1r) * decomposition.getCanonicalMatrix() * - Eigen::kroneckerProduct(K2l, K2r) * std::exp(IM * globalPhase)) + Eigen::kroneckerProduct(K2l, K2r) * std::exp(C_IM * globalPhase)) .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); // determine actual specialization of canonical gate so that the 1q @@ -318,7 +318,7 @@ struct TwoQubitWeylDecomposition { assert((Eigen::kroneckerProduct(decomposition.k1l, decomposition.k1r) * decomposition.getCanonicalMatrix() * Eigen::kroneckerProduct(decomposition.k2l, decomposition.k2r) * - std::exp(IM * decomposition.globalPhase)) + std::exp(C_IM * decomposition.globalPhase)) .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); return decomposition; @@ -355,10 +355,10 @@ struct TwoQubitWeylDecomposition { static matrix4x4 magicBasisTransform(const matrix4x4& unitary, MagicBasisTransform direction) { const matrix4x4 bNonNormalized{ - {C_ONE, IM, C_ZERO, C_ZERO}, - {C_ZERO, C_ZERO, IM, C_ONE}, - {C_ZERO, C_ZERO, IM, C_M_ONE}, - {C_ONE, M_IM, C_ZERO, C_ZERO}, + {C_ONE, C_IM, C_ZERO, C_ZERO}, + {C_ZERO, C_ZERO, C_IM, C_ONE}, + {C_ZERO, C_ZERO, C_IM, C_M_ONE}, + {C_ONE, C_M_IM, C_ZERO, C_ZERO}, }; const matrix4x4 bNonNormalizedDagger{ diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 096016e109..aa86483bf3 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -587,7 +587,7 @@ struct GateDecompositionPattern final throw std::runtime_error{"Unknown gate type!"}; } } - assert((unitaryMatrix * std::exp(IM * sequence.globalPhase)) + assert((unitaryMatrix * std::exp(C_IM * sequence.globalPhase)) .isApprox(series.getUnitaryMatrix().value_or(matrix4x4::Zero()), SANITY_CHECK_PRECISION)); diff --git a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp index 7831bf5956..2cdb9cfe8b 100644 --- a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp +++ b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp @@ -74,7 +74,7 @@ class BasisDecomposerTest matrix = getTwoQubitMatrix(gate) * matrix; } - matrix *= std::exp(IM * sequence.globalPhase); + matrix *= std::exp(C_IM * sequence.globalPhase); return matrix; } @@ -148,6 +148,46 @@ TEST(BasisDecomposerTest, Random) { std::cerr << "Iterations: " << iterations << '\n'; } +TEST(BasisDecomposerTest, Crash) { + using namespace std::complex_literals; + matrix4x4 originalMatrix{{-0.23104450537689214 + -0.44268488901708902i, + -0.60656504798621003 + -0.27756198294119977i, + -0.2168858251642842 + -0.27845819247692827i, + -0.42430958159720128 + 0.032705758031399738i}, + {0.12891961731437976 + -0.2577139933400836i, + 0.059033561840284507 + 0.051774294297249751i, + 0.58205201943239671 + -0.20399736896613216i, + -0.027126130902642431 + 0.72777907642808048i}, + {0.60297884333102469 + 0.35765188950245741i, + -0.59087990913607613 + 0.22062558535485413i, + 0.077633311362340196 + -0.28085102069787549i, + 0.13707024540895657 + -0.083632801340620747i}, + {-0.22282692851707037 + 0.3556143358154254i, + 0.3014101239694616 + 0.24537699657586307i, + 0.10077337125619777 + -0.63242595993554396i, + -0.48930698747658014 + -0.1526088977163027i}}; + + const Gate basisGate{.type = qc::X, .parameter = {}, .qubitId = {0, 1}}; + const llvm::SmallVector eulerBases = {EulerBasis::XYX, + EulerBasis::ZXZ}; + + auto targetDecomposition = + TwoQubitWeylDecomposition::create(originalMatrix, 1.0); + auto decomposer = TwoQubitBasisDecomposer::create(basisGate, 1.0); + auto decomposedSequence = decomposer.twoQubitDecompose( + targetDecomposition, eulerBases, 1.0, true, std::nullopt); + + ASSERT_TRUE(decomposedSequence.has_value()); + + auto restoredMatrix = BasisDecomposerTest::restore(*decomposedSequence); + + EXPECT_TRUE(restoredMatrix.isApprox(originalMatrix)) + << "ORIGINAL:\n" + << originalMatrix << '\n' + << "RESULT:\n" + << restoredMatrix << '\n'; +} + INSTANTIATE_TEST_CASE_P( SingleQubitMatrices, BasisDecomposerTest, testing::Combine( diff --git a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp index 24438cdc0a..cbfc50ac9d 100644 --- a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp @@ -54,7 +54,7 @@ class EulerDecompositionTest matrix = getSingleQubitMatrix(gate) * matrix; } - matrix *= std::exp(IM * sequence.globalPhase); + matrix *= std::exp(C_IM * sequence.globalPhase); return matrix; } diff --git a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp index ae7bd6a25a..e33a5b604d 100644 --- a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp @@ -60,7 +60,7 @@ class WeylDecompositionTest : public testing::TestWithParam { [[nodiscard]] static qfp globalPhaseFactor(const TwoQubitWeylDecomposition& decomposition) { - return std::exp(IM * decomposition.globalPhase); + return std::exp(C_IM * decomposition.globalPhase); } [[nodiscard]] static matrix4x4 can(const TwoQubitWeylDecomposition& decomposition) { From f28b77e1ae45a9fe56f7acb3a7f7a42730b543da Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 6 Feb 2026 15:15:15 +0100 Subject: [PATCH 194/237] remove custom types --- .../Passes/Decomposition/BasisDecomposer.h | 199 +++++++++--------- .../Passes/Decomposition/EulerDecomposition.h | 39 ++-- mlir/include/mlir/Passes/Decomposition/Gate.h | 3 +- .../mlir/Passes/Decomposition/GateSequence.h | 6 +- .../mlir/Passes/Decomposition/Helpers.h | 28 +-- .../Passes/Decomposition/UnitaryMatrices.h | 139 ++++++------ .../Passes/Decomposition/WeylDecomposition.h | 142 ++++++------- .../Patterns/GateDecompositionPattern.cpp | 31 +-- .../Decomposition/test_basis_decomposer.cpp | 28 +-- .../test_euler_decomposition.cpp | 18 +- .../Decomposition/test_weyl_decomposition.cpp | 51 ++--- 11 files changed, 344 insertions(+), 340 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index 887a662be3..ad43ece213 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -56,7 +56,7 @@ class TwoQubitBasisDecomposer { * basis gate). */ static TwoQubitBasisDecomposer create(const Gate& basisGate, - fp basisFidelity) { + double basisFidelity) { auto relativeEq = [](auto&& lhs, auto&& rhs, auto&& epsilon, auto&& maxRelative) { // Handle same infinities @@ -83,13 +83,13 @@ class TwoQubitBasisDecomposer { } return absDiff <= absLhs * maxRelative; }; - const matrix2x2 k12RArr{ - {qfp(0., FRAC1_SQRT2), qfp(FRAC1_SQRT2, 0.)}, - {qfp(-FRAC1_SQRT2, 0.), qfp(0., -FRAC1_SQRT2)}, + const Eigen::Matrix2cd k12RArr{ + {{0., FRAC1_SQRT2}, {FRAC1_SQRT2, 0.}}, + {{-FRAC1_SQRT2, 0.}, {0., -FRAC1_SQRT2}}, }; - const matrix2x2 k12LArr{ - {qfp(0.5, 0.5), qfp(0.5, 0.5)}, - {qfp(-0.5, 0.5), qfp(0.5, -0.5)}, + const Eigen::Matrix2cd k12LArr{ + {{0.5, 0.5}, {0.5, 0.5}}, + {{-0.5, 0.5}, {0.5, -0.5}}, }; const auto basisDecomposer = @@ -102,45 +102,50 @@ class TwoQubitBasisDecomposer { // Create some useful matrices U1, U2, U3 are equivalent to the basis, // expand as Ui = Ki1.Ubasis.Ki2 auto b = basisDecomposer.b; - auto temp = qfp(0.5, -0.5); - const matrix2x2 k11l{ - {temp * (C_M_IM * std::exp(qfp(0., -b))), temp * std::exp(qfp(0., -b))}, - {temp * (C_M_IM * std::exp(qfp(0., b))), temp * -std::exp(qfp(0., b))}}; - const matrix2x2 k11r{{FRAC1_SQRT2 * (C_IM * std::exp(qfp(0., -b))), - FRAC1_SQRT2 * -std::exp(qfp(0., -b))}, - {FRAC1_SQRT2 * std::exp(qfp(0., b)), - FRAC1_SQRT2 * (C_M_IM * std::exp(qfp(0., b)))}}; - const matrix2x2 k32lK21l{{FRAC1_SQRT2 * qfp(1., std::cos(2. * b)), - FRAC1_SQRT2 * (C_IM * std::sin(2. * b))}, - {FRAC1_SQRT2 * (C_IM * std::sin(2. * b)), - FRAC1_SQRT2 * qfp(1., -std::cos(2. * b))}}; - temp = qfp(0.5, 0.5); - const matrix2x2 k21r{ - {temp * (C_M_IM * std::exp(qfp(0., -2. * b))), - temp * std::exp(qfp(0., -2. * b))}, - {temp * (C_IM * std::exp(qfp(0., 2. * b))), - temp * std::exp(qfp(0., 2. * b))}, + std::complex temp{0.5, -0.5}; + const Eigen::Matrix2cd k11l{ + {temp * (C_M_IM * std::exp(std::complex{0., -b})), + temp * std::exp(std::complex{0., -b})}, + {temp * (C_M_IM * std::exp(std::complex{0., b})), + temp * -std::exp(std::complex{0., b})}}; + const Eigen::Matrix2cd k11r{ + {FRAC1_SQRT2 * (C_IM * std::exp(std::complex{0., -b})), + FRAC1_SQRT2 * -std::exp(std::complex{0., -b})}, + {FRAC1_SQRT2 * std::exp(std::complex{0., b}), + FRAC1_SQRT2 * (C_M_IM * std::exp(std::complex{0., b}))}}; + const Eigen::Matrix2cd k32lK21l{ + {FRAC1_SQRT2 * std::complex{1., std::cos(2. * b)}, + FRAC1_SQRT2 * (C_IM * std::sin(2. * b))}, + {FRAC1_SQRT2 * (C_IM * std::sin(2. * b)), + FRAC1_SQRT2 * std::complex{1., -std::cos(2. * b)}}}; + temp = std::complex{0.5, 0.5}; + const Eigen::Matrix2cd k21r{ + {temp * (C_M_IM * std::exp(std::complex{0., -2. * b})), + temp * std::exp(std::complex{0., -2. * b})}, + {temp * (C_IM * std::exp(std::complex{0., 2. * b})), + temp * std::exp(std::complex{0., 2. * b})}, }; - const matrix2x2 k22l{ - {qfp(FRAC1_SQRT2, 0.), qfp(-FRAC1_SQRT2, 0.)}, - {qfp(FRAC1_SQRT2, 0.), qfp(FRAC1_SQRT2, 0.)}, + const Eigen::Matrix2cd k22l{ + {FRAC1_SQRT2, -FRAC1_SQRT2}, + {FRAC1_SQRT2, FRAC1_SQRT2}, }; - const matrix2x2 k22r{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; - const matrix2x2 k31l{ - {FRAC1_SQRT2 * std::exp(qfp(0., -b)), - FRAC1_SQRT2 * std::exp(qfp(0., -b))}, - {FRAC1_SQRT2 * -std::exp(qfp(0., b)), - FRAC1_SQRT2 * std::exp(qfp(0., b))}, + const Eigen::Matrix2cd k22r{{0, 1}, {-1, 0}}; + const Eigen::Matrix2cd k31l{ + {FRAC1_SQRT2 * std::exp(std::complex{0., -b}), + FRAC1_SQRT2 * std::exp(std::complex{0., -b})}, + {FRAC1_SQRT2 * -std::exp(std::complex{0., b}), + FRAC1_SQRT2 * std::exp(std::complex{0., b})}, }; - const matrix2x2 k31r{ - {C_IM * std::exp(qfp(0., b)), C_ZERO}, - {C_ZERO, C_M_IM * std::exp(qfp(0., -b))}, + const Eigen::Matrix2cd k31r{ + {C_IM * std::exp(std::complex{0., b}), C_ZERO}, + {C_ZERO, C_M_IM * std::exp(std::complex{0., -b})}, }; - temp = qfp(0.5, 0.5); - const matrix2x2 k32r{ - {temp * std::exp(qfp(0., b)), temp * -std::exp(qfp(0., -b))}, - {temp * (C_M_IM * std::exp(qfp(0., b))), - temp * (C_M_IM * std::exp(qfp(0., -b)))}, + temp = std::complex{0.5, 0.5}; + const Eigen::Matrix2cd k32r{ + {temp * std::exp(std::complex{0., b}), + temp * -std::exp(std::complex{0., -b})}, + {temp * (C_M_IM * std::exp(std::complex{0., b})), + temp * (C_M_IM * std::exp(std::complex{0., -b}))}, }; auto k1lDagger = basisDecomposer.k1l.transpose().conjugate(); auto k1rDagger = basisDecomposer.k1r.transpose().conjugate(); @@ -216,20 +221,20 @@ class TwoQubitBasisDecomposer { [[nodiscard]] std::optional twoQubitDecompose( const decomposition::TwoQubitWeylDecomposition& targetDecomposition, const llvm::SmallVector& target1qEulerBases, - std::optional basisFidelity, bool approximate, + std::optional basisFidelity, bool approximate, std::optional numBasisGateUses) const { auto getBasisFidelity = [&]() { if (approximate) { return basisFidelity.value_or(this->basisFidelity); } - return static_cast(1.0); + return 1.0; }; - fp actualBasisFidelity = getBasisFidelity(); + double actualBasisFidelity = getBasisFidelity(); auto traces = this->traces(targetDecomposition); auto getDefaultNbasis = [&]() { // determine smallest number of basis gates required to fulfill given // basis fidelity constraint - auto bestValue = std::numeric_limits::min(); + auto bestValue = std::numeric_limits::min(); auto bestIndex = -1; for (int i = 0; std::cmp_less(i, traces.size()); ++i) { // lower basis fidelity means it becomes easier to use fewer basis gates @@ -325,15 +330,18 @@ class TwoQubitBasisDecomposer { * Constructs decomposer instance. */ TwoQubitBasisDecomposer( - Gate basisGate, fp basisFidelity, + Gate basisGate, double basisFidelity, const decomposition::TwoQubitWeylDecomposition& basisDecomposer, - bool isSuperControlled, const matrix2x2& u0l, const matrix2x2& u0r, - const matrix2x2& u1l, const matrix2x2& u1ra, const matrix2x2& u1rb, - const matrix2x2& u2la, const matrix2x2& u2lb, const matrix2x2& u2ra, - const matrix2x2& u2rb, const matrix2x2& u3l, const matrix2x2& u3r, - const matrix2x2& q0l, const matrix2x2& q0r, const matrix2x2& q1la, - const matrix2x2& q1lb, const matrix2x2& q1ra, const matrix2x2& q1rb, - const matrix2x2& q2l, const matrix2x2& q2r) + bool isSuperControlled, const Eigen::Matrix2cd& u0l, + const Eigen::Matrix2cd& u0r, const Eigen::Matrix2cd& u1l, + const Eigen::Matrix2cd& u1ra, const Eigen::Matrix2cd& u1rb, + const Eigen::Matrix2cd& u2la, const Eigen::Matrix2cd& u2lb, + const Eigen::Matrix2cd& u2ra, const Eigen::Matrix2cd& u2rb, + const Eigen::Matrix2cd& u3l, const Eigen::Matrix2cd& u3r, + const Eigen::Matrix2cd& q0l, const Eigen::Matrix2cd& q0r, + const Eigen::Matrix2cd& q1la, const Eigen::Matrix2cd& q1lb, + const Eigen::Matrix2cd& q1ra, const Eigen::Matrix2cd& q1rb, + const Eigen::Matrix2cd& q2l, const Eigen::Matrix2cd& q2r) : basisGate{std::move(basisGate)}, basisFidelity{basisFidelity}, basisDecomposer{basisDecomposer}, isSuperControlled{isSuperControlled}, u0l{u0l}, u0r{u0r}, u1l{u1l}, u1ra{u1ra}, u1rb{u1rb}, u2la{u2la}, @@ -355,7 +363,7 @@ class TwoQubitBasisDecomposer { * * which is optimal for all targets and bases */ - [[nodiscard]] static llvm::SmallVector + [[nodiscard]] static llvm::SmallVector decomp0(const decomposition::TwoQubitWeylDecomposition& target) { return { target.k1r * target.k2r, @@ -377,7 +385,7 @@ class TwoQubitBasisDecomposer { * * which is optimal for all targets and bases with ``z==0`` or ``c==0``. */ - [[nodiscard]] llvm::SmallVector + [[nodiscard]] llvm::SmallVector decomp1(const decomposition::TwoQubitWeylDecomposition& target) const { // may not work for z != 0 and c != 0 (not always in Weyl chamber) return { @@ -411,7 +419,7 @@ class TwoQubitBasisDecomposer { * and target :math:`\sim U_d(x, y, 0)`. No guarantees for * non-supercontrolled basis. */ - [[nodiscard]] llvm::SmallVector decomp2Supercontrolled( + [[nodiscard]] llvm::SmallVector decomp2Supercontrolled( const decomposition::TwoQubitWeylDecomposition& target) const { if (!isSuperControlled) { llvm::reportFatalInternalError( @@ -437,7 +445,7 @@ class TwoQubitBasisDecomposer { * :math:`\sim U_d(\pi/4, b, 0)`, all b, and any target. No guarantees for * non-supercontrolled basis. */ - [[nodiscard]] llvm::SmallVector decomp3Supercontrolled( + [[nodiscard]] llvm::SmallVector decomp3Supercontrolled( const decomposition::TwoQubitWeylDecomposition& target) const { if (!isSuperControlled) { llvm::reportFatalInternalError( @@ -462,20 +470,21 @@ class TwoQubitBasisDecomposer { * This can be used to determine the smallest number of basis gates that are * necessary to construct an equivalent to the canonical gate. */ - [[nodiscard]] std::array + [[nodiscard]] std::array, 4> traces(const decomposition::TwoQubitWeylDecomposition& target) const { return { - static_cast(4.) * - qfp(std::cos(target.a) * std::cos(target.b) * std::cos(target.c), - std::sin(target.a) * std::sin(target.b) * std::sin(target.c)), - static_cast(4.) * - qfp(std::cos(qc::PI_4 - target.a) * - std::cos(basisDecomposer.b - target.b) * std::cos(target.c), - std::sin(qc::PI_4 - target.a) * - std::sin(basisDecomposer.b - target.b) * - std::sin(target.c)), - qfp(4. * std::cos(target.c), 0.), - qfp(4., 0.), + 4. * + std::complex{ + std::cos(target.a) * std::cos(target.b) * std::cos(target.c), + std::sin(target.a) * std::sin(target.b) * std::sin(target.c)}, + 4. * std::complex{std::cos(qc::PI_4 - target.a) * + std::cos(basisDecomposer.b - target.b) * + std::cos(target.c), + std::sin(qc::PI_4 - target.a) * + std::sin(basisDecomposer.b - target.b) * + std::sin(target.c)}, + std::complex{4. * std::cos(target.c), 0.}, + std::complex{4., 0.}, }; } @@ -485,16 +494,16 @@ class TwoQubitBasisDecomposer { * least complexity will be chosen. */ [[nodiscard]] static OneQubitGateSequence unitaryToGateSequenceInner( - const matrix2x2& unitaryMat, + const Eigen::Matrix2cd& unitaryMat, const llvm::SmallVector& targetBasisList, QubitId /*qubit*/, // TODO: add error map here: per qubit a mapping of operation to error // value for better calculateError() - bool simplify, std::optional atol) { - auto calculateError = [](const OneQubitGateSequence& sequence) -> fp { - return static_cast(sequence.complexity()); + bool simplify, std::optional atol) { + auto calculateError = [](const OneQubitGateSequence& sequence) -> double { + return static_cast(sequence.complexity()); }; - auto minError = std::numeric_limits::max(); + auto minError = std::numeric_limits::max(); OneQubitGateSequence bestCircuit; for (auto targetBasis : targetBasisList) { auto circuit = EulerDecomposition::generateCircuit( @@ -516,34 +525,34 @@ class TwoQubitBasisDecomposer { // basis gate of this decomposer instance Gate basisGate{}; // fidelity with which the basis gate decomposition has been calculated - fp basisFidelity; + double basisFidelity; // cached decomposition for basis gate decomposition::TwoQubitWeylDecomposition basisDecomposer; // true if basis gate is super-controlled bool isSuperControlled; // pre-built components for decomposition with 3 basis gates - matrix2x2 u0l; - matrix2x2 u0r; - matrix2x2 u1l; - matrix2x2 u1ra; - matrix2x2 u1rb; - matrix2x2 u2la; - matrix2x2 u2lb; - matrix2x2 u2ra; - matrix2x2 u2rb; - matrix2x2 u3l; - matrix2x2 u3r; + Eigen::Matrix2cd u0l; + Eigen::Matrix2cd u0r; + Eigen::Matrix2cd u1l; + Eigen::Matrix2cd u1ra; + Eigen::Matrix2cd u1rb; + Eigen::Matrix2cd u2la; + Eigen::Matrix2cd u2lb; + Eigen::Matrix2cd u2ra; + Eigen::Matrix2cd u2rb; + Eigen::Matrix2cd u3l; + Eigen::Matrix2cd u3r; // pre-built components for decomposition with 2 basis gates - matrix2x2 q0l; - matrix2x2 q0r; - matrix2x2 q1la; - matrix2x2 q1lb; - matrix2x2 q1ra; - matrix2x2 q1rb; - matrix2x2 q2l; - matrix2x2 q2r; + Eigen::Matrix2cd q0l; + Eigen::Matrix2cd q0r; + Eigen::Matrix2cd q1la; + Eigen::Matrix2cd q1lb; + Eigen::Matrix2cd q1ra; + Eigen::Matrix2cd q1rb; + Eigen::Matrix2cd q2l; + Eigen::Matrix2cd q2r; }; } // namespace mlir::qco::decomposition diff --git a/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h index f6e9b67334..c06ba815c2 100644 --- a/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h @@ -41,8 +41,8 @@ class EulerDecomposition { * given euler basis. */ [[nodiscard]] static OneQubitGateSequence - generateCircuit(EulerBasis targetBasis, const matrix2x2& unitaryMatrix, - bool simplify, std::optional atol) { + generateCircuit(EulerBasis targetBasis, const Eigen::Matrix2cd& unitaryMatrix, + bool simplify, std::optional atol) { auto [theta, phi, lambda, phase] = anglesFromUnitary(unitaryMatrix, targetBasis); @@ -70,8 +70,8 @@ class EulerDecomposition { * * @return array containing (theta, phi, lambda, phase) in this order */ - static std::array anglesFromUnitary(const matrix2x2& matrix, - EulerBasis basis) { + static std::array anglesFromUnitary(const Eigen::Matrix2cd& matrix, + EulerBasis basis) { if (basis == EulerBasis::XYX) { return paramsXyxInner(matrix); } @@ -88,7 +88,7 @@ class EulerDecomposition { } private: - static std::array paramsZyzInner(const matrix2x2& matrix) { + static std::array paramsZyzInner(const Eigen::Matrix2cd& matrix) { const auto detArg = std::arg(matrix.determinant()); const auto phase = 0.5 * detArg; const auto theta = @@ -100,21 +100,17 @@ class EulerDecomposition { return {theta, phi, lam, phase}; } - static std::array paramsZxzInner(const matrix2x2& matrix) { + static std::array paramsZxzInner(const Eigen::Matrix2cd& matrix) { const auto [theta, phi, lam, phase] = paramsZyzInner(matrix); return {theta, phi + (qc::PI / 2.), lam - (qc::PI / 2.), phase}; } - static std::array paramsXyxInner(const matrix2x2& matrix) { - const matrix2x2 matZyz{ - {static_cast(0.5) * - (matrix(0, 0) + matrix(0, 1) + matrix(1, 0) + matrix(1, 1)), - static_cast(0.5) * - (matrix(0, 0) - matrix(0, 1) + matrix(1, 0) - matrix(1, 1))}, - {static_cast(0.5) * - (matrix(0, 0) + matrix(0, 1) - matrix(1, 0) - matrix(1, 1)), - static_cast(0.5) * - (matrix(0, 0) - matrix(0, 1) - matrix(1, 0) + matrix(1, 1))}, + static std::array paramsXyxInner(const Eigen::Matrix2cd& matrix) { + const Eigen::Matrix2cd matZyz{ + {0.5 * (matrix(0, 0) + matrix(0, 1) + matrix(1, 0) + matrix(1, 1)), + 0.5 * (matrix(0, 0) - matrix(0, 1) + matrix(1, 0) - matrix(1, 1))}, + {0.5 * (matrix(0, 0) + matrix(0, 1) - matrix(1, 0) - matrix(1, 1)), + 0.5 * (matrix(0, 0) - matrix(0, 1) - matrix(1, 0) + matrix(1, 1))}, }; auto [theta, phi, lam, phase] = paramsZyzInner(matZyz); auto newPhi = helpers::mod2pi(phi + qc::PI, 0.); @@ -127,11 +123,11 @@ class EulerDecomposition { }; } - static std::array paramsXzxInner(const matrix2x2& matrix) { + static std::array paramsXzxInner(const Eigen::Matrix2cd& matrix) { auto det = matrix.determinant(); auto phase = std::imag(std::log(det)) / 2.0; auto sqrtDet = std::sqrt(det); - const matrix2x2 matZyz{ + const Eigen::Matrix2cd matZyz{ { {(matrix(0, 0) / sqrtDet).real(), (matrix(1, 0) / sqrtDet).imag()}, {(matrix(1, 0) / sqrtDet).real(), (matrix(0, 0) / sqrtDet).imag()}, @@ -159,9 +155,10 @@ class EulerDecomposition { * indicating that they have been altered from the originals. */ [[nodiscard]] static OneQubitGateSequence - decomposeKAK(fp theta, fp phi, fp lambda, fp phase, qc::OpType kGate, - qc::OpType aGate, bool simplify, std::optional atol) { - fp angleZeroEpsilon = atol.value_or(DEFAULT_ATOL); + decomposeKAK(double theta, double phi, double lambda, double phase, + qc::OpType kGate, qc::OpType aGate, bool simplify, + std::optional atol) { + double angleZeroEpsilon = atol.value_or(DEFAULT_ATOL); if (!simplify) { // setting atol to negative value to make all angle checks true; this will // effectively disable the simplification since all rotations appear to be diff --git a/mlir/include/mlir/Passes/Decomposition/Gate.h b/mlir/include/mlir/Passes/Decomposition/Gate.h index df0fabde3d..c7af09fbd8 100644 --- a/mlir/include/mlir/Passes/Decomposition/Gate.h +++ b/mlir/include/mlir/Passes/Decomposition/Gate.h @@ -10,7 +10,6 @@ #pragma once -#include "Helpers.h" #include "ir/operations/OpType.hpp" #include @@ -25,7 +24,7 @@ using QubitId = std::size_t; */ struct Gate { qc::OpType type{qc::I}; - llvm::SmallVector parameter; + llvm::SmallVector parameter; llvm::SmallVector qubitId = {0}; }; diff --git a/mlir/include/mlir/Passes/Decomposition/GateSequence.h b/mlir/include/mlir/Passes/Decomposition/GateSequence.h index 1f10d53482..5e071cc45a 100644 --- a/mlir/include/mlir/Passes/Decomposition/GateSequence.h +++ b/mlir/include/mlir/Passes/Decomposition/GateSequence.h @@ -44,7 +44,7 @@ struct QubitGateSequence { /** * Global phase adjustment required for the sequence. */ - fp globalPhase{}; + double globalPhase{}; /** * @return true if the global phase adjustment is not zero. */ @@ -73,8 +73,8 @@ struct QubitGateSequence { /** * Calculate overall unitary matrix of the sequence. */ - [[nodiscard]] matrix4x4 getUnitaryMatrix() const { - matrix4x4 unitaryMatrix = matrix4x4::Identity(); + [[nodiscard]] Eigen::Matrix4cd getUnitaryMatrix() const { + Eigen::Matrix4cd unitaryMatrix = Eigen::Matrix4cd::Identity(); for (auto&& gate : gates) { auto gateMatrix = getTwoQubitMatrix(gate); unitaryMatrix = gateMatrix * unitaryMatrix; diff --git a/mlir/include/mlir/Passes/Decomposition/Helpers.h b/mlir/include/mlir/Passes/Decomposition/Helpers.h index 419d1de0a2..0f85f79e3e 100644 --- a/mlir/include/mlir/Passes/Decomposition/Helpers.h +++ b/mlir/include/mlir/Passes/Decomposition/Helpers.h @@ -28,21 +28,11 @@ #include // NOLINT(misc-include-cleaner) namespace mlir::qco { -using fp = qc::fp; -using qfp = std::complex; -// NOLINTBEGIN(misc-include-cleaner) -using matrix2x2 = Eigen::Matrix2; -using matrix4x4 = Eigen::Matrix4; -using rmatrix4x4 = Eigen::Matrix4; -using diagonal4x4 = Eigen::Vector; -using rdiagonal4x4 = Eigen::Vector; -// NOLINTEND(misc-include-cleaner) - -constexpr qfp C_ZERO{0., 0.}; -constexpr qfp C_ONE{1., 0.}; -constexpr qfp C_M_ONE{-1., 0.}; -constexpr qfp C_IM{0., 1.}; -constexpr qfp C_M_IM{0., -1.}; +constexpr std::complex C_ZERO{0., 0.}; +constexpr std::complex C_ONE{1., 0.}; +constexpr std::complex C_M_ONE{-1., 0.}; +constexpr std::complex C_IM{0., 1.}; +constexpr std::complex C_M_IM{0., -1.}; } // namespace mlir::qco @@ -72,19 +62,19 @@ template template [[nodiscard]] bool isUnitaryMatrix(const Eigen::Matrix& matrix, - fp tolerance = 1e-12) { + double tolerance = 1e-12) { return (matrix.transpose().conjugate() * matrix).isIdentity(tolerance); } // NOLINTEND(misc-include-cleaner) -[[nodiscard]] inline fp remEuclid(fp a, fp b) { +[[nodiscard]] inline double remEuclid(double a, double b) { auto r = std::fmod(a, b); return (r < 0.0) ? r + std::abs(b) : r; } // Wrap angle into interval [-π,π). If within atol of the endpoint, clamp // to -π -[[nodiscard]] inline fp mod2pi(fp angle, fp angleZeroEpsilon = 1e-13) { +[[nodiscard]] inline double mod2pi(double angle, double angleZeroEpsilon = 1e-13) { // remEuclid() isn't exactly the same as Python's % operator, but // because the RHS here is a constant and positive it is effectively // equivalent for this case @@ -95,7 +85,7 @@ template return wrapped; } -[[nodiscard]] inline fp traceToFidelity(const qfp& x) { +[[nodiscard]] inline double traceToFidelity(const std::complex& x) { auto xAbs = std::abs(x); return (4.0 + xAbs * xAbs) / 20.0; } diff --git a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index eaddfcc32a..1492a91764 100644 --- a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -18,24 +18,24 @@ namespace mlir::qco::decomposition { -inline constexpr auto SQRT2 = - static_cast(1.414213562373095048801688724209698079L); -inline constexpr auto FRAC1_SQRT2 = static_cast( - 0.707106781186547524400844362104849039284835937688474036588L); - -[[nodiscard]] constexpr matrix2x2 uMatrix(const fp lambda, const fp phi, - const fp theta) { - return matrix2x2{{{{std::cos(theta / 2.), 0.}, - {-std::cos(lambda) * std::sin(theta / 2.), - -std::sin(lambda) * std::sin(theta / 2.)}}, - {{std::cos(phi) * std::sin(theta / 2.), - std::sin(phi) * std::sin(theta / 2.)}, - {std::cos(lambda + phi) * std::cos(theta / 2.), - std::sin(lambda + phi) * std::cos(theta / 2.)}}}}; +inline constexpr double SQRT2 = 1.414213562373095048801688724209698079L; +inline constexpr double FRAC1_SQRT2 = + 0.707106781186547524400844362104849039284835937688474036588L; + +[[nodiscard]] constexpr Eigen::Matrix2cd +uMatrix(const double lambda, const double phi, const double theta) { + return Eigen::Matrix2cd{{{{std::cos(theta / 2.), 0.}, + {-std::cos(lambda) * std::sin(theta / 2.), + -std::sin(lambda) * std::sin(theta / 2.)}}, + {{std::cos(phi) * std::sin(theta / 2.), + std::sin(phi) * std::sin(theta / 2.)}, + {std::cos(lambda + phi) * std::cos(theta / 2.), + std::sin(lambda + phi) * std::cos(theta / 2.)}}}}; } -[[nodiscard]] constexpr matrix2x2 u2Matrix(const fp lambda, const fp phi) { - return matrix2x2{ +[[nodiscard]] constexpr Eigen::Matrix2cd u2Matrix(const double lambda, + const double phi) { + return Eigen::Matrix2cd{ {FRAC1_SQRT2, {-std::cos(lambda) * FRAC1_SQRT2, -std::sin(lambda) * FRAC1_SQRT2}}, {{std::cos(phi) * FRAC1_SQRT2, std::sin(phi) * FRAC1_SQRT2}, @@ -43,79 +43,83 @@ inline constexpr auto FRAC1_SQRT2 = static_cast( std::sin(lambda + phi) * FRAC1_SQRT2}}}; } -[[nodiscard]] constexpr matrix2x2 rxMatrix(fp theta) { +[[nodiscard]] constexpr Eigen::Matrix2cd rxMatrix(double theta) { auto halfTheta = theta / 2.; - auto cos = qfp(std::cos(halfTheta), 0.); - auto isin = qfp(0., -std::sin(halfTheta)); - return matrix2x2{{cos, isin}, {isin, cos}}; + auto cos = std::complex{std::cos(halfTheta), 0.}; + auto isin = std::complex{0., -std::sin(halfTheta)}; + return Eigen::Matrix2cd{{cos, isin}, {isin, cos}}; } -[[nodiscard]] constexpr matrix2x2 ryMatrix(fp theta) { +[[nodiscard]] constexpr Eigen::Matrix2cd ryMatrix(double theta) { auto halfTheta = theta / 2.; - auto cos = qfp(std::cos(halfTheta), 0.); - auto sin = qfp(std::sin(halfTheta), 0.); - return matrix2x2{{cos, -sin}, {sin, cos}}; + auto cos = std::complex{std::cos(halfTheta), 0.}; + auto sin = std::complex{std::sin(halfTheta), 0.}; + return Eigen::Matrix2cd{{cos, -sin}, {sin, cos}}; } -[[nodiscard]] constexpr matrix2x2 rzMatrix(fp theta) { - return matrix2x2{{qfp{std::cos(theta / 2.), -std::sin(theta / 2.)}, 0}, - {0, qfp{std::cos(theta / 2.), std::sin(theta / 2.)}}}; +[[nodiscard]] constexpr Eigen::Matrix2cd rzMatrix(double theta) { + return Eigen::Matrix2cd{ + {std::complex{std::cos(theta / 2.), -std::sin(theta / 2.)}, 0}, + {0, std::complex{std::cos(theta / 2.), std::sin(theta / 2.)}}}; } -[[nodiscard]] constexpr matrix4x4 rxxMatrix(const fp theta) { +[[nodiscard]] constexpr Eigen::Matrix4cd rxxMatrix(const double theta) { const auto cosTheta = std::cos(theta / 2.); const auto sinTheta = std::sin(theta / 2.); - return matrix4x4{{cosTheta, C_ZERO, C_ZERO, {0., -sinTheta}}, - {C_ZERO, cosTheta, {0., -sinTheta}, C_ZERO}, - {C_ZERO, {0., -sinTheta}, cosTheta, C_ZERO}, - {{0., -sinTheta}, C_ZERO, C_ZERO, cosTheta}}; + return Eigen::Matrix4cd{{cosTheta, C_ZERO, C_ZERO, {0., -sinTheta}}, + {C_ZERO, cosTheta, {0., -sinTheta}, C_ZERO}, + {C_ZERO, {0., -sinTheta}, cosTheta, C_ZERO}, + {{0., -sinTheta}, C_ZERO, C_ZERO, cosTheta}}; } -[[nodiscard]] constexpr matrix4x4 ryyMatrix(const fp theta) { +[[nodiscard]] constexpr Eigen::Matrix4cd ryyMatrix(const double theta) { const auto cosTheta = std::cos(theta / 2.); const auto sinTheta = std::sin(theta / 2.); - return matrix4x4{{{cosTheta, 0, 0, {0., sinTheta}}, - {0, cosTheta, {0., -sinTheta}, 0}, - {0, {0., -sinTheta}, cosTheta, 0}, - {{0., sinTheta}, 0, 0, cosTheta}}}; + return Eigen::Matrix4cd{{{cosTheta, 0, 0, {0., sinTheta}}, + {0, cosTheta, {0., -sinTheta}, 0}, + {0, {0., -sinTheta}, cosTheta, 0}, + {{0., sinTheta}, 0, 0, cosTheta}}}; } -[[nodiscard]] constexpr matrix4x4 rzzMatrix(const fp theta) { +[[nodiscard]] constexpr Eigen::Matrix4cd rzzMatrix(const double theta) { const auto cosTheta = std::cos(theta / 2.); const auto sinTheta = std::sin(theta / 2.); - return matrix4x4{{qfp{cosTheta, -sinTheta}, C_ZERO, C_ZERO, C_ZERO}, - {C_ZERO, {cosTheta, sinTheta}, C_ZERO, C_ZERO}, - {C_ZERO, C_ZERO, {cosTheta, sinTheta}, C_ZERO}, - {C_ZERO, C_ZERO, C_ZERO, {cosTheta, -sinTheta}}}; + return Eigen::Matrix4cd{ + {std::complex{cosTheta, -sinTheta}, C_ZERO, C_ZERO, C_ZERO}, + {C_ZERO, {cosTheta, sinTheta}, C_ZERO, C_ZERO}, + {C_ZERO, C_ZERO, {cosTheta, sinTheta}, C_ZERO}, + {C_ZERO, C_ZERO, C_ZERO, {cosTheta, -sinTheta}}}; } -[[nodiscard]] constexpr matrix2x2 pMatrix(const fp lambda) { - return matrix2x2{{1, 0}, {0, {std::cos(lambda), std::sin(lambda)}}}; +[[nodiscard]] constexpr Eigen::Matrix2cd pMatrix(const double lambda) { + return Eigen::Matrix2cd{{1, 0}, {0, {std::cos(lambda), std::sin(lambda)}}}; } -constexpr matrix4x4 SWAP_GATE{ +constexpr Eigen::Matrix4cd SWAP_GATE{ {1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}; -constexpr matrix2x2 H_GATE{{1.0 / SQRT2, 1.0 / SQRT2}, - {1.0 / SQRT2, -1.0 / SQRT2}}; -constexpr matrix2x2 IPZ{{C_IM, C_ZERO}, {C_ZERO, C_M_IM}}; -constexpr matrix2x2 IPY{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; -constexpr matrix2x2 IPX{{C_ZERO, C_IM}, {C_IM, C_ZERO}}; - -[[nodiscard]] inline matrix4x4 -expandToTwoQubits(const matrix2x2& singleQubitMatrix, QubitId qubitId) { +constexpr Eigen::Matrix2cd H_GATE{{1.0 / SQRT2, 1.0 / SQRT2}, + {1.0 / SQRT2, -1.0 / SQRT2}}; +constexpr Eigen::Matrix2cd IPZ{{C_IM, C_ZERO}, {C_ZERO, C_M_IM}}; +constexpr Eigen::Matrix2cd IPY{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; +constexpr Eigen::Matrix2cd IPX{{C_ZERO, C_IM}, {C_IM, C_ZERO}}; + +[[nodiscard]] inline Eigen::Matrix4cd +expandToTwoQubits(const Eigen::Matrix2cd& singleQubitMatrix, QubitId qubitId) { if (qubitId == 0) { - return Eigen::kroneckerProduct(matrix2x2::Identity(), singleQubitMatrix); + return Eigen::kroneckerProduct(Eigen::Matrix2cd::Identity(), + singleQubitMatrix); } if (qubitId == 1) { - return Eigen::kroneckerProduct(singleQubitMatrix, matrix2x2::Identity()); + return Eigen::kroneckerProduct(singleQubitMatrix, + Eigen::Matrix2cd::Identity()); } throw std::invalid_argument{"Invalid qubit id for single-qubit expansion"}; } -[[nodiscard]] inline matrix4x4 -fixTwoQubitMatrixQubitOrder(const matrix4x4& twoQubitMatrix, +[[nodiscard]] inline Eigen::Matrix4cd +fixTwoQubitMatrixQubitOrder(const Eigen::Matrix4cd& twoQubitMatrix, const llvm::SmallVector& qubitIds) { if (qubitIds == llvm::SmallVector{1, 0}) { // since UnitaryOpInterface::getUnitaryMatrix() does have a static @@ -128,10 +132,11 @@ fixTwoQubitMatrixQubitOrder(const matrix4x4& twoQubitMatrix, throw std::invalid_argument{"Invalid qubit IDs for fixing two-qubit matrix"}; } -[[nodiscard]] inline matrix2x2 getSingleQubitMatrix(const Gate& gate) { +[[nodiscard]] inline Eigen::Matrix2cd getSingleQubitMatrix(const Gate& gate) { if (gate.type == qc::SX) { - return matrix2x2{{qfp{0.5, 0.5}, qfp{0.5, -0.5}}, - {qfp{0.5, -0.5}, qfp{0.5, 0.5}}}; + return Eigen::Matrix2cd{ + {std::complex{0.5, 0.5}, std::complex{0.5, -0.5}}, + {std::complex{0.5, -0.5}, std::complex{0.5, 0.5}}}; } if (gate.type == qc::RX) { return rxMatrix(gate.parameter[0]); @@ -143,10 +148,10 @@ fixTwoQubitMatrixQubitOrder(const matrix4x4& twoQubitMatrix, return rzMatrix(gate.parameter[0]); } if (gate.type == qc::X) { - return matrix2x2{{0, 1}, {1, 0}}; + return Eigen::Matrix2cd{{0, 1}, {1, 0}}; } if (gate.type == qc::I) { - return matrix2x2::Identity(); + return Eigen::Matrix2cd::Identity(); } if (gate.type == qc::P) { return pMatrix(gate.parameter[0]); @@ -166,9 +171,9 @@ fixTwoQubitMatrixQubitOrder(const matrix4x4& twoQubitMatrix, } // TODO: remove? only used for verification of circuit and in unittests -[[nodiscard]] inline matrix4x4 getTwoQubitMatrix(const Gate& gate) { +[[nodiscard]] inline Eigen::Matrix4cd getTwoQubitMatrix(const Gate& gate) { if (gate.qubitId.empty()) { - return matrix4x4::Identity(); + return Eigen::Matrix4cd::Identity(); } if (gate.qubitId.size() == 1) { return expandToTwoQubits(getSingleQubitMatrix(gate), gate.qubitId[0]); @@ -177,11 +182,11 @@ fixTwoQubitMatrixQubitOrder(const matrix4x4& twoQubitMatrix, if (gate.type == qc::X) { // controlled X (CX) if (gate.qubitId == llvm::SmallVector{0, 1}) { - return matrix4x4{ + return Eigen::Matrix4cd{ {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}}; } if (gate.qubitId == llvm::SmallVector{1, 0}) { - return matrix4x4{ + return Eigen::Matrix4cd{ {1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}, {0, 1, 0, 0}}; } } @@ -195,7 +200,7 @@ fixTwoQubitMatrixQubitOrder(const matrix4x4& twoQubitMatrix, return rzzMatrix(gate.parameter[0]); } if (gate.type == qc::I) { - return matrix4x4::Identity(); + return Eigen::Matrix4cd::Identity(); } throw std::invalid_argument{"unsupported gate type for two qubit matrix (" + qc::toString(gate.type) + ")"}; diff --git a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index 09f09b612b..2f44f7989d 100644 --- a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -44,7 +44,7 @@ namespace mlir::qco::decomposition { * Allowed deviation for internal assert statements which ensure the correctness * of the decompositions. */ -constexpr fp SANITY_CHECK_PRECISION = 1e-12; +constexpr double SANITY_CHECK_PRECISION = 1e-12; /** * Weyl decomposition of a 2-qubit unitary matrix (4x4). @@ -71,26 +71,26 @@ struct TwoQubitWeylDecomposition { }; // a, b, c are the parameters of the canonical gate (CAN) - fp a; // rotation of RXX gate in CAN (must be taken times -2.0) - fp b; // rotation of RYY gate in CAN (must be taken times -2.0) - fp c; // rotation of RZZ gate in CAN (must be taken times -2.0) - fp globalPhase; // global phase adjustment + double a; // rotation of RXX gate in CAN (must be taken times -2.0) + double b; // rotation of RYY gate in CAN (must be taken times -2.0) + double c; // rotation of RZZ gate in CAN (must be taken times -2.0) + double globalPhase; // global phase adjustment /** * q1 - k2r - C - k1r - * A * q0 - k2l - N - k1l - */ - matrix2x2 k1l; // "left" qubit after canonical gate - matrix2x2 k2l; // "left" qubit before canonical gate - matrix2x2 k1r; // "right" qubit after canonical gate - matrix2x2 k2r; // "right" qubit before canonical gate - Specialization specialization; // detected symmetries in the matrix - EulerBasis defaultEulerBasis; // recommended euler basis for k1l/k2l/k1r/k2r - std::optional requestedFidelity; // desired fidelity; - // if set to std::nullopt, no automatic - // specialization will be applied - fp calculatedFidelity; // actual fidelity of decomposition - matrix4x4 unitaryMatrix; // original matrix for this decomposition + Eigen::Matrix2cd k1l; // "left" qubit after canonical gate + Eigen::Matrix2cd k2l; // "left" qubit before canonical gate + Eigen::Matrix2cd k1r; // "right" qubit after canonical gate + Eigen::Matrix2cd k2r; // "right" qubit before canonical gate + Specialization specialization; // detected symmetries in the matrix + EulerBasis defaultEulerBasis; // recommended euler basis for k1l/k2l/k1r/k2r + std::optional // desired fidelity; + requestedFidelity; // if set to std::nullopt, no automatic + // specialization will be applied + double calculatedFidelity; // actual fidelity of decomposition + Eigen::Matrix4cd unitaryMatrix; // original matrix for this decomposition /** * Create Weyl decomposition. @@ -102,11 +102,11 @@ struct TwoQubitWeylDecomposition { * gate and thus potentially decreasing the number of basis * gates. */ - static TwoQubitWeylDecomposition create(const matrix4x4& unitaryMatrix, - std::optional fidelity) { + static TwoQubitWeylDecomposition create(const Eigen::Matrix4cd& unitaryMatrix, + std::optional fidelity) { auto u = unitaryMatrix; auto detU = u.determinant(); - auto detPow = std::pow(detU, static_cast(-0.25)); + auto detPow = std::pow(detU, static_cast(-0.25)); u *= detPow; // remove global phase from unitary matrix auto globalPhase = std::arg(detU) / 4.; @@ -118,7 +118,7 @@ struct TwoQubitWeylDecomposition { // 2. magic basis diagonalizes canonical gate, allowing calculation of // canonical gate parameters later on auto uP = magicBasisTransform(u, MagicBasisTransform::OutOf); - const matrix4x4 m2 = uP.transpose() * uP; + const Eigen::Matrix4cd m2 = uP.transpose() * uP; // diagonalization yields eigenvectors (p) and eigenvalues (d); // p is used to calculate K1/K2 (and thus the single-qubit gates @@ -129,8 +129,8 @@ struct TwoQubitWeylDecomposition { // extract Weyl coordinates from eigenvalues, map to [0, 2*pi) // NOLINTNEXTLINE(misc-include-cleaner) - Eigen::Vector cs; - rdiagonal4x4 dReal = -1.0 * d.cwiseArg() / 2.0; + Eigen::Vector3d cs; + Eigen::Vector4d dReal = -1.0 * d.cwiseArg() / 2.0; dReal(3) = -dReal(0) - dReal(1) - dReal(2); for (int i = 0; i < static_cast(cs.size()); ++i) { assert(i < dReal.size()); @@ -156,7 +156,7 @@ struct TwoQubitWeylDecomposition { // update eigenvectors (columns of p) according to new order of // weyl coordinates - matrix4x4 pOrig = p; + Eigen::Matrix4cd pOrig = p; for (int i = 0; std::cmp_less(i, order.size()); ++i) { p.col(i) = pOrig.col(order[i]); } @@ -170,20 +170,20 @@ struct TwoQubitWeylDecomposition { // re-create complex eigenvalue matrix; this matrix contains the // parameters of the canonical gate which is later used in the // verification - matrix4x4 temp = dReal.asDiagonal(); + Eigen::Matrix4cd temp = dReal.asDiagonal(); temp *= C_IM; // since the matrix is diagonal, matrix exponental is equivalent to // element-wise exponential function temp.diagonal() = temp.diagonal().array().exp().matrix(); // combined matrix k1 of 1q gates after canonical gate - matrix4x4 k1 = uP * p * temp; + Eigen::Matrix4cd k1 = uP * p * temp; assert((k1.transpose() * k1).isIdentity()); // k1 must be orthogonal assert(k1.determinant().real() > 0.0); k1 = magicBasisTransform(k1, MagicBasisTransform::Into); // combined matrix k2 of 1q gates before canonical gate - matrix4x4 k2 = p.transpose().conjugate(); + Eigen::Matrix4cd k2 = p.transpose().conjugate(); assert((k2.transpose() * k2).isIdentity()); // k2 must be orthogonal assert(k2.determinant().real() > 0.0); k2 = magicBasisTransform(k2, MagicBasisTransform::Into); @@ -327,7 +327,7 @@ struct TwoQubitWeylDecomposition { /** * Calculate matrix of canonical gate based on its parameters a, b, c. */ - [[nodiscard]] matrix4x4 getCanonicalMatrix() const { + [[nodiscard]] Eigen::Matrix4cd getCanonicalMatrix() const { auto xx = getTwoQubitMatrix({ .type = qc::RXX, .parameter = {-2.0 * a}, @@ -352,20 +352,20 @@ struct TwoQubitWeylDecomposition { OutOf, }; - static matrix4x4 magicBasisTransform(const matrix4x4& unitary, - MagicBasisTransform direction) { - const matrix4x4 bNonNormalized{ + static Eigen::Matrix4cd magicBasisTransform(const Eigen::Matrix4cd& unitary, + MagicBasisTransform direction) { + const Eigen::Matrix4cd bNonNormalized{ {C_ONE, C_IM, C_ZERO, C_ZERO}, {C_ZERO, C_ZERO, C_IM, C_ONE}, {C_ZERO, C_ZERO, C_IM, C_M_ONE}, {C_ONE, C_M_IM, C_ZERO, C_ZERO}, }; - const matrix4x4 bNonNormalizedDagger{ - {qfp(0.5, 0.), C_ZERO, C_ZERO, qfp(0.5, 0.)}, - {qfp(0., -0.5), C_ZERO, C_ZERO, qfp(0., 0.5)}, - {C_ZERO, qfp(0., -0.5), qfp(0., -0.5), C_ZERO}, - {C_ZERO, qfp(0.5, 0.), qfp(-0.5, 0.), C_ZERO}, + const Eigen::Matrix4cd bNonNormalizedDagger{ + {{0.5, 0.}, C_ZERO, C_ZERO, {0.5, 0.}}, + {{0., -0.5}, C_ZERO, C_ZERO, {0., 0.5}}, + {C_ZERO, {0., -0.5}, {0., -0.5}, C_ZERO}, + {C_ZERO, {0.5, 0.}, {-0.5, 0.}, C_ZERO}, }; if (direction == MagicBasisTransform::OutOf) { return bNonNormalizedDagger * unitary * bNonNormalized; @@ -376,7 +376,7 @@ struct TwoQubitWeylDecomposition { throw std::logic_error{"Unknown MagicBasisTransform direction!"}; } - static fp closestPartialSwap(fp a, fp b, fp c) { + static double closestPartialSwap(double a, double b, double c) { auto m = (a + b + c) / 3.; auto [am, bm, cm] = std::array{a - m, b - m, c - m}; auto [ab, bc, ca] = std::array{a - b, b - c, c - a}; @@ -397,8 +397,8 @@ struct TwoQubitWeylDecomposition { * * @return pair of (P, D.diagonal()) */ - [[nodiscard]] static std::pair - diagonalizeComplexSymmetric(const matrix4x4& m, fp precision) { + [[nodiscard]] static std::pair + diagonalizeComplexSymmetric(const Eigen::Matrix4cd& m, double precision) { // We can't use raw `eig` directly because it isn't guaranteed to give // us real or orthogonal eigenvectors. Instead, since `M` is // complex-symmetric, @@ -411,11 +411,11 @@ struct TwoQubitWeylDecomposition { // a little bit. The fixed seed is to make failures deterministic; the // value is not important. auto state = std::mt19937{2023}; - std::normal_distribution dist; + std::normal_distribution dist; for (int i = 0; i < 100; ++i) { - fp randA{}; - fp randB{}; + double randA{}; + double randB{}; // For debugging the algorithm use the same RNG values as the // Qiskit implementation for the first random trial. // In most cases this loop only executes a single iteration and @@ -428,14 +428,12 @@ struct TwoQubitWeylDecomposition { randA = dist(state); randB = dist(state); } - const rmatrix4x4 m2Real = randA * m.real() + randB * m.imag(); - const rmatrix4x4 pReal = helpers::selfAdjointEvd(m2Real).first; - const matrix4x4 p = pReal; - const diagonal4x4 d = (p.transpose() * m * p).diagonal(); + const Eigen::Matrix4d m2Real = randA * m.real() + randB * m.imag(); + auto&& pReal = helpers::selfAdjointEvd(m2Real).first; + const Eigen::Matrix4cd p = pReal; + auto&& d = (p.transpose() * m * p).diagonal(); - const matrix4x4 diagD = d.asDiagonal(); - - const matrix4x4 compare = p * diagD * p.transpose(); + auto&& compare = p * d.asDiagonal() * p.transpose(); if (compare.isApprox(m, precision)) { // p are the eigenvectors which are decomposed into the // single-qubit gates surrounding the canonical gate @@ -444,7 +442,7 @@ struct TwoQubitWeylDecomposition { // check that p is in SO(4) assert((p.transpose() * p).isIdentity(SANITY_CHECK_PRECISION)); // make sure determinant of eigenvalues is 1.0 - assert(std::abs(matrix4x4{d.asDiagonal()}.determinant() - 1.0) < + assert(std::abs(Eigen::Matrix4cd{d.asDiagonal()}.determinant() - 1.0) < SANITY_CHECK_PRECISION); return std::make_pair(p, d); } @@ -464,20 +462,20 @@ struct TwoQubitWeylDecomposition { * @return single-qubit matrices A and B and the required * global phase adjustment */ - static std::tuple - decomposeTwoQubitProductGate(const matrix4x4& specialUnitary) { + static std::tuple + decomposeTwoQubitProductGate(const Eigen::Matrix4cd& specialUnitary) { // for alternative approaches, see // pennylane's math.decomposition.su2su2_to_tensor_products // or quantumflow.kronecker_decomposition // first quadrant - matrix2x2 r{{specialUnitary(0, 0), specialUnitary(0, 1)}, - {specialUnitary(1, 0), specialUnitary(1, 1)}}; + Eigen::Matrix2cd r{{specialUnitary(0, 0), specialUnitary(0, 1)}, + {specialUnitary(1, 0), specialUnitary(1, 1)}}; auto detR = r.determinant(); if (std::abs(detR) < 0.1) { // third quadrant - r = matrix2x2{{specialUnitary(2, 0), specialUnitary(2, 1)}, - {specialUnitary(3, 0), specialUnitary(3, 1)}}; + r = Eigen::Matrix2cd{{specialUnitary(2, 0), specialUnitary(2, 1)}, + {specialUnitary(3, 0), specialUnitary(3, 1)}}; detR = r.determinant(); } if (std::abs(detR) < 0.1) { @@ -486,16 +484,17 @@ struct TwoQubitWeylDecomposition { } r /= std::sqrt(detR); // transpose with complex conjugate of each element - const matrix2x2 rTConj = r.transpose().conjugate(); + const Eigen::Matrix2cd rTConj = r.transpose().conjugate(); - matrix4x4 temp = Eigen::kroneckerProduct(matrix2x2::Identity(), rTConj); + Eigen::Matrix4cd temp = + Eigen::kroneckerProduct(Eigen::Matrix2cd::Identity(), rTConj); temp = specialUnitary * temp; // [[a, b, c, d], // [e, f, g, h], => [[a, c], // [i, j, k, l], [i, k]] // [m, n, o, p]] - matrix2x2 l{{temp(0, 0), temp(0, 2)}, {temp(2, 0), temp(2, 2)}}; + Eigen::Matrix2cd l{{temp(0, 0), temp(0, 2)}, {temp(2, 0), temp(2, 2)}}; auto detL = l.determinant(); if (std::abs(detL) < 0.9) { throw std::runtime_error{ @@ -511,13 +510,14 @@ struct TwoQubitWeylDecomposition { * Calculate trace of two sets of parameters for the canonical gate. * The trace has been defined in: https://arxiv.org/abs/1811.12926 */ - [[nodiscard]] static qfp getTrace(fp a, fp b, fp c, fp ap, fp bp, fp cp) { + [[nodiscard]] static std::complex + getTrace(double a, double b, double c, double ap, double bp, double cp) { auto da = a - ap; auto db = b - bp; auto dc = c - cp; - return static_cast(4.) * - qfp(std::cos(da) * std::cos(db) * std::cos(dc), - std::sin(da) * std::sin(db) * std::sin(dc)); + return static_cast(4.) * + std::complex{std::cos(da) * std::cos(db) * std::cos(dc), + std::sin(da) * std::sin(db) * std::sin(dc)}; } /** @@ -526,7 +526,7 @@ struct TwoQubitWeylDecomposition { * close enough to the actual canonical gate matrix. */ [[nodiscard]] Specialization bestSpecialization() const { - auto isClose = [this](fp ap, fp bp, fp cp) -> bool { + auto isClose = [this](double ap, double bp, double cp) -> bool { auto tr = getTrace(a, b, c, ap, bp, cp); if (requestedFidelity) { return helpers::traceToFidelity(tr) >= *requestedFidelity; @@ -599,9 +599,9 @@ struct TwoQubitWeylDecomposition { c = 0.; // unmodified global phase k1l = k1l * k2l; - k2l = matrix2x2::Identity(); + k2l = Eigen::Matrix2cd::Identity(); k1r = k1r * k2r; - k2r = matrix2x2::Identity(); + k2r = Eigen::Matrix2cd::Identity(); } else if (newSpecialization == Specialization::SWAPEquiv) { // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, -\pi/4)` // Thus, :math:`U \sim \text{SWAP}` @@ -613,16 +613,16 @@ struct TwoQubitWeylDecomposition { // unmodified global phase k1l = k1l * k2r; k1r = k1r * k2l; - k2l = matrix2x2::Identity(); - k2r = matrix2x2::Identity(); + k2l = Eigen::Matrix2cd::Identity(); + k2r = Eigen::Matrix2cd::Identity(); } else { flippedFromOriginal = true; globalPhase += qc::PI_2; k1l = k1l * IPZ * k2r; k1r = k1r * IPZ * k2l; - k2l = matrix2x2::Identity(); - k2r = matrix2x2::Identity(); + k2l = Eigen::Matrix2cd::Identity(); + k2r = Eigen::Matrix2cd::Identity(); } a = qc::PI_4; b = qc::PI_4; @@ -644,7 +644,7 @@ struct TwoQubitWeylDecomposition { k1l = k1l * k2l; k1r = k1r * k2l; k2r = k2lDagger * k2r; - k2l = matrix2x2::Identity(); + k2l = Eigen::Matrix2cd::Identity(); } else if (newSpecialization == Specialization::PartialSWAPFlipEquiv) { // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4)` // Thus, :math:`U \sim \text{SWAP}^\alpha` @@ -665,7 +665,7 @@ struct TwoQubitWeylDecomposition { k1l = k1l * k2l; k1r = k1r * IPZ * k2l * IPZ; k2r = IPZ * k2lDagger * IPZ * k2r; - k2l = matrix2x2::Identity(); + k2l = Eigen::Matrix2cd::Identity(); } else if (newSpecialization == Specialization::ControlledEquiv) { // :math:`U \sim U_d(\alpha, 0, 0)` // Thus, :math:`U \sim \text{Ctrl-U}` diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index aa86483bf3..235f3f0f56 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -263,8 +263,8 @@ struct GateDecompositionPattern final } for (std::size_t i = 0; i < result.outQubits.size(); ++i) { - if (auto user = - getUser(result.outQubits[i], &UnitaryOpInterface::isTwoQubit)) { + if (auto user = getUser(result.outQubits[i], + &UnitaryOpInterface::isTwoQubit)) { foundGate = result.appendTwoQubitGate(*user); break; // go back to single-qubit collection } @@ -273,10 +273,11 @@ struct GateDecompositionPattern final return result; } - [[nodiscard]] std::optional getSingleQubitUnitaryMatrix() { - matrix2x2 unitaryMatrix = matrix2x2::Identity(); + [[nodiscard]] std::optional + getSingleQubitUnitaryMatrix() { + Eigen::Matrix2cd unitaryMatrix = Eigen::Matrix2cd::Identity(); for (auto&& gate : gates) { - if (auto gateMatrix = gate.op.getUnitaryMatrix()) { + if (auto gateMatrix = gate.op.getUnitaryMatrix()) { unitaryMatrix = *gateMatrix * unitaryMatrix; } else { return std::nullopt; @@ -287,20 +288,20 @@ struct GateDecompositionPattern final return unitaryMatrix; } - [[nodiscard]] std::optional getUnitaryMatrix() { - matrix4x4 unitaryMatrix = matrix4x4::Identity(); - matrix4x4 gateMatrix; + [[nodiscard]] std::optional getUnitaryMatrix() { + Eigen::Matrix4cd unitaryMatrix = Eigen::Matrix4cd::Identity(); + Eigen::Matrix4cd gateMatrix; for (auto&& gate : gates) { if (gate.op.isSingleQubit()) { assert(gate.qubitIds.size() == 1); - auto matrix = gate.op.getUnitaryMatrix(); + auto matrix = gate.op.getUnitaryMatrix(); if (!matrix) { return std::nullopt; } gateMatrix = decomposition::expandToTwoQubits(*matrix, gate.qubitIds[0]); } else if (gate.op.isTwoQubit()) { - if (auto matrix = gate.op.getUnitaryMatrix()) { + if (auto matrix = gate.op.getUnitaryMatrix()) { gateMatrix = decomposition::fixTwoQubitMatrixQubitOrder( *matrix, gate.qubitIds); } else { @@ -458,8 +459,7 @@ struct GateDecompositionPattern final }; while (auto* op = inQubits[qubitId].getDefiningOp()) { auto unitaryOp = mlir::dyn_cast(op); - if (unitaryOp && unitaryOp.isSingleQubit() && - !isBarrier(unitaryOp)) { + if (unitaryOp && unitaryOp.isSingleQubit() && !isBarrier(unitaryOp)) { prependSingleQubitGate(unitaryOp); } else { break; @@ -543,7 +543,7 @@ struct GateDecompositionPattern final createGate(rewriter, location, sequence.globalPhase); } - matrix4x4 unitaryMatrix = matrix4x4::Identity(); + Eigen::Matrix4cd unitaryMatrix = Eigen::Matrix4cd::Identity(); for (auto&& gate : sequence.gates) { // TODO: need to add each basis gate we want to use if (gate.type == qc::X && gate.qubitId.size() > 1) { @@ -588,8 +588,9 @@ struct GateDecompositionPattern final } } assert((unitaryMatrix * std::exp(C_IM * sequence.globalPhase)) - .isApprox(series.getUnitaryMatrix().value_or(matrix4x4::Zero()), - SANITY_CHECK_PRECISION)); + .isApprox( + series.getUnitaryMatrix().value_or(Eigen::Matrix4cd::Zero()), + SANITY_CHECK_PRECISION)); if (series.isSingleQubitSeries()) { rewriter.replaceAllUsesWith(series.outQubits[0], inQubits[0]); diff --git a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp index 2cdb9cfe8b..e016a8126b 100644 --- a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp +++ b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp @@ -33,22 +33,22 @@ using namespace mlir::qco; using namespace mlir::qco::decomposition; namespace { -[[nodiscard]] matrix4x4 randomUnitaryMatrix() { +[[nodiscard]] Eigen::Matrix4cd randomUnitaryMatrix() { [[maybe_unused]] static auto initializeRandom = []() { // Eigen uses std::rand() internally, use fixed seed for deterministic // testing behavior std::srand(123456UL); return true; }(); - const matrix4x4 randomMatrix = matrix4x4::Random(); - Eigen::HouseholderQR qr{}; // NOLINT(misc-include-cleaner) + const Eigen::Matrix4cd randomMatrix = Eigen::Matrix4cd::Random(); + Eigen::HouseholderQR qr{}; // NOLINT(misc-include-cleaner) qr.compute(randomMatrix); - const matrix4x4 unitaryMatrix = qr.householderQ(); + const Eigen::Matrix4cd unitaryMatrix = qr.householderQ(); assert(helpers::isUnitaryMatrix(unitaryMatrix)); return unitaryMatrix; } -[[nodiscard]] matrix4x4 canonicalGate(fp a, fp b, fp c) { +[[nodiscard]] Eigen::Matrix4cd canonicalGate(double a, double b, double c) { TwoQubitWeylDecomposition tmp{}; tmp.a = a; tmp.b = b; @@ -59,7 +59,7 @@ namespace { class BasisDecomposerTest : public testing::TestWithParam< - std::tuple, matrix4x4>> { + std::tuple, Eigen::Matrix4cd>> { public: void SetUp() override { basisGate = std::get<0>(GetParam()); @@ -68,8 +68,8 @@ class BasisDecomposerTest targetDecomposition = TwoQubitWeylDecomposition::create(target, 1.0); } - [[nodiscard]] static matrix4x4 restore(const TwoQubitGateSequence& sequence) { - matrix4x4 matrix = matrix4x4::Identity(); + [[nodiscard]] static Eigen::Matrix4cd restore(const TwoQubitGateSequence& sequence) { + Eigen::Matrix4cd matrix = Eigen::Matrix4cd::Identity(); for (auto&& gate : sequence.gates) { matrix = getTwoQubitMatrix(gate) * matrix; } @@ -79,7 +79,7 @@ class BasisDecomposerTest } protected: - matrix4x4 target; + Eigen::Matrix4cd target; Gate basisGate; llvm::SmallVector eulerBases; TwoQubitWeylDecomposition targetDecomposition; @@ -150,7 +150,7 @@ TEST(BasisDecomposerTest, Random) { TEST(BasisDecomposerTest, Crash) { using namespace std::complex_literals; - matrix4x4 originalMatrix{{-0.23104450537689214 + -0.44268488901708902i, + Eigen::Matrix4cd originalMatrix{{-0.23104450537689214 + -0.44268488901708902i, -0.60656504798621003 + -0.27756198294119977i, -0.2168858251642842 + -0.27845819247692827i, -0.42430958159720128 + 0.032705758031399738i}, @@ -202,9 +202,9 @@ INSTANTIATE_TEST_CASE_P( EulerBasis::XZX}, llvm::SmallVector{EulerBasis::XZX}), // targets to be decomposed - testing::Values(matrix4x4::Identity(), + testing::Values(Eigen::Matrix4cd::Identity(), Eigen::kroneckerProduct(rzMatrix(1.0), ryMatrix(3.1)), - Eigen::kroneckerProduct(matrix2x2::Identity(), + Eigen::kroneckerProduct(Eigen::Matrix2cd::Identity(), rxMatrix(0.1))))); INSTANTIATE_TEST_CASE_P( @@ -224,10 +224,10 @@ INSTANTIATE_TEST_CASE_P( ::testing::Values( rzzMatrix(2.0), ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0), canonicalGate(1.5, -0.2, 0.0) * - Eigen::kroneckerProduct(rxMatrix(1.0), matrix2x2::Identity()), + Eigen::kroneckerProduct(rxMatrix(1.0), Eigen::Matrix2cd::Identity()), Eigen::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * canonicalGate(1.1, 0.2, 3.0) * - Eigen::kroneckerProduct(rxMatrix(1.0), matrix2x2::Identity()), + Eigen::kroneckerProduct(rxMatrix(1.0), Eigen::Matrix2cd::Identity()), Eigen::kroneckerProduct(H_GATE, IPZ) * getTwoQubitMatrix( {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * diff --git a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp index cbfc50ac9d..1c2f1a3570 100644 --- a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp @@ -29,27 +29,27 @@ using namespace mlir::qco; using namespace mlir::qco::decomposition; namespace { -[[nodiscard]] matrix2x2 randomUnitaryMatrix() { +[[nodiscard]] Eigen::Matrix2cd randomUnitaryMatrix() { [[maybe_unused]] static auto initializeRandom = []() { // Eigen uses std::rand() internally, use fixed seed for deterministic // testing behavior std::srand(123456UL); return true; }(); - const matrix2x2 randomMatrix = matrix2x2::Random(); - Eigen::HouseholderQR qr{}; // NOLINT(misc-include-cleaner) + const Eigen::Matrix2cd randomMatrix = Eigen::Matrix2cd::Random(); + Eigen::HouseholderQR qr{}; // NOLINT(misc-include-cleaner) qr.compute(randomMatrix); - const matrix2x2 unitaryMatrix = qr.householderQ(); + const Eigen::Matrix2cd unitaryMatrix = qr.householderQ(); assert(helpers::isUnitaryMatrix(unitaryMatrix)); return unitaryMatrix; } } // namespace class EulerDecompositionTest - : public testing::TestWithParam> { + : public testing::TestWithParam> { public: - [[nodiscard]] static matrix2x2 restore(const TwoQubitGateSequence& sequence) { - matrix2x2 matrix = matrix2x2::Identity(); + [[nodiscard]] static Eigen::Matrix2cd restore(const TwoQubitGateSequence& sequence) { + Eigen::Matrix2cd matrix = Eigen::Matrix2cd::Identity(); for (auto&& gate : sequence.gates) { matrix = getSingleQubitMatrix(gate) * matrix; } @@ -64,7 +64,7 @@ class EulerDecompositionTest } protected: - matrix2x2 originalMatrix; + Eigen::Matrix2cd originalMatrix; EulerBasis eulerBasis{}; }; @@ -107,5 +107,5 @@ INSTANTIATE_TEST_CASE_P( SingleQubitMatrices, EulerDecompositionTest, testing::Combine(testing::Values(EulerBasis::XYX, EulerBasis::XZX, EulerBasis::ZYZ, EulerBasis::ZXZ), - testing::Values(matrix2x2::Identity(), ryMatrix(2.0), + testing::Values(Eigen::Matrix2cd::Identity(), ryMatrix(2.0), rxMatrix(0.5), rzMatrix(3.14), H_GATE))); diff --git a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp index e33a5b604d..fd9e75524f 100644 --- a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp @@ -26,22 +26,22 @@ using namespace mlir::qco; using namespace mlir::qco::decomposition; namespace { -[[nodiscard]] matrix4x4 randomUnitaryMatrix() { +[[nodiscard]] Eigen::Matrix4cd randomUnitaryMatrix() { [[maybe_unused]] static auto initializeRandom = []() { // Eigen uses std::rand() internally, use fixed seed for deterministic // testing behavior std::srand(123456UL); return true; }(); - const matrix4x4 randomMatrix = matrix4x4::Random(); - Eigen::HouseholderQR qr{}; // NOLINT(misc-include-cleaner) + const Eigen::Matrix4cd randomMatrix = Eigen::Matrix4cd::Random(); + Eigen::HouseholderQR qr{}; // NOLINT(misc-include-cleaner) qr.compute(randomMatrix); - const matrix4x4 unitaryMatrix = qr.householderQ(); + const Eigen::Matrix4cd unitaryMatrix = qr.householderQ(); assert(helpers::isUnitaryMatrix(unitaryMatrix)); return unitaryMatrix; } -[[nodiscard]] matrix4x4 canonicalGate(fp a, fp b, fp c) { +[[nodiscard]] Eigen::Matrix4cd canonicalGate(double a, double b, double c) { TwoQubitWeylDecomposition tmp{}; tmp.a = a; tmp.b = b; @@ -50,27 +50,27 @@ namespace { } } // namespace -class WeylDecompositionTest : public testing::TestWithParam { +class WeylDecompositionTest : public testing::TestWithParam { public: - [[nodiscard]] static matrix4x4 + [[nodiscard]] static Eigen::Matrix4cd restore(const TwoQubitWeylDecomposition& decomposition) { return k1(decomposition) * can(decomposition) * k2(decomposition) * globalPhaseFactor(decomposition); } - [[nodiscard]] static qfp + [[nodiscard]] static std::complex globalPhaseFactor(const TwoQubitWeylDecomposition& decomposition) { return std::exp(C_IM * decomposition.globalPhase); } - [[nodiscard]] static matrix4x4 + [[nodiscard]] static Eigen::Matrix4cd can(const TwoQubitWeylDecomposition& decomposition) { return decomposition.getCanonicalMatrix(); } - [[nodiscard]] static matrix4x4 + [[nodiscard]] static Eigen::Matrix4cd k1(const TwoQubitWeylDecomposition& decomposition) { return Eigen::kroneckerProduct(decomposition.k1l, decomposition.k1r); } - [[nodiscard]] static matrix4x4 + [[nodiscard]] static Eigen::Matrix4cd k2(const TwoQubitWeylDecomposition& decomposition) { return Eigen::kroneckerProduct(decomposition.k2l, decomposition.k2r); } @@ -120,24 +120,27 @@ TEST(WeylDecompositionTest, Random) { INSTANTIATE_TEST_CASE_P( SingleQubitMatrices, WeylDecompositionTest, - ::testing::Values(matrix4x4::Identity(), + ::testing::Values(Eigen::Matrix4cd::Identity(), Eigen::kroneckerProduct(rzMatrix(1.0), ryMatrix(3.1)), - Eigen::kroneckerProduct(matrix2x2::Identity(), + Eigen::kroneckerProduct(Eigen::Matrix2cd::Identity(), rxMatrix(0.1)))); INSTANTIATE_TEST_CASE_P( TwoQubitMatrices, WeylDecompositionTest, - ::testing::Values( - rzzMatrix(2.0), ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0), - canonicalGate(1.5, -0.2, 0.0) * - Eigen::kroneckerProduct(rxMatrix(1.0), matrix2x2::Identity()), - Eigen::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * - canonicalGate(1.1, 0.2, 3.0) * - Eigen::kroneckerProduct(rxMatrix(1.0), matrix2x2::Identity()), - Eigen::kroneckerProduct(H_GATE, IPZ) * - getTwoQubitMatrix( - {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * - Eigen::kroneckerProduct(IPX, IPY))); + ::testing::Values(rzzMatrix(2.0), + ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0), + canonicalGate(1.5, -0.2, 0.0) * + Eigen::kroneckerProduct(rxMatrix(1.0), + Eigen::Matrix2cd::Identity()), + Eigen::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * + canonicalGate(1.1, 0.2, 3.0) * + Eigen::kroneckerProduct(rxMatrix(1.0), + Eigen::Matrix2cd::Identity()), + Eigen::kroneckerProduct(H_GATE, IPZ) * + getTwoQubitMatrix({.type = qc::X, + .parameter = {}, + .qubitId = {0, 1}}) * + Eigen::kroneckerProduct(IPX, IPY))); INSTANTIATE_TEST_CASE_P( SpecializedMatrices, WeylDecompositionTest, From 69e989f3c5ca3db5a1f48a2e54fb10197edcf584 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 6 Feb 2026 17:23:09 +0100 Subject: [PATCH 195/237] remove constants and other stuff --- .../Passes/Decomposition/BasisDecomposer.h | 47 ++++++++----------- .../mlir/Passes/Decomposition/GateSequence.h | 2 +- .../mlir/Passes/Decomposition/Helpers.h | 17 +++---- .../Passes/Decomposition/UnitaryMatrices.h | 32 ++++++------- .../Passes/Decomposition/WeylDecomposition.h | 30 ++++++------ .../Patterns/GateDecompositionPattern.cpp | 4 +- .../Decomposition/test_basis_decomposer.cpp | 44 +++++++++-------- .../test_euler_decomposition.cpp | 11 +++-- .../Decomposition/test_weyl_decomposition.cpp | 2 +- 9 files changed, 91 insertions(+), 98 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index ad43ece213..5259fc9c54 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -57,6 +57,8 @@ class TwoQubitBasisDecomposer { */ static TwoQubitBasisDecomposer create(const Gate& basisGate, double basisFidelity) { + using namespace std::complex_literals; + auto relativeEq = [](auto&& lhs, auto&& rhs, auto&& epsilon, auto&& maxRelative) { // Handle same infinities @@ -84,8 +86,8 @@ class TwoQubitBasisDecomposer { return absDiff <= absLhs * maxRelative; }; const Eigen::Matrix2cd k12RArr{ - {{0., FRAC1_SQRT2}, {FRAC1_SQRT2, 0.}}, - {{-FRAC1_SQRT2, 0.}, {0., -FRAC1_SQRT2}}, + {1i * FRAC1_SQRT2, FRAC1_SQRT2}, + {-FRAC1_SQRT2, -1i * FRAC1_SQRT2}, }; const Eigen::Matrix2cd k12LArr{ {{0.5, 0.5}, {0.5, 0.5}}, @@ -104,26 +106,21 @@ class TwoQubitBasisDecomposer { auto b = basisDecomposer.b; std::complex temp{0.5, -0.5}; const Eigen::Matrix2cd k11l{ - {temp * (C_M_IM * std::exp(std::complex{0., -b})), - temp * std::exp(std::complex{0., -b})}, - {temp * (C_M_IM * std::exp(std::complex{0., b})), - temp * -std::exp(std::complex{0., b})}}; - const Eigen::Matrix2cd k11r{ - {FRAC1_SQRT2 * (C_IM * std::exp(std::complex{0., -b})), - FRAC1_SQRT2 * -std::exp(std::complex{0., -b})}, - {FRAC1_SQRT2 * std::exp(std::complex{0., b}), - FRAC1_SQRT2 * (C_M_IM * std::exp(std::complex{0., b}))}}; + {temp * (-1i * std::exp(-1i * b)), temp * std::exp(-1i * b)}, + {temp * (-1i * std::exp(1i * b)), temp * -std::exp(1i * b)}}; + const Eigen::Matrix2cd k11r{{FRAC1_SQRT2 * (1i * std::exp(-1i * b)), + FRAC1_SQRT2 * -std::exp(-1i * b)}, + {FRAC1_SQRT2 * std::exp(1i * b), + FRAC1_SQRT2 * (-1i * std::exp(1i * b))}}; const Eigen::Matrix2cd k32lK21l{ {FRAC1_SQRT2 * std::complex{1., std::cos(2. * b)}, - FRAC1_SQRT2 * (C_IM * std::sin(2. * b))}, - {FRAC1_SQRT2 * (C_IM * std::sin(2. * b)), + FRAC1_SQRT2 * (1i * std::sin(2. * b))}, + {FRAC1_SQRT2 * (1i * std::sin(2. * b)), FRAC1_SQRT2 * std::complex{1., -std::cos(2. * b)}}}; temp = std::complex{0.5, 0.5}; const Eigen::Matrix2cd k21r{ - {temp * (C_M_IM * std::exp(std::complex{0., -2. * b})), - temp * std::exp(std::complex{0., -2. * b})}, - {temp * (C_IM * std::exp(std::complex{0., 2. * b})), - temp * std::exp(std::complex{0., 2. * b})}, + {temp * (-1i * std::exp(-2i * b)), temp * std::exp(-2i * b)}, + {temp * (1i * std::exp(2i * b)), temp * std::exp(2i * b)}, }; const Eigen::Matrix2cd k22l{ {FRAC1_SQRT2, -FRAC1_SQRT2}, @@ -131,21 +128,17 @@ class TwoQubitBasisDecomposer { }; const Eigen::Matrix2cd k22r{{0, 1}, {-1, 0}}; const Eigen::Matrix2cd k31l{ - {FRAC1_SQRT2 * std::exp(std::complex{0., -b}), - FRAC1_SQRT2 * std::exp(std::complex{0., -b})}, - {FRAC1_SQRT2 * -std::exp(std::complex{0., b}), - FRAC1_SQRT2 * std::exp(std::complex{0., b})}, + {FRAC1_SQRT2 * std::exp(-1i * b), FRAC1_SQRT2 * std::exp(-1i * b)}, + {FRAC1_SQRT2 * -std::exp(1i * b), FRAC1_SQRT2 * std::exp(1i * b)}, }; const Eigen::Matrix2cd k31r{ - {C_IM * std::exp(std::complex{0., b}), C_ZERO}, - {C_ZERO, C_M_IM * std::exp(std::complex{0., -b})}, + {1i * std::exp(1i * b), 0}, + {0, -1i * std::exp(-1i * b)}, }; temp = std::complex{0.5, 0.5}; const Eigen::Matrix2cd k32r{ - {temp * std::exp(std::complex{0., b}), - temp * -std::exp(std::complex{0., -b})}, - {temp * (C_M_IM * std::exp(std::complex{0., b})), - temp * (C_M_IM * std::exp(std::complex{0., -b}))}, + {temp * std::exp(1i * b), temp * -std::exp(-1i * b)}, + {temp * (-1i * std::exp(1i * b)), temp * (-1i * std::exp(-1i * b))}, }; auto k1lDagger = basisDecomposer.k1l.transpose().conjugate(); auto k1rDagger = basisDecomposer.k1r.transpose().conjugate(); diff --git a/mlir/include/mlir/Passes/Decomposition/GateSequence.h b/mlir/include/mlir/Passes/Decomposition/GateSequence.h index 5e071cc45a..f8892f6899 100644 --- a/mlir/include/mlir/Passes/Decomposition/GateSequence.h +++ b/mlir/include/mlir/Passes/Decomposition/GateSequence.h @@ -79,7 +79,7 @@ struct QubitGateSequence { auto gateMatrix = getTwoQubitMatrix(gate); unitaryMatrix = gateMatrix * unitaryMatrix; } - unitaryMatrix *= std::exp(C_IM * globalPhase); + unitaryMatrix *= helpers::globalPhaseFactor(globalPhase); assert(helpers::isUnitaryMatrix(unitaryMatrix)); return unitaryMatrix; } diff --git a/mlir/include/mlir/Passes/Decomposition/Helpers.h b/mlir/include/mlir/Passes/Decomposition/Helpers.h index 0f85f79e3e..14b846eda1 100644 --- a/mlir/include/mlir/Passes/Decomposition/Helpers.h +++ b/mlir/include/mlir/Passes/Decomposition/Helpers.h @@ -27,15 +27,6 @@ #include #include // NOLINT(misc-include-cleaner) -namespace mlir::qco { -constexpr std::complex C_ZERO{0., 0.}; -constexpr std::complex C_ONE{1., 0.}; -constexpr std::complex C_M_ONE{-1., 0.}; -constexpr std::complex C_IM{0., 1.}; -constexpr std::complex C_M_IM{0., -1.}; - -} // namespace mlir::qco - namespace mlir::qco::helpers { [[nodiscard]] inline qc::OpType getQcType(UnitaryOpInterface op) { @@ -74,7 +65,8 @@ template // Wrap angle into interval [-π,π). If within atol of the endpoint, clamp // to -π -[[nodiscard]] inline double mod2pi(double angle, double angleZeroEpsilon = 1e-13) { +[[nodiscard]] inline double mod2pi(double angle, + double angleZeroEpsilon = 1e-13) { // remEuclid() isn't exactly the same as Python's % operator, but // because the RHS here is a constant and positive it is effectively // equivalent for this case @@ -102,4 +94,9 @@ template return 1; } +[[nodiscard]] inline std::complex +globalPhaseFactor(double globalPhase) { + return std::exp(std::complex{0, 1} * globalPhase); +} + } // namespace mlir::qco::helpers diff --git a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index 1492a91764..6d85ced0ee 100644 --- a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -52,25 +52,24 @@ uMatrix(const double lambda, const double phi, const double theta) { [[nodiscard]] constexpr Eigen::Matrix2cd ryMatrix(double theta) { auto halfTheta = theta / 2.; - auto cos = std::complex{std::cos(halfTheta), 0.}; - auto sin = std::complex{std::sin(halfTheta), 0.}; + std::complex cos{std::cos(halfTheta), 0.}; + std::complex sin{std::sin(halfTheta), 0.}; return Eigen::Matrix2cd{{cos, -sin}, {sin, cos}}; } [[nodiscard]] constexpr Eigen::Matrix2cd rzMatrix(double theta) { - return Eigen::Matrix2cd{ - {std::complex{std::cos(theta / 2.), -std::sin(theta / 2.)}, 0}, - {0, std::complex{std::cos(theta / 2.), std::sin(theta / 2.)}}}; + return Eigen::Matrix2cd{{{std::cos(theta / 2.), -std::sin(theta / 2.)}, 0}, + {0, {std::cos(theta / 2.), std::sin(theta / 2.)}}}; } [[nodiscard]] constexpr Eigen::Matrix4cd rxxMatrix(const double theta) { const auto cosTheta = std::cos(theta / 2.); const auto sinTheta = std::sin(theta / 2.); - return Eigen::Matrix4cd{{cosTheta, C_ZERO, C_ZERO, {0., -sinTheta}}, - {C_ZERO, cosTheta, {0., -sinTheta}, C_ZERO}, - {C_ZERO, {0., -sinTheta}, cosTheta, C_ZERO}, - {{0., -sinTheta}, C_ZERO, C_ZERO, cosTheta}}; + return Eigen::Matrix4cd{{cosTheta, 0, 0, {0., -sinTheta}}, + {0, cosTheta, {0., -sinTheta}, 0}, + {0, {0., -sinTheta}, cosTheta, 0}, + {{0., -sinTheta}, 0, 0, cosTheta}}; } [[nodiscard]] constexpr Eigen::Matrix4cd ryyMatrix(const double theta) { @@ -87,11 +86,10 @@ uMatrix(const double lambda, const double phi, const double theta) { const auto cosTheta = std::cos(theta / 2.); const auto sinTheta = std::sin(theta / 2.); - return Eigen::Matrix4cd{ - {std::complex{cosTheta, -sinTheta}, C_ZERO, C_ZERO, C_ZERO}, - {C_ZERO, {cosTheta, sinTheta}, C_ZERO, C_ZERO}, - {C_ZERO, C_ZERO, {cosTheta, sinTheta}, C_ZERO}, - {C_ZERO, C_ZERO, C_ZERO, {cosTheta, -sinTheta}}}; + return Eigen::Matrix4cd{{{cosTheta, -sinTheta}, 0, 0, 0}, + {0, {cosTheta, sinTheta}, 0, 0}, + {0, 0, {cosTheta, sinTheta}, 0}, + {0, 0, 0, {cosTheta, -sinTheta}}}; } [[nodiscard]] constexpr Eigen::Matrix2cd pMatrix(const double lambda) { @@ -101,9 +99,9 @@ constexpr Eigen::Matrix4cd SWAP_GATE{ {1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}; constexpr Eigen::Matrix2cd H_GATE{{1.0 / SQRT2, 1.0 / SQRT2}, {1.0 / SQRT2, -1.0 / SQRT2}}; -constexpr Eigen::Matrix2cd IPZ{{C_IM, C_ZERO}, {C_ZERO, C_M_IM}}; -constexpr Eigen::Matrix2cd IPY{{C_ZERO, C_ONE}, {C_M_ONE, C_ZERO}}; -constexpr Eigen::Matrix2cd IPX{{C_ZERO, C_IM}, {C_IM, C_ZERO}}; +constexpr Eigen::Matrix2cd IPZ{{{0, 1}, 0}, {0, {0, -1}}}; +constexpr Eigen::Matrix2cd IPY{{0, 1}, {-1, 0}}; +constexpr Eigen::Matrix2cd IPX{{0, {0, 1}}, {{0, 1}, 0}}; [[nodiscard]] inline Eigen::Matrix4cd expandToTwoQubits(const Eigen::Matrix2cd& singleQubitMatrix, QubitId qubitId) { diff --git a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index 2f44f7989d..e83f9b0910 100644 --- a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -106,7 +106,7 @@ struct TwoQubitWeylDecomposition { std::optional fidelity) { auto u = unitaryMatrix; auto detU = u.determinant(); - auto detPow = std::pow(detU, static_cast(-0.25)); + auto detPow = std::pow(detU, -0.25); u *= detPow; // remove global phase from unitary matrix auto globalPhase = std::arg(detU) / 4.; @@ -132,7 +132,7 @@ struct TwoQubitWeylDecomposition { Eigen::Vector3d cs; Eigen::Vector4d dReal = -1.0 * d.cwiseArg() / 2.0; dReal(3) = -dReal(0) - dReal(1) - dReal(2); - for (int i = 0; i < static_cast(cs.size()); ++i) { + for (int i = 0; i < cs.size(); ++i) { assert(i < dReal.size()); cs[i] = helpers::remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); } @@ -171,7 +171,7 @@ struct TwoQubitWeylDecomposition { // parameters of the canonical gate which is later used in the // verification Eigen::Matrix4cd temp = dReal.asDiagonal(); - temp *= C_IM; + temp *= std::complex{0, 1}; // since the matrix is diagonal, matrix exponental is equivalent to // element-wise exponential function temp.diagonal() = temp.diagonal().array().exp().matrix(); @@ -281,7 +281,8 @@ struct TwoQubitWeylDecomposition { // make sure decomposition is equal to input assert((Eigen::kroneckerProduct(K1l, K1r) * decomposition.getCanonicalMatrix() * - Eigen::kroneckerProduct(K2l, K2r) * std::exp(C_IM * globalPhase)) + Eigen::kroneckerProduct(K2l, K2r) * + helpers::globalPhaseFactor(globalPhase)) .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); // determine actual specialization of canonical gate so that the 1q @@ -318,7 +319,7 @@ struct TwoQubitWeylDecomposition { assert((Eigen::kroneckerProduct(decomposition.k1l, decomposition.k1r) * decomposition.getCanonicalMatrix() * Eigen::kroneckerProduct(decomposition.k2l, decomposition.k2r) * - std::exp(C_IM * decomposition.globalPhase)) + helpers::globalPhaseFactor(decomposition.globalPhase)) .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); return decomposition; @@ -354,18 +355,19 @@ struct TwoQubitWeylDecomposition { static Eigen::Matrix4cd magicBasisTransform(const Eigen::Matrix4cd& unitary, MagicBasisTransform direction) { + using namespace std::complex_literals; const Eigen::Matrix4cd bNonNormalized{ - {C_ONE, C_IM, C_ZERO, C_ZERO}, - {C_ZERO, C_ZERO, C_IM, C_ONE}, - {C_ZERO, C_ZERO, C_IM, C_M_ONE}, - {C_ONE, C_M_IM, C_ZERO, C_ZERO}, + {1, 1i, 0, 0}, + {0, 0, 1i, 1}, + {0, 0, 1i, -1}, + {1, -1i, 0, 0}, }; const Eigen::Matrix4cd bNonNormalizedDagger{ - {{0.5, 0.}, C_ZERO, C_ZERO, {0.5, 0.}}, - {{0., -0.5}, C_ZERO, C_ZERO, {0., 0.5}}, - {C_ZERO, {0., -0.5}, {0., -0.5}, C_ZERO}, - {C_ZERO, {0.5, 0.}, {-0.5, 0.}, C_ZERO}, + {0.5, 0, 0, 0.5}, + {-0.5i, 0, 0, 0.5i}, + {0, -0.5i, -0.5i, 0}, + {0, 0.5, -0.5, 0}, }; if (direction == MagicBasisTransform::OutOf) { return bNonNormalizedDagger * unitary * bNonNormalized; @@ -515,7 +517,7 @@ struct TwoQubitWeylDecomposition { auto da = a - ap; auto db = b - bp; auto dc = c - cp; - return static_cast(4.) * + return 4. * std::complex{std::cos(da) * std::cos(db) * std::cos(dc), std::sin(da) * std::sin(db) * std::sin(dc)}; } diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 235f3f0f56..57f8f4db2a 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -23,8 +23,6 @@ #include #include #include -#include -#include #include #include #include @@ -587,7 +585,7 @@ struct GateDecompositionPattern final throw std::runtime_error{"Unknown gate type!"}; } } - assert((unitaryMatrix * std::exp(C_IM * sequence.globalPhase)) + assert((unitaryMatrix * helpers::globalPhaseFactor(sequence.globalPhase)) .isApprox( series.getUnitaryMatrix().value_or(Eigen::Matrix4cd::Zero()), SANITY_CHECK_PRECISION)); diff --git a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp index e016a8126b..6c9e9050e6 100644 --- a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp +++ b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp @@ -68,13 +68,14 @@ class BasisDecomposerTest targetDecomposition = TwoQubitWeylDecomposition::create(target, 1.0); } - [[nodiscard]] static Eigen::Matrix4cd restore(const TwoQubitGateSequence& sequence) { + [[nodiscard]] static Eigen::Matrix4cd + restore(const TwoQubitGateSequence& sequence) { Eigen::Matrix4cd matrix = Eigen::Matrix4cd::Identity(); for (auto&& gate : sequence.gates) { matrix = getTwoQubitMatrix(gate) * matrix; } - matrix *= std::exp(C_IM * sequence.globalPhase); + matrix *= helpers::globalPhaseFactor(sequence.globalPhase); return matrix; } @@ -150,22 +151,23 @@ TEST(BasisDecomposerTest, Random) { TEST(BasisDecomposerTest, Crash) { using namespace std::complex_literals; - Eigen::Matrix4cd originalMatrix{{-0.23104450537689214 + -0.44268488901708902i, - -0.60656504798621003 + -0.27756198294119977i, - -0.2168858251642842 + -0.27845819247692827i, - -0.42430958159720128 + 0.032705758031399738i}, - {0.12891961731437976 + -0.2577139933400836i, - 0.059033561840284507 + 0.051774294297249751i, - 0.58205201943239671 + -0.20399736896613216i, - -0.027126130902642431 + 0.72777907642808048i}, - {0.60297884333102469 + 0.35765188950245741i, - -0.59087990913607613 + 0.22062558535485413i, - 0.077633311362340196 + -0.28085102069787549i, - 0.13707024540895657 + -0.083632801340620747i}, - {-0.22282692851707037 + 0.3556143358154254i, - 0.3014101239694616 + 0.24537699657586307i, - 0.10077337125619777 + -0.63242595993554396i, - -0.48930698747658014 + -0.1526088977163027i}}; + Eigen::Matrix4cd originalMatrix{ + {-0.23104450537689214 + -0.44268488901708902i, + -0.60656504798621003 + -0.27756198294119977i, + -0.2168858251642842 + -0.27845819247692827i, + -0.42430958159720128 + 0.032705758031399738i}, + {0.12891961731437976 + -0.2577139933400836i, + 0.059033561840284507 + 0.051774294297249751i, + 0.58205201943239671 + -0.20399736896613216i, + -0.027126130902642431 + 0.72777907642808048i}, + {0.60297884333102469 + 0.35765188950245741i, + -0.59087990913607613 + 0.22062558535485413i, + 0.077633311362340196 + -0.28085102069787549i, + 0.13707024540895657 + -0.083632801340620747i}, + {-0.22282692851707037 + 0.3556143358154254i, + 0.3014101239694616 + 0.24537699657586307i, + 0.10077337125619777 + -0.63242595993554396i, + -0.48930698747658014 + -0.1526088977163027i}}; const Gate basisGate{.type = qc::X, .parameter = {}, .qubitId = {0, 1}}; const llvm::SmallVector eulerBases = {EulerBasis::XYX, @@ -224,10 +226,12 @@ INSTANTIATE_TEST_CASE_P( ::testing::Values( rzzMatrix(2.0), ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0), canonicalGate(1.5, -0.2, 0.0) * - Eigen::kroneckerProduct(rxMatrix(1.0), Eigen::Matrix2cd::Identity()), + Eigen::kroneckerProduct(rxMatrix(1.0), + Eigen::Matrix2cd::Identity()), Eigen::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * canonicalGate(1.1, 0.2, 3.0) * - Eigen::kroneckerProduct(rxMatrix(1.0), Eigen::Matrix2cd::Identity()), + Eigen::kroneckerProduct(rxMatrix(1.0), + Eigen::Matrix2cd::Identity()), Eigen::kroneckerProduct(H_GATE, IPZ) * getTwoQubitMatrix( {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * diff --git a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp index 1c2f1a3570..717df977c1 100644 --- a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -48,13 +47,14 @@ namespace { class EulerDecompositionTest : public testing::TestWithParam> { public: - [[nodiscard]] static Eigen::Matrix2cd restore(const TwoQubitGateSequence& sequence) { + [[nodiscard]] static Eigen::Matrix2cd + restore(const TwoQubitGateSequence& sequence) { Eigen::Matrix2cd matrix = Eigen::Matrix2cd::Identity(); for (auto&& gate : sequence.gates) { matrix = getSingleQubitMatrix(gate) * matrix; } - matrix *= std::exp(C_IM * sequence.globalPhase); + matrix *= helpers::globalPhaseFactor(sequence.globalPhase); return matrix; } @@ -107,5 +107,6 @@ INSTANTIATE_TEST_CASE_P( SingleQubitMatrices, EulerDecompositionTest, testing::Combine(testing::Values(EulerBasis::XYX, EulerBasis::XZX, EulerBasis::ZYZ, EulerBasis::ZXZ), - testing::Values(Eigen::Matrix2cd::Identity(), ryMatrix(2.0), - rxMatrix(0.5), rzMatrix(3.14), H_GATE))); + testing::Values(Eigen::Matrix2cd::Identity(), + ryMatrix(2.0), rxMatrix(0.5), + rzMatrix(3.14), H_GATE))); diff --git a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp index fd9e75524f..a0fc271d98 100644 --- a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp @@ -60,7 +60,7 @@ class WeylDecompositionTest : public testing::TestWithParam { [[nodiscard]] static std::complex globalPhaseFactor(const TwoQubitWeylDecomposition& decomposition) { - return std::exp(C_IM * decomposition.globalPhase); + return helpers::globalPhaseFactor(decomposition.globalPhase); } [[nodiscard]] static Eigen::Matrix4cd can(const TwoQubitWeylDecomposition& decomposition) { From 618f2dc7b3f8a13e2d82b7e92279a2d2b5d301e8 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 6 Feb 2026 17:41:28 +0100 Subject: [PATCH 196/237] fix typo in comment --- mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index e83f9b0910..af09946815 100644 --- a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -172,7 +172,7 @@ struct TwoQubitWeylDecomposition { // verification Eigen::Matrix4cd temp = dReal.asDiagonal(); temp *= std::complex{0, 1}; - // since the matrix is diagonal, matrix exponental is equivalent to + // since the matrix is diagonal, matrix exponential is equivalent to // element-wise exponential function temp.diagonal() = temp.diagonal().array().exp().matrix(); From b8cacc5d075edd33f52194ec78a7d04949657f44 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 6 Feb 2026 19:42:28 +0100 Subject: [PATCH 197/237] replace exceptions by llvm error reporting --- .../Passes/Decomposition/BasisDecomposer.h | 11 +++++++- .../mlir/Passes/Decomposition/EulerBasis.h | 6 ++--- .../Passes/Decomposition/EulerDecomposition.h | 8 +++--- .../Passes/Decomposition/UnitaryMatrices.h | 21 ++++++++------- .../Passes/Decomposition/WeylDecomposition.h | 27 ++++++++++--------- .../Patterns/GateDecompositionPattern.cpp | 12 ++++++--- 6 files changed, 53 insertions(+), 32 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index 5259fc9c54..f50555914e 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -216,6 +217,12 @@ class TwoQubitBasisDecomposer { const llvm::SmallVector& target1qEulerBases, std::optional basisFidelity, bool approximate, std::optional numBasisGateUses) const { + if (target1qEulerBases.empty()) { + llvm::reportFatalUsageError( + "Unable to perform two-qubit basis decomposition without at least " + "one euler basis!"); + } + auto getBasisFidelity = [&]() { if (approximate) { return basisFidelity.value_or(this->basisFidelity); @@ -257,7 +264,9 @@ class TwoQubitBasisDecomposer { if (bestNbasis == 3) { return decomp3Supercontrolled(targetDecomposition); } - throw std::logic_error{"Invalid basis to use"}; + llvm::reportFatalInternalError( + "Invalid number of basis gates to use in basis decomposition (" + + llvm::Twine(bestNbasis) + ")!"); }; auto decomposition = chooseDecomposition(); llvm::SmallVector, 8> diff --git a/mlir/include/mlir/Passes/Decomposition/EulerBasis.h b/mlir/include/mlir/Passes/Decomposition/EulerBasis.h index 8b8ee93f58..01e97896c0 100644 --- a/mlir/include/mlir/Passes/Decomposition/EulerBasis.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerBasis.h @@ -14,7 +14,7 @@ #include #include -#include +#include namespace mlir::qco::decomposition { /** @@ -68,8 +68,8 @@ getGateTypesForEulerBasis(EulerBasis eulerBasis) { case EulerBasis::U1X: return {qc::RZ, qc::P}; } - throw std::invalid_argument{ - "Unsupported euler basis for translation to gate types"}; + llvm::reportFatalInternalError( + "Unsupported euler basis for translation to gate types"); } } // namespace mlir::qco::decomposition diff --git a/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h index c06ba815c2..c21a63b07b 100644 --- a/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h @@ -22,8 +22,8 @@ #include #include #include +#include #include -#include namespace mlir::qco::decomposition { @@ -60,7 +60,8 @@ class EulerDecomposition { return decomposeKAK(theta, phi, lambda, phase, qc::RX, qc::RY, simplify, atol); default: - throw std::invalid_argument{"Unsupported base for circuit generation!"}; + llvm::reportFatalInternalError("Unsupported euler basis for circuit " + "generation in decomposition!"); } } @@ -84,7 +85,8 @@ class EulerDecomposition { if (basis == EulerBasis::ZXZ) { return paramsZxzInner(matrix); } - throw std::invalid_argument{"Unknown EulerBasis for angles_from_unitary"}; + llvm::reportFatalInternalError( + "Unsupported euler basis for angle computation in decomposition!"); } private: diff --git a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index 6d85ced0ee..6943cacff9 100644 --- a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -11,9 +11,9 @@ #pragma once #include "Gate.h" -#include "Helpers.h" #include "ir/operations/OpType.hpp" +#include #include namespace mlir::qco::decomposition { @@ -113,7 +113,7 @@ expandToTwoQubits(const Eigen::Matrix2cd& singleQubitMatrix, QubitId qubitId) { return Eigen::kroneckerProduct(singleQubitMatrix, Eigen::Matrix2cd::Identity()); } - throw std::invalid_argument{"Invalid qubit id for single-qubit expansion"}; + llvm::reportFatalInternalError("Invalid qubit id for single-qubit expansion"); } [[nodiscard]] inline Eigen::Matrix4cd @@ -127,7 +127,8 @@ fixTwoQubitMatrixQubitOrder(const Eigen::Matrix4cd& twoQubitMatrix, if (qubitIds == llvm::SmallVector{0, 1}) { return twoQubitMatrix; } - throw std::invalid_argument{"Invalid qubit IDs for fixing two-qubit matrix"}; + llvm::reportFatalInternalError( + "Invalid qubit IDs for fixing two-qubit matrix"); } [[nodiscard]] inline Eigen::Matrix2cd getSingleQubitMatrix(const Gate& gate) { @@ -163,9 +164,9 @@ fixTwoQubitMatrixQubitOrder(const Eigen::Matrix4cd& twoQubitMatrix, if (gate.type == qc::H) { return H_GATE; } - throw std::invalid_argument{ - "unsupported gate type for single qubit matrix (" + - qc::toString(gate.type) + ")"}; + llvm::reportFatalInternalError( + llvm::StringRef("unsupported gate type for single qubit matrix (" + + qc::toString(gate.type) + ")")); } // TODO: remove? only used for verification of circuit and in unittests @@ -200,10 +201,12 @@ fixTwoQubitMatrixQubitOrder(const Eigen::Matrix4cd& twoQubitMatrix, if (gate.type == qc::I) { return Eigen::Matrix4cd::Identity(); } - throw std::invalid_argument{"unsupported gate type for two qubit matrix (" + - qc::toString(gate.type) + ")"}; + llvm::reportFatalInternalError( + llvm::StringRef("unsupported gate type for two qubit matrix (" + + qc::toString(gate.type) + ")")); } - throw std::logic_error{"Invalid number of qubit IDs in compute_unitary"}; + llvm::reportFatalInternalError( + "Invalid number of qubit IDs in compute_unitary"); } } // namespace mlir::qco::decomposition diff --git a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index af09946815..702387712d 100644 --- a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -308,9 +309,9 @@ struct TwoQubitWeylDecomposition { if (decomposition.requestedFidelity) { if (decomposition.calculatedFidelity + 1.0e-13 < *decomposition.requestedFidelity) { - throw std::runtime_error{ + llvm::reportFatalInternalError( "TwoQubitWeylDecomposition: Calculated fidelity of " - "specialization is worse than requested fidelity!"}; + "specialization is worse than requested fidelity!"); } } decomposition.globalPhase += std::arg(trace); @@ -375,7 +376,7 @@ struct TwoQubitWeylDecomposition { if (direction == MagicBasisTransform::Into) { return bNonNormalized * unitary * bNonNormalizedDagger; } - throw std::logic_error{"Unknown MagicBasisTransform direction!"}; + llvm::reportFatalInternalError("Unknown MagicBasisTransform direction!"); } static double closestPartialSwap(double a, double b, double c) { @@ -449,8 +450,8 @@ struct TwoQubitWeylDecomposition { return std::make_pair(p, d); } } - throw std::runtime_error{ - "TwoQubitWeylDecomposition: failed to diagonalize M2."}; + llvm::reportFatalInternalError( + "TwoQubitWeylDecomposition: failed to diagonalize M2."); } /** @@ -481,8 +482,8 @@ struct TwoQubitWeylDecomposition { detR = r.determinant(); } if (std::abs(detR) < 0.1) { - throw std::runtime_error{ - "decompose_two_qubit_product_gate: unable to decompose: det_r < 0.1"}; + llvm::reportFatalInternalError( + "decompose_two_qubit_product_gate: unable to decompose: det_r < 0.1"); } r /= std::sqrt(detR); // transpose with complex conjugate of each element @@ -499,8 +500,8 @@ struct TwoQubitWeylDecomposition { Eigen::Matrix2cd l{{temp(0, 0), temp(0, 2)}, {temp(2, 0), temp(2, 2)}}; auto detL = l.determinant(); if (std::abs(detL) < 0.9) { - throw std::runtime_error{ - "decompose_two_qubit_product_gate: unable to decompose: detL < 0.9"}; + llvm::reportFatalInternalError( + "decompose_two_qubit_product_gate: unable to decompose: detL < 0.9"); } l /= std::sqrt(detL); auto phase = std::arg(detL) / 2.; @@ -575,8 +576,9 @@ struct TwoQubitWeylDecomposition { */ bool applySpecialization() { if (specialization != Specialization::General) { - throw std::logic_error{"Application of specialization only works on " - "general decomposition!"}; + llvm::reportFatalInternalError( + "Application of specialization only works on " + "general Weyl decompositions!"); } bool flippedFromOriginal = false; auto newSpecialization = bestSpecialization(); @@ -771,7 +773,8 @@ struct TwoQubitWeylDecomposition { k2r = IPZ * rxMatrix(-k2lphi) * IPZ * k2r; defaultEulerBasis = eulerBasis; } else { - throw std::logic_error{"Unknown specialization"}; + llvm::reportFatalInternalError( + "Unknown specialization for Weyl decomposition!"); } return flippedFromOriginal; } diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 57f8f4db2a..b3c34bf61e 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -366,8 +367,8 @@ struct GateDecompositionPattern final // NOLINTNEXTLINE(readability-qualified-auto) auto it = llvm::find(outQubits, operand); if (it == outQubits.end()) { - throw std::logic_error{"Operand of single-qubit op and user of " - "qubit is not in current outQubits"}; + llvm::reportFatalInternalError("Operand of single-qubit op and user of " + "qubit is not in current outQubits"); } QubitId qubitId = std::distance(outQubits.begin(), it); *it = nextGate->getResult(0); @@ -533,7 +534,9 @@ struct GateDecompositionPattern final } else if (qubitIds.size() == 1) { inQubits[qubitIds[0]] = newGate.getOutputQubit(0); } else { - throw std::logic_error{"Invalid number of qubit IDs!"}; + llvm::reportFatalInternalError( + "Invalid number of qubit IDs while trying to apply " + "decomposition result to MLIR!"); } }; @@ -582,7 +585,8 @@ struct GateDecompositionPattern final unitaryMatrix; updateInQubits(gate.qubitId, newGate); } else { - throw std::runtime_error{"Unknown gate type!"}; + llvm::reportFatalInternalError("Unsupported gate type in decomposition " + "while applying result to MLIR!"); } } assert((unitaryMatrix * helpers::globalPhaseFactor(sequence.globalPhase)) From f50760dd1ce3cc7cb7c720ba4d5fe0e742833586 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 6 Feb 2026 19:42:48 +0100 Subject: [PATCH 198/237] some coderabbitai comments and complexity caching --- .../Passes/Decomposition/BasisDecomposer.h | 21 +++++----- .../Passes/Decomposition/EulerDecomposition.h | 4 +- .../mlir/Passes/Decomposition/GateSequence.h | 3 -- .../Patterns/GateDecompositionPattern.cpp | 40 +++++++++---------- 4 files changed, 32 insertions(+), 36 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index f50555914e..c4c9707a31 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -234,7 +234,7 @@ class TwoQubitBasisDecomposer { auto getDefaultNbasis = [&]() { // determine smallest number of basis gates required to fulfill given // basis fidelity constraint - auto bestValue = std::numeric_limits::min(); + auto bestValue = std::numeric_limits::lowest(); auto bestIndex = -1; for (int i = 0; std::cmp_less(i, traces.size()); ++i) { // lower basis fidelity means it becomes easier to use fewer basis gates @@ -273,8 +273,8 @@ class TwoQubitBasisDecomposer { eulerDecompositions; for (auto&& decomp : decomposition) { assert(helpers::isUnitaryMatrix(decomp)); - auto eulerDecomp = unitaryToGateSequenceInner(decomp, target1qEulerBases, - 0, true, std::nullopt); + auto eulerDecomp = unitaryToGateSequence(decomp, target1qEulerBases, 0, + true, std::nullopt); eulerDecompositions.push_back(eulerDecomp); } TwoQubitGateSequence gates{ @@ -495,12 +495,15 @@ class TwoQubitBasisDecomposer { * sequence. Multiple euler bases may be specified and the one with the * least complexity will be chosen. */ - [[nodiscard]] static OneQubitGateSequence unitaryToGateSequenceInner( - const Eigen::Matrix2cd& unitaryMat, - const llvm::SmallVector& targetBasisList, QubitId /*qubit*/, - // TODO: add error map here: per qubit a mapping of operation to error - // value for better calculateError() - bool simplify, std::optional atol) { + [[nodiscard]] static OneQubitGateSequence + unitaryToGateSequence(const Eigen::Matrix2cd& unitaryMat, + const llvm::SmallVector& targetBasisList, + QubitId /*qubit*/, + // TODO: add error map here: per qubit a mapping of + // operation to error value for better calculateError() + bool simplify, std::optional atol) { + assert(!targetBasisList.empty()); + auto calculateError = [](const OneQubitGateSequence& sequence) -> double { return static_cast(sequence.complexity()); }; diff --git a/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h index c21a63b07b..735696b5ce 100644 --- a/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h @@ -129,7 +129,7 @@ class EulerDecomposition { auto det = matrix.determinant(); auto phase = std::imag(std::log(det)) / 2.0; auto sqrtDet = std::sqrt(det); - const Eigen::Matrix2cd matZyz{ + const Eigen::Matrix2cd matZxz{ { {(matrix(0, 0) / sqrtDet).real(), (matrix(1, 0) / sqrtDet).imag()}, {(matrix(1, 0) / sqrtDet).real(), (matrix(0, 0) / sqrtDet).imag()}, @@ -139,7 +139,7 @@ class EulerDecomposition { {(matrix(0, 0) / sqrtDet).real(), -(matrix(1, 0) / sqrtDet).imag()}, }, }; - auto [theta, phi, lam, phase_zxz] = paramsZxzInner(matZyz); + auto [theta, phi, lam, phase_zxz] = paramsZxzInner(matZxz); return {theta, phi, lam, phase + phase_zxz}; } diff --git a/mlir/include/mlir/Passes/Decomposition/GateSequence.h b/mlir/include/mlir/Passes/Decomposition/GateSequence.h index f8892f6899..ec5c2ea4c6 100644 --- a/mlir/include/mlir/Passes/Decomposition/GateSequence.h +++ b/mlir/include/mlir/Passes/Decomposition/GateSequence.h @@ -56,9 +56,6 @@ struct QubitGateSequence { * Calculate complexity of sequence according to getComplexity(). */ [[nodiscard]] std::size_t complexity() const { - // TODO: add more sophisticated metric to determine complexity of - // series/sequence - // TODO: caching mechanism std::size_t c{}; for (auto&& gate : gates) { c += helpers::getComplexity(gate.type, gate.qubitId.size()); diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index b3c34bf61e..d8c71a35f6 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -117,7 +117,9 @@ struct GateDecompositionPattern final return mlir::failure(); } - std::optional bestSequence; + // sequence and cached complexity to avoid repeated recomputations + auto bestSequence = std::make_pair(decomposition::TwoQubitGateSequence{}, + std::numeric_limits::max()); if (series.isSingleQubitSeries()) { // only a single-qubit series; @@ -130,9 +132,10 @@ struct GateDecompositionPattern final for (auto&& eulerBasis : decomposerEulerBases) { auto sequence = decomposition::EulerDecomposition::generateCircuit( eulerBasis, *unitaryMatrix, true, std::nullopt); - if (!bestSequence || - sequence.complexity() < bestSequence->complexity()) { - bestSequence = sequence; + + auto newComplexity = sequence.complexity(); + if (newComplexity < bestSequence.second) { + bestSequence = std::make_pair(sequence, newComplexity); } } } else { @@ -152,37 +155,30 @@ struct GateDecompositionPattern final std::nullopt); if (sequence) { // decomposition successful - if (!bestSequence || - sequence->complexity() < bestSequence->complexity()) { + auto newComplexity = sequence->complexity(); + if (newComplexity < bestSequence.second) { // this decomposition is better than any successful decomposition // before - bestSequence = sequence; + bestSequence = std::make_pair(*sequence, newComplexity); } } } } - llvm::errs() << "Found series (" << series.complexity << "): "; - for (auto&& gate : series.gates) { - llvm::errs() << gate.op->getName().stripDialect().str() << ", "; - } - - if (!bestSequence) { + if (bestSequence.second == std::numeric_limits::max()) { + // unable to decompose series return mlir::failure(); } - llvm::errs() << "\nDecomposition (" << bestSequence->complexity() << "): "; - for (auto&& gate : bestSequence->gates) { - llvm::errs() << qc::toString(gate.type) << ", "; - } - llvm::errs() << "\n"; - // only accept new sequence if it shortens existing series by more than two - // gates; this prevents an oscillation with phase gates - if (bestSequence->complexity() + 2 >= series.complexity && + if (bestSequence.second >= series.complexity && !(forceApplication && containsForeignGates)) { + // decomposition is longer/more complex than input series; result will + // always be used (even if more complex) if forceApplication is set and + // the input series contained at least one gate unavailable for the + // decomposition return mlir::failure(); } - applySeries(rewriter, series, *bestSequence); + applySeries(rewriter, series, bestSequence.first); return mlir::success(); } From 4d1f561b1ff495213f803ceb43304a9fa4215558 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 6 Feb 2026 20:26:29 +0100 Subject: [PATCH 199/237] remove crash test --- .../Decomposition/test_basis_decomposer.cpp | 41 ------------------- 1 file changed, 41 deletions(-) diff --git a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp index 6c9e9050e6..6c7f2633dc 100644 --- a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp +++ b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp @@ -149,47 +149,6 @@ TEST(BasisDecomposerTest, Random) { std::cerr << "Iterations: " << iterations << '\n'; } -TEST(BasisDecomposerTest, Crash) { - using namespace std::complex_literals; - Eigen::Matrix4cd originalMatrix{ - {-0.23104450537689214 + -0.44268488901708902i, - -0.60656504798621003 + -0.27756198294119977i, - -0.2168858251642842 + -0.27845819247692827i, - -0.42430958159720128 + 0.032705758031399738i}, - {0.12891961731437976 + -0.2577139933400836i, - 0.059033561840284507 + 0.051774294297249751i, - 0.58205201943239671 + -0.20399736896613216i, - -0.027126130902642431 + 0.72777907642808048i}, - {0.60297884333102469 + 0.35765188950245741i, - -0.59087990913607613 + 0.22062558535485413i, - 0.077633311362340196 + -0.28085102069787549i, - 0.13707024540895657 + -0.083632801340620747i}, - {-0.22282692851707037 + 0.3556143358154254i, - 0.3014101239694616 + 0.24537699657586307i, - 0.10077337125619777 + -0.63242595993554396i, - -0.48930698747658014 + -0.1526088977163027i}}; - - const Gate basisGate{.type = qc::X, .parameter = {}, .qubitId = {0, 1}}; - const llvm::SmallVector eulerBases = {EulerBasis::XYX, - EulerBasis::ZXZ}; - - auto targetDecomposition = - TwoQubitWeylDecomposition::create(originalMatrix, 1.0); - auto decomposer = TwoQubitBasisDecomposer::create(basisGate, 1.0); - auto decomposedSequence = decomposer.twoQubitDecompose( - targetDecomposition, eulerBases, 1.0, true, std::nullopt); - - ASSERT_TRUE(decomposedSequence.has_value()); - - auto restoredMatrix = BasisDecomposerTest::restore(*decomposedSequence); - - EXPECT_TRUE(restoredMatrix.isApprox(originalMatrix)) - << "ORIGINAL:\n" - << originalMatrix << '\n' - << "RESULT:\n" - << restoredMatrix << '\n'; -} - INSTANTIATE_TEST_CASE_P( SingleQubitMatrices, BasisDecomposerTest, testing::Combine( From 9e1cab94b086dd3a29e2a81c956418348b7ccbeb Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 6 Feb 2026 20:27:26 +0100 Subject: [PATCH 200/237] make Weyl decomposition non-trivial class; some other fixes --- .../Passes/Decomposition/BasisDecomposer.h | 74 ++-- .../mlir/Passes/Decomposition/Helpers.h | 2 +- .../Passes/Decomposition/UnitaryMatrices.h | 26 +- .../Passes/Decomposition/WeylDecomposition.h | 397 +++++++++++------- .../Decomposition/test_basis_decomposer.cpp | 22 +- .../Decomposition/test_weyl_decomposition.cpp | 52 +-- 6 files changed, 322 insertions(+), 251 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index c4c9707a31..51b79b32de 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -99,12 +99,12 @@ class TwoQubitBasisDecomposer { decomposition::TwoQubitWeylDecomposition::create( getTwoQubitMatrix(basisGate), basisFidelity); const auto isSuperControlled = - relativeEq(basisDecomposer.a, qc::PI_4, 1e-13, 1e-09) && - relativeEq(basisDecomposer.c, 0.0, 1e-13, 1e-09); + relativeEq(basisDecomposer.a(), qc::PI_4, 1e-13, 1e-09) && + relativeEq(basisDecomposer.c(), 0.0, 1e-13, 1e-09); // Create some useful matrices U1, U2, U3 are equivalent to the basis, // expand as Ui = Ki1.Ubasis.Ki2 - auto b = basisDecomposer.b; + auto b = basisDecomposer.b(); std::complex temp{0.5, -0.5}; const Eigen::Matrix2cd k11l{ {temp * (-1i * std::exp(-1i * b)), temp * std::exp(-1i * b)}, @@ -141,10 +141,10 @@ class TwoQubitBasisDecomposer { {temp * std::exp(1i * b), temp * -std::exp(-1i * b)}, {temp * (-1i * std::exp(1i * b)), temp * (-1i * std::exp(-1i * b))}, }; - auto k1lDagger = basisDecomposer.k1l.transpose().conjugate(); - auto k1rDagger = basisDecomposer.k1r.transpose().conjugate(); - auto k2lDagger = basisDecomposer.k2l.transpose().conjugate(); - auto k2rDagger = basisDecomposer.k2r.transpose().conjugate(); + auto k1lDagger = basisDecomposer.k1l().transpose().conjugate(); + auto k1rDagger = basisDecomposer.k1r().transpose().conjugate(); + auto k2lDagger = basisDecomposer.k2l().transpose().conjugate(); + auto k2rDagger = basisDecomposer.k2r().transpose().conjugate(); // Pre-build the fixed parts of the matrices used in 3-part // decomposition auto u0l = k31l * k1lDagger; @@ -279,7 +279,7 @@ class TwoQubitBasisDecomposer { } TwoQubitGateSequence gates{ .gates = {}, - .globalPhase = targetDecomposition.globalPhase, + .globalPhase = targetDecomposition.globalPhase(), }; // Worst case length is 5x 1q gates for each 1q decomposition + 1x 2q // gate We might overallocate a bit if the euler basis is different but @@ -289,7 +289,7 @@ class TwoQubitBasisDecomposer { // QuantumCircuit or DAGCircuit when we return to Python space. constexpr auto twoQubitSequenceDefaultCapacity = 21; gates.gates.reserve(twoQubitSequenceDefaultCapacity); - gates.globalPhase -= bestNbasis * basisDecomposer.globalPhase; + gates.globalPhase -= bestNbasis * basisDecomposer.globalPhase(); if (bestNbasis == 2) { gates.globalPhase += qc::PI; } @@ -368,8 +368,8 @@ class TwoQubitBasisDecomposer { [[nodiscard]] static llvm::SmallVector decomp0(const decomposition::TwoQubitWeylDecomposition& target) { return { - target.k1r * target.k2r, - target.k1l * target.k2l, + target.k1r() * target.k2r(), + target.k1l() * target.k2l(), }; } @@ -391,10 +391,10 @@ class TwoQubitBasisDecomposer { decomp1(const decomposition::TwoQubitWeylDecomposition& target) const { // may not work for z != 0 and c != 0 (not always in Weyl chamber) return { - basisDecomposer.k2r.transpose().conjugate() * target.k2r, - basisDecomposer.k2l.transpose().conjugate() * target.k2l, - target.k1r * basisDecomposer.k1r.transpose().conjugate(), - target.k1l * basisDecomposer.k1l.transpose().conjugate(), + basisDecomposer.k2r().transpose().conjugate() * target.k2r(), + basisDecomposer.k2l().transpose().conjugate() * target.k2l(), + target.k1r() * basisDecomposer.k1r().transpose().conjugate(), + target.k1l() * basisDecomposer.k1l().transpose().conjugate(), }; } @@ -429,12 +429,12 @@ class TwoQubitBasisDecomposer { "- no guarantee for exact decomposition with two basis gates"); } return { - q2r * target.k2r, - q2l * target.k2l, - q1ra * rzMatrix(2. * target.b) * q1rb, - q1la * rzMatrix(-2. * target.a) * q1lb, - target.k1r * q0r, - target.k1l * q0l, + q2r * target.k2r(), + q2l * target.k2l(), + q1ra * rzMatrix(2. * target.b()) * q1rb, + q1la * rzMatrix(-2. * target.a()) * q1lb, + target.k1r() * q0r, + target.k1l() * q0l, }; } @@ -455,14 +455,14 @@ class TwoQubitBasisDecomposer { "- no guarantee for exact decomposition with three basis gates"); } return { - u3r * target.k2r, - u3l * target.k2l, - u2ra * rzMatrix(2. * target.b) * u2rb, - u2la * rzMatrix(-2. * target.a) * u2lb, - u1ra * rzMatrix(-2. * target.c) * u1rb, + u3r * target.k2r(), + u3l * target.k2l(), + u2ra * rzMatrix(2. * target.b()) * u2rb, + u2la * rzMatrix(-2. * target.a()) * u2lb, + u1ra * rzMatrix(-2. * target.c()) * u1rb, u1l, - target.k1r * u0r, - target.k1l * u0l, + target.k1r() * u0r, + target.k1l() * u0l, }; } @@ -477,15 +477,15 @@ class TwoQubitBasisDecomposer { return { 4. * std::complex{ - std::cos(target.a) * std::cos(target.b) * std::cos(target.c), - std::sin(target.a) * std::sin(target.b) * std::sin(target.c)}, - 4. * std::complex{std::cos(qc::PI_4 - target.a) * - std::cos(basisDecomposer.b - target.b) * - std::cos(target.c), - std::sin(qc::PI_4 - target.a) * - std::sin(basisDecomposer.b - target.b) * - std::sin(target.c)}, - std::complex{4. * std::cos(target.c), 0.}, + std::cos(target.a()) * std::cos(target.b()) * std::cos(target.c()), + std::sin(target.a()) * std::sin(target.b()) * std::sin(target.c())}, + 4. * std::complex{std::cos(qc::PI_4 - target.a()) * + std::cos(basisDecomposer.b() - target.b()) * + std::cos(target.c()), + std::sin(qc::PI_4 - target.a()) * + std::sin(basisDecomposer.b() - target.b()) * + std::sin(target.c())}, + std::complex{4. * std::cos(target.c()), 0.}, std::complex{4., 0.}, }; } diff --git a/mlir/include/mlir/Passes/Decomposition/Helpers.h b/mlir/include/mlir/Passes/Decomposition/Helpers.h index 14b846eda1..5195e7b5af 100644 --- a/mlir/include/mlir/Passes/Decomposition/Helpers.h +++ b/mlir/include/mlir/Passes/Decomposition/Helpers.h @@ -43,7 +43,7 @@ namespace mlir::qco::helpers { // NOLINTBEGIN(misc-include-cleaner) template -[[nodiscard]] inline auto selfAdjointEvd(const Eigen::Matrix& a) { +[[nodiscard]] auto selfAdjointEvd(const Eigen::Matrix& a) { Eigen::SelfAdjointEigenSolver> s; s.compute(a); auto vecs = s.eigenvectors().eval(); diff --git a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index 6943cacff9..469ada0a3a 100644 --- a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -13,6 +13,7 @@ #include "Gate.h" #include "ir/operations/OpType.hpp" +#include #include #include @@ -95,13 +96,14 @@ uMatrix(const double lambda, const double phi, const double theta) { [[nodiscard]] constexpr Eigen::Matrix2cd pMatrix(const double lambda) { return Eigen::Matrix2cd{{1, 0}, {0, {std::cos(lambda), std::sin(lambda)}}}; } -constexpr Eigen::Matrix4cd SWAP_GATE{ + +inline constexpr Eigen::Matrix4cd SWAP_GATE{ {1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}; -constexpr Eigen::Matrix2cd H_GATE{{1.0 / SQRT2, 1.0 / SQRT2}, - {1.0 / SQRT2, -1.0 / SQRT2}}; -constexpr Eigen::Matrix2cd IPZ{{{0, 1}, 0}, {0, {0, -1}}}; -constexpr Eigen::Matrix2cd IPY{{0, 1}, {-1, 0}}; -constexpr Eigen::Matrix2cd IPX{{0, {0, 1}}, {{0, 1}, 0}}; +inline constexpr Eigen::Matrix2cd H_GATE{{1.0 / SQRT2, 1.0 / SQRT2}, + {1.0 / SQRT2, -1.0 / SQRT2}}; +inline constexpr Eigen::Matrix2cd IPZ{{{0, 1}, 0}, {0, {0, -1}}}; +inline constexpr Eigen::Matrix2cd IPY{{0, 1}, {-1, 0}}; +inline constexpr Eigen::Matrix2cd IPX{{0, {0, 1}}, {{0, 1}, 0}}; [[nodiscard]] inline Eigen::Matrix4cd expandToTwoQubits(const Eigen::Matrix2cd& singleQubitMatrix, QubitId qubitId) { @@ -138,12 +140,15 @@ fixTwoQubitMatrixQubitOrder(const Eigen::Matrix4cd& twoQubitMatrix, {std::complex{0.5, -0.5}, std::complex{0.5, 0.5}}}; } if (gate.type == qc::RX) { + assert(gate.parameter.size() == 1); return rxMatrix(gate.parameter[0]); } if (gate.type == qc::RY) { + assert(gate.parameter.size() == 1); return ryMatrix(gate.parameter[0]); } if (gate.type == qc::RZ) { + assert(gate.parameter.size() == 1); return rzMatrix(gate.parameter[0]); } if (gate.type == qc::X) { @@ -153,12 +158,15 @@ fixTwoQubitMatrixQubitOrder(const Eigen::Matrix4cd& twoQubitMatrix, return Eigen::Matrix2cd::Identity(); } if (gate.type == qc::P) { + assert(gate.parameter.size() == 1); return pMatrix(gate.parameter[0]); } if (gate.type == qc::U) { + assert(gate.parameter.size() == 3); return uMatrix(gate.parameter[0], gate.parameter[1], gate.parameter[2]); } if (gate.type == qc::U2) { + assert(gate.parameter.size() == 2); return u2Matrix(gate.parameter[0], gate.parameter[1]); } if (gate.type == qc::H) { @@ -188,21 +196,25 @@ fixTwoQubitMatrixQubitOrder(const Eigen::Matrix4cd& twoQubitMatrix, return Eigen::Matrix4cd{ {1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}, {0, 1, 0, 0}}; } + llvm::reportFatalInternalError("Invalid qubit IDs for CX gate"); } if (gate.type == qc::RXX) { + assert(gate.parameter.size() == 1); return rxxMatrix(gate.parameter[0]); } if (gate.type == qc::RYY) { + assert(gate.parameter.size() == 1); return ryyMatrix(gate.parameter[0]); } if (gate.type == qc::RZZ) { + assert(gate.parameter.size() == 1); return rzzMatrix(gate.parameter[0]); } if (gate.type == qc::I) { return Eigen::Matrix4cd::Identity(); } llvm::reportFatalInternalError( - llvm::StringRef("unsupported gate type for two qubit matrix (" + + llvm::StringRef("Unsupported gate type for two qubit matrix (" + qc::toString(gate.type) + ")")); } llvm::reportFatalInternalError( diff --git a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index 702387712d..04fd8e3410 100644 --- a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -54,45 +55,8 @@ constexpr double SANITY_CHECK_PRECISION = 1e-12; * decomposed using a single-qubit decomposition into e.g. rotation gates and * the canonical gate is RXX(-2 * a), RYY(-2 * b), RZZ (-2 * c). */ -struct TwoQubitWeylDecomposition { - enum class Specialization : std::uint8_t { - General, // canonical gate has no special symmetry. - IdEquiv, // canonical gate is identity. - SWAPEquiv, // canonical gate is SWAP. - PartialSWAPEquiv, // canonical gate is partial SWAP. - PartialSWAPFlipEquiv, // canonical gate is flipped partial SWAP. - ControlledEquiv, // canonical gate is a controlled gate. - MirrorControlledEquiv, // canonical gate is swap + controlled gate. - - // These next 3 gates use the definition of fSim from eq (1) in: - // https://arxiv.org/pdf/2001.08343.pdf - FSimaabEquiv, // parameters a=b & a!=c - FSimabbEquiv, // parameters a!=b & b=c - FSimabmbEquiv, // parameters a!=b!=c & -b=c - }; - - // a, b, c are the parameters of the canonical gate (CAN) - double a; // rotation of RXX gate in CAN (must be taken times -2.0) - double b; // rotation of RYY gate in CAN (must be taken times -2.0) - double c; // rotation of RZZ gate in CAN (must be taken times -2.0) - double globalPhase; // global phase adjustment - /** - * q1 - k2r - C - k1r - - * A - * q0 - k2l - N - k1l - - */ - Eigen::Matrix2cd k1l; // "left" qubit after canonical gate - Eigen::Matrix2cd k2l; // "left" qubit before canonical gate - Eigen::Matrix2cd k1r; // "right" qubit after canonical gate - Eigen::Matrix2cd k2r; // "right" qubit before canonical gate - Specialization specialization; // detected symmetries in the matrix - EulerBasis defaultEulerBasis; // recommended euler basis for k1l/k2l/k1r/k2r - std::optional // desired fidelity; - requestedFidelity; // if set to std::nullopt, no automatic - // specialization will be applied - double calculatedFidelity; // actual fidelity of decomposition - Eigen::Matrix4cd unitaryMatrix; // original matrix for this decomposition - +class TwoQubitWeylDecomposition { +public: /** * Create Weyl decomposition. * @@ -263,22 +227,22 @@ struct TwoQubitWeylDecomposition { // bind weyl coordinates as parameters of canonical gate auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); - TwoQubitWeylDecomposition decomposition{ - .a = a, - .b = b, - .c = c, - .globalPhase = globalPhase, - .k1l = K1l, - .k2l = K2l, - .k1r = K1r, - .k2r = K2r, - .specialization = Specialization::General, - .defaultEulerBasis = EulerBasis::ZYZ, - .requestedFidelity = fidelity, - // will be calculated if a specialization is used, set to -1 for now - .calculatedFidelity = -1.0, - .unitaryMatrix = unitaryMatrix, - }; + TwoQubitWeylDecomposition decomposition; + decomposition.a_ = a; + decomposition.b_ = b; + decomposition.c_ = c; + decomposition.globalPhase_ = globalPhase; + decomposition.k1l_ = K1l; + decomposition.k2l_ = K2l; + decomposition.k1r_ = K1r; + decomposition.k2r_ = K2r; + decomposition.specialization = Specialization::General; + decomposition.defaultEulerBasis = EulerBasis::ZYZ; + decomposition.requestedFidelity = fidelity; + // will be calculated if a specialization is used; set to -1 for now + decomposition.calculatedFidelity = -1.0; + decomposition.unitaryMatrix = unitaryMatrix; + // make sure decomposition is equal to input assert((Eigen::kroneckerProduct(K1l, K1r) * decomposition.getCanonicalMatrix() * @@ -293,11 +257,11 @@ struct TwoQubitWeylDecomposition { auto getTrace = [&]() { if (flippedFromOriginal) { return TwoQubitWeylDecomposition::getTrace( - qc::PI_2 - a, b, -c, decomposition.a, decomposition.b, - decomposition.c); + qc::PI_2 - a, b, -c, decomposition.a_, decomposition.b_, + decomposition.c_); } return TwoQubitWeylDecomposition::getTrace( - a, b, c, decomposition.a, decomposition.b, decomposition.c); + a, b, c, decomposition.a_, decomposition.b_, decomposition.c_); }; // use trace to calculate fidelity of applied specialization and // adjust global phase @@ -306,30 +270,100 @@ struct TwoQubitWeylDecomposition { // final check if specialization is close enough to the original matrix to // satisfy the requested fidelity; since no forced specialization is // allowed, this should never fail - if (decomposition.requestedFidelity) { - if (decomposition.calculatedFidelity + 1.0e-13 < - *decomposition.requestedFidelity) { - llvm::reportFatalInternalError( - "TwoQubitWeylDecomposition: Calculated fidelity of " - "specialization is worse than requested fidelity!"); - } + if (decomposition.requestedFidelity && + decomposition.calculatedFidelity + 1.0e-13 < + *decomposition.requestedFidelity) { + llvm::reportFatalInternalError(llvm::formatv( + "TwoQubitWeylDecomposition: Calculated fidelity of " + "specialization is worse than requested fidelity ({0:F4} vs {1:F4})!", + decomposition.calculatedFidelity, *decomposition.requestedFidelity)); } - decomposition.globalPhase += std::arg(trace); + decomposition.globalPhase_ += std::arg(trace); // final check if decomposition is still valid after specialization - assert((Eigen::kroneckerProduct(decomposition.k1l, decomposition.k1r) * + assert((Eigen::kroneckerProduct(decomposition.k1l_, decomposition.k1r_) * decomposition.getCanonicalMatrix() * - Eigen::kroneckerProduct(decomposition.k2l, decomposition.k2r) * - helpers::globalPhaseFactor(decomposition.globalPhase)) + Eigen::kroneckerProduct(decomposition.k2l_, decomposition.k2r_) * + helpers::globalPhaseFactor(decomposition.globalPhase_)) .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); return decomposition; } + TwoQubitWeylDecomposition(const TwoQubitWeylDecomposition&) = default; + TwoQubitWeylDecomposition(TwoQubitWeylDecomposition&&) = default; + TwoQubitWeylDecomposition& + operator=(const TwoQubitWeylDecomposition&) = default; + TwoQubitWeylDecomposition& operator=(TwoQubitWeylDecomposition&&) = default; + /** * Calculate matrix of canonical gate based on its parameters a, b, c. */ [[nodiscard]] Eigen::Matrix4cd getCanonicalMatrix() const { + return getCanonicalMatrix(a_, b_, c_); + } + + /** + * First parameter of canonical gate. + * + * @note must be multiplied by -2.0 for rotation angle of RXX gate + */ + [[nodiscard]] double a() const { return a_; } + /** + * First parameter of canonical gate. + * + * @note must be multiplied by -2.0 for rotation angle of RYY gate + */ + [[nodiscard]] double b() const { return b_; } + /** + * First parameter of canonical gate. + * + * @note must be multiplied by -2.0 for rotation angle of RZZ gate + */ + [[nodiscard]] double c() const { return c_; } + /** + * Necessary global phase adjustment after applying decomposition. + */ + [[nodiscard]] double globalPhase() const { return globalPhase_; } + + /** + * "Left" qubit after canonical gate. + * + * q1 - k2r - C - k1r - + * A + * q0 - k2l - N - *k1l* - + */ + [[nodiscard]] const Eigen::Matrix2cd& k1l() const { return k1l_; } + /** + * "Left" qubit before canonical gate. + * + * q1 - k2r - C - k1r - + * A + * q0 - *k2l* - N - k1l - + */ + [[nodiscard]] const Eigen::Matrix2cd& k2l() const { return k2l_; } + /** + * "Right" qubit after canonical gate. + * + * q1 - k2r - C - *k1r* - + * A + * q0 - k2l - N - k1l - + */ + [[nodiscard]] const Eigen::Matrix2cd& k1r() const { return k1r_; } + /** + * "Right" qubit before canonical gate. + * + * q1 - *k2r* - C - k1r - + * A + * q0 - k2l - N - k1l - + */ + [[nodiscard]] const Eigen::Matrix2cd& k2r() const { return k2r_; } + + /** + * Calculate matrix of canonical gate based on given parameters a, b, c. + */ + [[nodiscard]] static Eigen::Matrix4cd getCanonicalMatrix(double a, double b, + double c) { auto xx = getTwoQubitMatrix({ .type = qc::RXX, .parameter = {-2.0 * a}, @@ -349,11 +383,29 @@ struct TwoQubitWeylDecomposition { } protected: + enum class Specialization : std::uint8_t { + General, // canonical gate has no special symmetry. + IdEquiv, // canonical gate is identity. + SWAPEquiv, // canonical gate is SWAP. + PartialSWAPEquiv, // canonical gate is partial SWAP. + PartialSWAPFlipEquiv, // canonical gate is flipped partial SWAP. + ControlledEquiv, // canonical gate is a controlled gate. + MirrorControlledEquiv, // canonical gate is swap + controlled gate. + + // These next 3 gates use the definition of fSim from eq (1) in: + // https://arxiv.org/pdf/2001.08343.pdf + FSimaabEquiv, // parameters a=b & a!=c + FSimabbEquiv, // parameters a!=b & b=c + FSimabmbEquiv, // parameters a!=b!=c & -b=c + }; + enum class MagicBasisTransform : std::uint8_t { Into, OutOf, }; + TwoQubitWeylDecomposition() = default; + static Eigen::Matrix4cd magicBasisTransform(const Eigen::Matrix4cd& unitary, MagicBasisTransform direction) { using namespace std::complex_literals; @@ -530,15 +582,15 @@ struct TwoQubitWeylDecomposition { */ [[nodiscard]] Specialization bestSpecialization() const { auto isClose = [this](double ap, double bp, double cp) -> bool { - auto tr = getTrace(a, b, c, ap, bp, cp); + auto tr = getTrace(a_, b_, c_, ap, bp, cp); if (requestedFidelity) { return helpers::traceToFidelity(tr) >= *requestedFidelity; } return false; }; - auto closestAbc = closestPartialSwap(a, b, c); - auto closestAbMinusC = closestPartialSwap(a, b, -c); + auto closestAbc = closestPartialSwap(a_, b_, c_); + auto closestAbMinusC = closestPartialSwap(a_, b_, -c_); if (isClose(0., 0., 0.)) { return Specialization::IdEquiv; @@ -553,19 +605,19 @@ struct TwoQubitWeylDecomposition { if (isClose(closestAbMinusC, closestAbMinusC, -closestAbMinusC)) { return Specialization::PartialSWAPFlipEquiv; } - if (isClose(a, 0., 0.)) { + if (isClose(a_, 0., 0.)) { return Specialization::ControlledEquiv; } - if (isClose(qc::PI_4, qc::PI_4, c)) { + if (isClose(qc::PI_4, qc::PI_4, c_)) { return Specialization::MirrorControlledEquiv; } - if (isClose((a + b) / 2., (a + b) / 2., c)) { + if (isClose((a_ + b_) / 2., (a_ + b_) / 2., c_)) { return Specialization::FSimaabEquiv; } - if (isClose(a, (b + c) / 2., (b + c) / 2.)) { + if (isClose(a_, (b_ + c_) / 2., (b_ + c_) / 2.)) { return Specialization::FSimabbEquiv; } - if (isClose(a, (b - c) / 2., (c - b) / 2.)) { + if (isClose(a_, (b_ - c_) / 2., (c_ - b_) / 2.)) { return Specialization::FSimabmbEquiv; } return Specialization::General; @@ -598,14 +650,14 @@ struct TwoQubitWeylDecomposition { // This gate binds 0 parameters, we make it canonical by setting: // // :math:`K2_l = Id` , :math:`K2_r = Id`. - a = 0.; - b = 0.; - c = 0.; + a_ = 0.; + b_ = 0.; + c_ = 0.; // unmodified global phase - k1l = k1l * k2l; - k2l = Eigen::Matrix2cd::Identity(); - k1r = k1r * k2r; - k2r = Eigen::Matrix2cd::Identity(); + k1l_ = k1l_ * k2l_; + k2l_ = Eigen::Matrix2cd::Identity(); + k1r_ = k1r_ * k2r_; + k2r_ = Eigen::Matrix2cd::Identity(); } else if (newSpecialization == Specialization::SWAPEquiv) { // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, -\pi/4)` // Thus, :math:`U \sim \text{SWAP}` @@ -613,24 +665,24 @@ struct TwoQubitWeylDecomposition { // This gate binds 0 parameters, we make it canonical by setting: // // :math:`K2_l = Id` , :math:`K2_r = Id`. - if (c > 0.) { + if (c_ > 0.) { // unmodified global phase - k1l = k1l * k2r; - k1r = k1r * k2l; - k2l = Eigen::Matrix2cd::Identity(); - k2r = Eigen::Matrix2cd::Identity(); + k1l_ = k1l_ * k2r_; + k1r_ = k1r_ * k2l_; + k2l_ = Eigen::Matrix2cd::Identity(); + k2r_ = Eigen::Matrix2cd::Identity(); } else { flippedFromOriginal = true; - globalPhase += qc::PI_2; - k1l = k1l * IPZ * k2r; - k1r = k1r * IPZ * k2l; - k2l = Eigen::Matrix2cd::Identity(); - k2r = Eigen::Matrix2cd::Identity(); + globalPhase_ += qc::PI_2; + k1l_ = k1l_ * IPZ * k2r_; + k1r_ = k1r_ * IPZ * k2l_; + k2l_ = Eigen::Matrix2cd::Identity(); + k2r_ = Eigen::Matrix2cd::Identity(); } - a = qc::PI_4; - b = qc::PI_4; - c = qc::PI_4; + a_ = qc::PI_4; + b_ = qc::PI_4; + c_ = qc::PI_4; } else if (newSpecialization == Specialization::PartialSWAPEquiv) { // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4)` // Thus, :math:`U \sim \text{SWAP}^\alpha` @@ -638,17 +690,17 @@ struct TwoQubitWeylDecomposition { // This gate binds 3 parameters, we make it canonical by setting: // // :math:`K2_l = Id`. - auto closest = closestPartialSwap(a, b, c); - auto k2lDagger = k2l.transpose().conjugate(); + auto closest = closestPartialSwap(a_, b_, c_); + auto k2lDagger = k2l_.transpose().conjugate(); - a = closest; - b = closest; - c = closest; + a_ = closest; + b_ = closest; + c_ = closest; // unmodified global phase - k1l = k1l * k2l; - k1r = k1r * k2l; - k2r = k2lDagger * k2r; - k2l = Eigen::Matrix2cd::Identity(); + k1l_ = k1l_ * k2l_; + k1r_ = k1r_ * k2l_; + k2r_ = k2lDagger * k2r_; + k2l_ = Eigen::Matrix2cd::Identity(); } else if (newSpecialization == Specialization::PartialSWAPFlipEquiv) { // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4)` // Thus, :math:`U \sim \text{SWAP}^\alpha` @@ -659,17 +711,17 @@ struct TwoQubitWeylDecomposition { // This gate binds 3 parameters, we make it canonical by setting: // // :math:`K2_l = Id` - auto closest = closestPartialSwap(a, b, -c); - auto k2lDagger = k2l.transpose().conjugate(); + auto closest = closestPartialSwap(a_, b_, -c_); + auto k2lDagger = k2l_.transpose().conjugate(); - a = closest; - b = closest; - c = -closest; + a_ = closest; + b_ = closest; + c_ = -closest; // unmodified global phase - k1l = k1l * k2l; - k1r = k1r * IPZ * k2l * IPZ; - k2r = IPZ * k2lDagger * IPZ * k2r; - k2l = Eigen::Matrix2cd::Identity(); + k1l_ = k1l_ * k2l_; + k1r_ = k1r_ * IPZ * k2l_ * IPZ; + k2r_ = IPZ * k2lDagger * IPZ * k2r_; + k2l_ = Eigen::Matrix2cd::Identity(); } else if (newSpecialization == Specialization::ControlledEquiv) { // :math:`U \sim U_d(\alpha, 0, 0)` // Thus, :math:`U \sim \text{Ctrl-U}` @@ -680,18 +732,18 @@ struct TwoQubitWeylDecomposition { // :math:`K2_r = Ry(\theta_r) Rx(\lambda_r)` auto eulerBasis = EulerBasis::XYX; auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - EulerDecomposition::anglesFromUnitary(k2l, eulerBasis); + EulerDecomposition::anglesFromUnitary(k2l_, eulerBasis); auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = - EulerDecomposition::anglesFromUnitary(k2r, eulerBasis); + EulerDecomposition::anglesFromUnitary(k2r_, eulerBasis); // unmodified parameter a - b = 0.; - c = 0.; - globalPhase = globalPhase + k2lphase + k2rphase; - k1l = k1l * rxMatrix(k2lphi); - k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda); - k1r = k1r * rxMatrix(k2rphi); - k2r = ryMatrix(k2rtheta) * rxMatrix(k2rlambda); + b_ = 0.; + c_ = 0.; + globalPhase_ = globalPhase_ + k2lphase + k2rphase; + k1l_ = k1l_ * rxMatrix(k2lphi); + k2l_ = ryMatrix(k2ltheta) * rxMatrix(k2llambda); + k1r_ = k1r_ * rxMatrix(k2rphi); + k2r_ = ryMatrix(k2rtheta) * rxMatrix(k2rlambda); defaultEulerBasis = eulerBasis; } else if (newSpecialization == Specialization::MirrorControlledEquiv) { // :math:`U \sim U_d(\pi/4, \pi/4, \alpha)` @@ -702,18 +754,18 @@ struct TwoQubitWeylDecomposition { // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` // :math:`K2_r = Ry(\theta_r)\cdot Rz(\lambda_r)` auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - EulerDecomposition::anglesFromUnitary(k2l, EulerBasis::ZYZ); + EulerDecomposition::anglesFromUnitary(k2l_, EulerBasis::ZYZ); auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = - EulerDecomposition::anglesFromUnitary(k2r, EulerBasis::ZYZ); + EulerDecomposition::anglesFromUnitary(k2r_, EulerBasis::ZYZ); - a = qc::PI_4; - b = qc::PI_4; + a_ = qc::PI_4; + b_ = qc::PI_4; // unmodified parameter c - globalPhase = globalPhase + k2lphase + k2rphase; - k1l = k1l * rzMatrix(k2rphi); - k2l = ryMatrix(k2ltheta) * rzMatrix(k2llambda); - k1r = k1r * rzMatrix(k2lphi); - k2r = ryMatrix(k2rtheta) * rzMatrix(k2rlambda); + globalPhase_ = globalPhase_ + k2lphase + k2rphase; + k1l_ = k1l_ * rzMatrix(k2rphi); + k2l_ = ryMatrix(k2ltheta) * rzMatrix(k2llambda); + k1r_ = k1r_ * rzMatrix(k2lphi); + k2r_ = ryMatrix(k2rtheta) * rzMatrix(k2rlambda); } else if (newSpecialization == Specialization::FSimaabEquiv) { // :math:`U \sim U_d(\alpha, \alpha, \beta), \alpha \geq |\beta|` // @@ -721,17 +773,17 @@ struct TwoQubitWeylDecomposition { // // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)`. auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - EulerDecomposition::anglesFromUnitary(k2l, EulerBasis::ZYZ); - auto ab = (a + b) / 2.; + EulerDecomposition::anglesFromUnitary(k2l_, EulerBasis::ZYZ); + auto ab = (a_ + b_) / 2.; - a = ab; - b = ab; + a_ = ab; + b_ = ab; // unmodified parameter c - globalPhase = globalPhase + k2lphase; - k1l = k1l * rzMatrix(k2lphi); - k2l = ryMatrix(k2ltheta) * rzMatrix(k2llambda); - k1r = k1r * rzMatrix(k2lphi); - k2r = rzMatrix(-k2lphi) * k2r; + globalPhase_ = globalPhase_ + k2lphase; + k1l_ = k1l_ * rzMatrix(k2lphi); + k2l_ = ryMatrix(k2ltheta) * rzMatrix(k2llambda); + k1r_ = k1r_ * rzMatrix(k2lphi); + k2r_ = rzMatrix(-k2lphi) * k2r_; } else if (newSpecialization == Specialization::FSimabbEquiv) { // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` // @@ -740,17 +792,17 @@ struct TwoQubitWeylDecomposition { // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` auto eulerBasis = EulerBasis::XYX; auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - EulerDecomposition::anglesFromUnitary(k2l, eulerBasis); - auto bc = (b + c) / 2.; + EulerDecomposition::anglesFromUnitary(k2l_, eulerBasis); + auto bc = (b_ + c_) / 2.; // unmodified parameter a - b = bc; - c = bc; - globalPhase = globalPhase + k2lphase; - k1l = k1l * rxMatrix(k2lphi); - k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda); - k1r = k1r * rxMatrix(k2lphi); - k2r = rxMatrix(-k2lphi) * k2r; + b_ = bc; + c_ = bc; + globalPhase_ = globalPhase_ + k2lphase; + k1l_ = k1l_ * rxMatrix(k2lphi); + k2l_ = ryMatrix(k2ltheta) * rxMatrix(k2llambda); + k1r_ = k1r_ * rxMatrix(k2lphi); + k2r_ = rxMatrix(-k2lphi) * k2r_; defaultEulerBasis = eulerBasis; } else if (newSpecialization == Specialization::FSimabmbEquiv) { // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` @@ -760,17 +812,17 @@ struct TwoQubitWeylDecomposition { // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` auto eulerBasis = EulerBasis::XYX; auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - EulerDecomposition::anglesFromUnitary(k2l, eulerBasis); - auto bc = (b - c) / 2.; + EulerDecomposition::anglesFromUnitary(k2l_, eulerBasis); + auto bc = (b_ - c_) / 2.; // unmodified parameter a - b = bc; - c = -bc; - globalPhase = globalPhase + k2lphase; - k1l = k1l * rxMatrix(k2lphi); - k2l = ryMatrix(k2ltheta) * rxMatrix(k2llambda); - k1r = k1r * IPZ * rxMatrix(k2lphi) * IPZ; - k2r = IPZ * rxMatrix(-k2lphi) * IPZ * k2r; + b_ = bc; + c_ = -bc; + globalPhase_ = globalPhase_ + k2lphase; + k1l_ = k1l_ * rxMatrix(k2lphi); + k2l_ = ryMatrix(k2ltheta) * rxMatrix(k2llambda); + k1r_ = k1r_ * IPZ * rxMatrix(k2lphi) * IPZ; + k2r_ = IPZ * rxMatrix(-k2lphi) * IPZ * k2r_; defaultEulerBasis = eulerBasis; } else { llvm::reportFatalInternalError( @@ -778,5 +830,30 @@ struct TwoQubitWeylDecomposition { } return flippedFromOriginal; } + +private: + // a, b, c are the parameters of the canonical gate (CAN) + double a_{}; // rotation of RXX gate in CAN (must be taken times -2.0) + double b_{}; // rotation of RYY gate in CAN (must be taken times -2.0) + double c_{}; // rotation of RZZ gate in CAN (must be taken times -2.0) + double globalPhase_{}; // global phase adjustment + /** + * q1 - k2r - C - k1r - + * A + * q0 - k2l - N - k1l - + */ + Eigen::Matrix2cd k1l_; // "left" qubit after canonical gate + Eigen::Matrix2cd k2l_; // "left" qubit before canonical gate + Eigen::Matrix2cd k1r_; // "right" qubit after canonical gate + Eigen::Matrix2cd k2r_; // "right" qubit before canonical gate + Specialization specialization{ + Specialization::General}; // detected symmetries in the matrix + EulerBasis defaultEulerBasis{ + EulerBasis::U3}; // recommended euler basis for k1l/k2l/k1r/k2r + std::optional // desired fidelity; + requestedFidelity; // if set to std::nullopt, no automatic + // specialization will be applied + double calculatedFidelity{}; // actual fidelity of decomposition + Eigen::Matrix4cd unitaryMatrix; // original matrix for this decomposition }; } // namespace mlir::qco::decomposition diff --git a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp index 6c7f2633dc..d867717790 100644 --- a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp +++ b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -47,14 +46,6 @@ namespace { assert(helpers::isUnitaryMatrix(unitaryMatrix)); return unitaryMatrix; } - -[[nodiscard]] Eigen::Matrix4cd canonicalGate(double a, double b, double c) { - TwoQubitWeylDecomposition tmp{}; - tmp.a = a; - tmp.b = b; - tmp.c = c; - return tmp.getCanonicalMatrix(); -} } // namespace class BasisDecomposerTest @@ -65,7 +56,8 @@ class BasisDecomposerTest basisGate = std::get<0>(GetParam()); eulerBases = std::get<1>(GetParam()); target = std::get<2>(GetParam()); - targetDecomposition = TwoQubitWeylDecomposition::create(target, 1.0); + targetDecomposition = std::make_unique( + TwoQubitWeylDecomposition::create(target, 1.0)); } [[nodiscard]] static Eigen::Matrix4cd @@ -83,14 +75,14 @@ class BasisDecomposerTest Eigen::Matrix4cd target; Gate basisGate; llvm::SmallVector eulerBases; - TwoQubitWeylDecomposition targetDecomposition; + std::unique_ptr targetDecomposition; }; TEST_P(BasisDecomposerTest, TestExact) { const auto& originalMatrix = target; auto decomposer = TwoQubitBasisDecomposer::create(basisGate, 1.0); auto decomposedSequence = decomposer.twoQubitDecompose( - targetDecomposition, eulerBases, 1.0, false, std::nullopt); + *targetDecomposition, eulerBases, 1.0, false, std::nullopt); ASSERT_TRUE(decomposedSequence.has_value()); @@ -105,7 +97,7 @@ TEST_P(BasisDecomposerTest, TestApproximation) { const auto& originalMatrix = target; auto decomposer = TwoQubitBasisDecomposer::create(basisGate, 1.0 - 1e-12); auto decomposedSequence = decomposer.twoQubitDecompose( - targetDecomposition, eulerBases, 1.0 - 1e-12, true, std::nullopt); + *targetDecomposition, eulerBases, 1.0 - 1e-12, true, std::nullopt); ASSERT_TRUE(decomposedSequence.has_value()); @@ -184,11 +176,11 @@ INSTANTIATE_TEST_CASE_P( // targets to be decomposed ::testing::Values( rzzMatrix(2.0), ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0), - canonicalGate(1.5, -0.2, 0.0) * + TwoQubitWeylDecomposition::getCanonicalMatrix(1.5, -0.2, 0.0) * Eigen::kroneckerProduct(rxMatrix(1.0), Eigen::Matrix2cd::Identity()), Eigen::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * - canonicalGate(1.1, 0.2, 3.0) * + TwoQubitWeylDecomposition::getCanonicalMatrix(1.1, 0.2, 3.0) * Eigen::kroneckerProduct(rxMatrix(1.0), Eigen::Matrix2cd::Identity()), Eigen::kroneckerProduct(H_GATE, IPZ) * diff --git a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp index a0fc271d98..11f65519f8 100644 --- a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -40,14 +39,6 @@ namespace { assert(helpers::isUnitaryMatrix(unitaryMatrix)); return unitaryMatrix; } - -[[nodiscard]] Eigen::Matrix4cd canonicalGate(double a, double b, double c) { - TwoQubitWeylDecomposition tmp{}; - tmp.a = a; - tmp.b = b; - tmp.c = c; - return tmp.getCanonicalMatrix(); -} } // namespace class WeylDecompositionTest : public testing::TestWithParam { @@ -60,7 +51,7 @@ class WeylDecompositionTest : public testing::TestWithParam { [[nodiscard]] static std::complex globalPhaseFactor(const TwoQubitWeylDecomposition& decomposition) { - return helpers::globalPhaseFactor(decomposition.globalPhase); + return helpers::globalPhaseFactor(decomposition.globalPhase()); } [[nodiscard]] static Eigen::Matrix4cd can(const TwoQubitWeylDecomposition& decomposition) { @@ -68,11 +59,11 @@ class WeylDecompositionTest : public testing::TestWithParam { } [[nodiscard]] static Eigen::Matrix4cd k1(const TwoQubitWeylDecomposition& decomposition) { - return Eigen::kroneckerProduct(decomposition.k1l, decomposition.k1r); + return Eigen::kroneckerProduct(decomposition.k1l(), decomposition.k1r()); } [[nodiscard]] static Eigen::Matrix4cd k2(const TwoQubitWeylDecomposition& decomposition) { - return Eigen::kroneckerProduct(decomposition.k2l, decomposition.k2r); + return Eigen::kroneckerProduct(decomposition.k2l(), decomposition.k2r()); } }; @@ -127,20 +118,19 @@ INSTANTIATE_TEST_CASE_P( INSTANTIATE_TEST_CASE_P( TwoQubitMatrices, WeylDecompositionTest, - ::testing::Values(rzzMatrix(2.0), - ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0), - canonicalGate(1.5, -0.2, 0.0) * - Eigen::kroneckerProduct(rxMatrix(1.0), - Eigen::Matrix2cd::Identity()), - Eigen::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * - canonicalGate(1.1, 0.2, 3.0) * - Eigen::kroneckerProduct(rxMatrix(1.0), - Eigen::Matrix2cd::Identity()), - Eigen::kroneckerProduct(H_GATE, IPZ) * - getTwoQubitMatrix({.type = qc::X, - .parameter = {}, - .qubitId = {0, 1}}) * - Eigen::kroneckerProduct(IPX, IPY))); + ::testing::Values( + rzzMatrix(2.0), ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0), + TwoQubitWeylDecomposition::getCanonicalMatrix(1.5, -0.2, 0.0) * + Eigen::kroneckerProduct(rxMatrix(1.0), + Eigen::Matrix2cd::Identity()), + Eigen::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * + TwoQubitWeylDecomposition::getCanonicalMatrix(1.1, 0.2, 3.0) * + Eigen::kroneckerProduct(rxMatrix(1.0), + Eigen::Matrix2cd::Identity()), + Eigen::kroneckerProduct(H_GATE, IPZ) * + getTwoQubitMatrix( + {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * + Eigen::kroneckerProduct(IPX, IPY))); INSTANTIATE_TEST_CASE_P( SpecializedMatrices, WeylDecompositionTest, @@ -153,16 +143,16 @@ INSTANTIATE_TEST_CASE_P( getTwoQubitMatrix( {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}), // partial swap equiv - canonicalGate(0.5, 0.5, 0.5), + TwoQubitWeylDecomposition::getCanonicalMatrix(0.5, 0.5, 0.5), // partial swap equiv (flipped) - canonicalGate(0.5, 0.5, -0.5), + TwoQubitWeylDecomposition::getCanonicalMatrix(0.5, 0.5, -0.5), // mirror controlled equiv getTwoQubitMatrix({.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * getTwoQubitMatrix( {.type = qc::X, .parameter = {}, .qubitId = {1, 0}}), // sim aab equiv - canonicalGate(0.5, 0.5, 0.1), + TwoQubitWeylDecomposition::getCanonicalMatrix(0.5, 0.5, 0.1), // sim abb equiv - canonicalGate(0.5, 0.1, 0.1), + TwoQubitWeylDecomposition::getCanonicalMatrix(0.5, 0.1, 0.1), // sim ab-b equiv - canonicalGate(0.5, 0.1, -0.1))); + TwoQubitWeylDecomposition::getCanonicalMatrix(0.5, 0.1, -0.1))); From f1b33bb9dd8b954ce2ce1e8faaa99827efef0e97 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 6 Feb 2026 21:01:39 +0100 Subject: [PATCH 201/237] better random basis tests and other fixes --- .../Passes/Decomposition/WeylDecomposition.h | 6 +++-- .../Patterns/GateDecompositionPattern.cpp | 22 +++++++++++----- .../Decomposition/test_basis_decomposer.cpp | 26 +++++++++++++++---- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index 04fd8e3410..ef03571e98 100644 --- a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -468,7 +468,8 @@ class TwoQubitWeylDecomposition { auto state = std::mt19937{2023}; std::normal_distribution dist; - for (int i = 0; i < 100; ++i) { + constexpr auto maxDiagonalizationAttempts = 100; + for (int i = 0; i < maxDiagonalizationAttempts; ++i) { double randA{}; double randB{}; // For debugging the algorithm use the same RNG values as the @@ -503,7 +504,8 @@ class TwoQubitWeylDecomposition { } } llvm::reportFatalInternalError( - "TwoQubitWeylDecomposition: failed to diagonalize M2."); + "TwoQubitWeylDecomposition: failed to diagonalize M2 (" + + llvm::Twine(maxDiagonalizationAttempts) + " iterations)."); } /** diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index d8c71a35f6..13c43e7e14 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -57,9 +57,9 @@ struct GateDecompositionPattern final * each decomposition. * * @param context MLIR context in which the pattern is applied - * @param basisGate Set of two-qubit gates that should be used in the - * decomposition. All two-qubit interactions will be - * represented by one of the gates in this set + * @param basisGates Set of two-qubit gates that should be used in the + * decomposition. All two-qubit interactions will be + * represented by one of the gates in this set * @param eulerBasis Set of euler bases that should be used for the * decomposition of local single-qubit modifications. For * each necessary single-qubit operation, the optimal basis @@ -73,11 +73,11 @@ struct GateDecompositionPattern final * gates available as basis gates or euler bases */ explicit GateDecompositionPattern(mlir::MLIRContext* context, - llvm::SmallVector basisGate, + llvm::SmallVector basisGates, llvm::SmallVector eulerBasis, bool singleQubitOnly, bool forceApplication) : OpInterfaceRewritePattern(context), - decomposerBasisGates{std::move(basisGate)}, + decomposerBasisGates{std::move(basisGates)}, decomposerEulerBases{std::move(eulerBasis)}, singleQubitOnly{singleQubitOnly}, forceApplication{forceApplication} { for (auto&& basisGate : decomposerBasisGates) { @@ -445,9 +445,10 @@ struct GateDecompositionPattern final * in the series. */ void backtrackSingleQubitSeries(QubitId qubitId) { + llvm::SmallVector backtrackedGates; auto prependSingleQubitGate = [&](UnitaryOpInterface op) { inQubits[qubitId] = op.getInputQubit(0); - gates.insert(gates.begin(), {.op = op, .qubitIds = {qubitId}}); + backtrackedGates.push_back({.op = op, .qubitIds = {qubitId}}); complexity += helpers::getComplexity(helpers::getQcType(op), 1); // outQubits do not need to be updated because the final out qubit is // already fixed @@ -460,6 +461,10 @@ struct GateDecompositionPattern final break; } } + + gates.insert(gates.begin(), + std::make_move_iterator(backtrackedGates.rbegin()), + std::make_move_iterator(backtrackedGates.rend())); } [[nodiscard]] static bool isBarrier(UnitaryOpInterface op) { @@ -475,7 +480,10 @@ struct GateDecompositionPattern final if (qubit) { auto users = qubit.getUsers(); auto userIt = users.begin(); - assert(qubit.hasOneUse()); + if (!qubit.hasOneUse()) { + llvm::reportFatalUsageError("Qubit has more than one use - unable to " + "collect gate series for decomposition!"); + } auto user = mlir::dyn_cast(*userIt); if (user && std::invoke(std::forward(filter), user)) { return user; diff --git a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp index d867717790..b068608a68 100644 --- a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp +++ b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp @@ -112,18 +112,34 @@ TEST(BasisDecomposerTest, Random) { auto stopTime = std::chrono::steady_clock::now() + std::chrono::seconds{3}; auto iterations = 0; - const Gate basisGate{.type = qc::X, .parameter = {}, .qubitId = {0, 1}}; - const llvm::SmallVector eulerBases = {EulerBasis::XYX, - EulerBasis::ZXZ}; + const llvm::SmallVector basisGates{ + {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}, + {.type = qc::X, .parameter = {}, .qubitId = {1, 0}}}; + const llvm::SmallVector eulerBases = { + EulerBasis::XYX, EulerBasis::ZXZ, EulerBasis::ZYZ, EulerBasis::XZX}; + std::mt19937 rng{123456UL}; + std::uniform_int_distribution distBasisGate{ + 0, basisGates.size() - 1}; + std::uniform_int_distribution distEulerBases{ + 1, eulerBases.size() - 1}; + + auto selectRandomEulerBases = [&]() { + auto tmp = eulerBases; + llvm::shuffle(tmp.begin(), tmp.end(), rng); + tmp.resize(distEulerBases(rng)); + return tmp; + }; + auto selectRandomBasisGate = [&]() { return basisGates[distBasisGate(rng)]; }; while (std::chrono::steady_clock::now() < stopTime) { auto originalMatrix = randomUnitaryMatrix(); auto targetDecomposition = TwoQubitWeylDecomposition::create(originalMatrix, 1.0); - auto decomposer = TwoQubitBasisDecomposer::create(basisGate, 1.0); + auto decomposer = + TwoQubitBasisDecomposer::create(selectRandomBasisGate(), 1.0); auto decomposedSequence = decomposer.twoQubitDecompose( - targetDecomposition, eulerBases, 1.0, true, std::nullopt); + targetDecomposition, selectRandomEulerBases(), 1.0, true, std::nullopt); ASSERT_TRUE(decomposedSequence.has_value()); From fa2e4206f85c6686d5883e02e34613db681d5a09 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 6 Feb 2026 22:55:39 +0100 Subject: [PATCH 202/237] more coderabbitai fixes --- .../Passes/Decomposition/BasisDecomposer.h | 18 +++++---- .../Decomposition/test_basis_decomposer.cpp | 35 +++-------------- .../test_euler_decomposition.cpp | 31 ++------------- .../Decomposition/test_weyl_decomposition.cpp | 38 ++++--------------- 4 files changed, 27 insertions(+), 95 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index 51b79b32de..672f018e4f 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -475,16 +475,18 @@ class TwoQubitBasisDecomposer { [[nodiscard]] std::array, 4> traces(const decomposition::TwoQubitWeylDecomposition& target) const { return { - 4. * - std::complex{ - std::cos(target.a()) * std::cos(target.b()) * std::cos(target.c()), - std::sin(target.a()) * std::sin(target.b()) * std::sin(target.c())}, - 4. * std::complex{std::cos(qc::PI_4 - target.a()) * - std::cos(basisDecomposer.b() - target.b()) * + 4. * std::complex{std::cos(target.a()) * std::cos(target.b()) * std::cos(target.c()), - std::sin(qc::PI_4 - target.a()) * - std::sin(basisDecomposer.b() - target.b()) * + std::sin(target.a()) * std::sin(target.b()) * std::sin(target.c())}, + 4. * + std::complex{ + std::cos(qc::PI_4 - target.a()) * + std::cos(basisDecomposer.b() - target.b()) * + std::cos(target.c()), + std::sin(qc::PI_4 - target.a()) * + std::sin(basisDecomposer.b() - target.b()) * + std::sin(target.c())}, std::complex{4. * std::cos(target.c()), 0.}, std::complex{4., 0.}, }; diff --git a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp index b068608a68..8e3d6f7fda 100644 --- a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp +++ b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp @@ -16,13 +16,12 @@ #include "mlir/Passes/Decomposition/Helpers.h" #include "mlir/Passes/Decomposition/UnitaryMatrices.h" #include "mlir/Passes/Decomposition/WeylDecomposition.h" +#include "utils.h" #include #include -#include #include #include -#include #include #include #include @@ -31,23 +30,6 @@ using namespace mlir::qco; using namespace mlir::qco::decomposition; -namespace { -[[nodiscard]] Eigen::Matrix4cd randomUnitaryMatrix() { - [[maybe_unused]] static auto initializeRandom = []() { - // Eigen uses std::rand() internally, use fixed seed for deterministic - // testing behavior - std::srand(123456UL); - return true; - }(); - const Eigen::Matrix4cd randomMatrix = Eigen::Matrix4cd::Random(); - Eigen::HouseholderQR qr{}; // NOLINT(misc-include-cleaner) - qr.compute(randomMatrix); - const Eigen::Matrix4cd unitaryMatrix = qr.householderQ(); - assert(helpers::isUnitaryMatrix(unitaryMatrix)); - return unitaryMatrix; -} -} // namespace - class BasisDecomposerTest : public testing::TestWithParam< std::tuple, Eigen::Matrix4cd>> { @@ -109,8 +91,7 @@ TEST_P(BasisDecomposerTest, TestApproximation) { } TEST(BasisDecomposerTest, Random) { - auto stopTime = std::chrono::steady_clock::now() + std::chrono::seconds{3}; - auto iterations = 0; + constexpr auto maxIterations = 2000; const llvm::SmallVector basisGates{ {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}, @@ -131,8 +112,8 @@ TEST(BasisDecomposerTest, Random) { }; auto selectRandomBasisGate = [&]() { return basisGates[distBasisGate(rng)]; }; - while (std::chrono::steady_clock::now() < stopTime) { - auto originalMatrix = randomUnitaryMatrix(); + for (int i = 0; i < maxIterations; ++i) { + auto originalMatrix = randomUnitaryMatrix(); auto targetDecomposition = TwoQubitWeylDecomposition::create(originalMatrix, 1.0); @@ -150,14 +131,10 @@ TEST(BasisDecomposerTest, Random) { << originalMatrix << '\n' << "RESULT:\n" << restoredMatrix << '\n'; - ++iterations; } - - RecordProperty("iterations", iterations); - std::cerr << "Iterations: " << iterations << '\n'; } -INSTANTIATE_TEST_CASE_P( +INSTANTIATE_TEST_SUITE_P( SingleQubitMatrices, BasisDecomposerTest, testing::Combine( // basis gates @@ -176,7 +153,7 @@ INSTANTIATE_TEST_CASE_P( Eigen::kroneckerProduct(Eigen::Matrix2cd::Identity(), rxMatrix(0.1))))); -INSTANTIATE_TEST_CASE_P( +INSTANTIATE_TEST_SUITE_P( TwoQubitMatrices, BasisDecomposerTest, testing::Combine( // basis gates diff --git a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp index 717df977c1..51b5a44884 100644 --- a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp @@ -13,37 +13,19 @@ #include "mlir/Passes/Decomposition/GateSequence.h" #include "mlir/Passes/Decomposition/Helpers.h" #include "mlir/Passes/Decomposition/UnitaryMatrices.h" +#include "utils.h" #include #include #include -#include #include #include -#include #include #include using namespace mlir::qco; using namespace mlir::qco::decomposition; -namespace { -[[nodiscard]] Eigen::Matrix2cd randomUnitaryMatrix() { - [[maybe_unused]] static auto initializeRandom = []() { - // Eigen uses std::rand() internally, use fixed seed for deterministic - // testing behavior - std::srand(123456UL); - return true; - }(); - const Eigen::Matrix2cd randomMatrix = Eigen::Matrix2cd::Random(); - Eigen::HouseholderQR qr{}; // NOLINT(misc-include-cleaner) - qr.compute(randomMatrix); - const Eigen::Matrix2cd unitaryMatrix = qr.householderQ(); - assert(helpers::isUnitaryMatrix(unitaryMatrix)); - return unitaryMatrix; -} -} // namespace - class EulerDecompositionTest : public testing::TestWithParam> { public: @@ -79,13 +61,12 @@ TEST_P(EulerDecompositionTest, TestExact) { } TEST(EulerDecompositionTest, Random) { - auto stopTime = std::chrono::steady_clock::now() + std::chrono::seconds{3}; - auto iterations = 0; + constexpr auto maxIterations = 10000; auto eulerBases = std::array{EulerBasis::XYX, EulerBasis::XZX, EulerBasis::ZYZ, EulerBasis::ZXZ}; std::size_t currentEulerBase = 0; - while (std::chrono::steady_clock::now() < stopTime) { - auto originalMatrix = randomUnitaryMatrix(); + for (int i = 0; i < maxIterations; ++i) { + auto originalMatrix = randomUnitaryMatrix(); auto eulerBasis = eulerBases[currentEulerBase++ % eulerBases.size()]; auto decomposition = EulerDecomposition::generateCircuit( eulerBasis, originalMatrix, true, std::nullopt); @@ -96,11 +77,7 @@ TEST(EulerDecompositionTest, Random) { << originalMatrix << '\n' << "RESULT:\n" << restoredMatrix << '\n'; - ++iterations; } - - RecordProperty("iterations", iterations); - std::cerr << "Iterations: " << iterations << '\n'; } INSTANTIATE_TEST_CASE_P( diff --git a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp index 11f65519f8..ec599bec0d 100644 --- a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp @@ -12,35 +12,16 @@ #include "mlir/Passes/Decomposition/Helpers.h" #include "mlir/Passes/Decomposition/UnitaryMatrices.h" #include "mlir/Passes/Decomposition/WeylDecomposition.h" +#include "utils.h" #include #include -#include -#include #include -#include #include using namespace mlir::qco; using namespace mlir::qco::decomposition; -namespace { -[[nodiscard]] Eigen::Matrix4cd randomUnitaryMatrix() { - [[maybe_unused]] static auto initializeRandom = []() { - // Eigen uses std::rand() internally, use fixed seed for deterministic - // testing behavior - std::srand(123456UL); - return true; - }(); - const Eigen::Matrix4cd randomMatrix = Eigen::Matrix4cd::Random(); - Eigen::HouseholderQR qr{}; // NOLINT(misc-include-cleaner) - qr.compute(randomMatrix); - const Eigen::Matrix4cd unitaryMatrix = qr.householderQ(); - assert(helpers::isUnitaryMatrix(unitaryMatrix)); - return unitaryMatrix; -} -} // namespace - class WeylDecompositionTest : public testing::TestWithParam { public: [[nodiscard]] static Eigen::Matrix4cd @@ -89,10 +70,9 @@ TEST_P(WeylDecompositionTest, TestApproximation) { } TEST(WeylDecompositionTest, Random) { - auto stopTime = std::chrono::steady_clock::now() + std::chrono::seconds{3}; - auto iterations = 0; - while (std::chrono::steady_clock::now() < stopTime) { - auto originalMatrix = randomUnitaryMatrix(); + constexpr auto maxIterations = 5000; + for (int i = 0; i < maxIterations; ++i) { + auto originalMatrix = randomUnitaryMatrix(); auto decomposition = TwoQubitWeylDecomposition::create(originalMatrix, 1.0 - 1e-12); auto restoredMatrix = WeylDecompositionTest::restore(decomposition); @@ -102,21 +82,17 @@ TEST(WeylDecompositionTest, Random) { << originalMatrix << '\n' << "RESULT:\n" << restoredMatrix << '\n'; - ++iterations; } - - RecordProperty("iterations", iterations); - std::cerr << "Iterations: " << iterations << '\n'; } -INSTANTIATE_TEST_CASE_P( +INSTANTIATE_TEST_SUITE_P( SingleQubitMatrices, WeylDecompositionTest, ::testing::Values(Eigen::Matrix4cd::Identity(), Eigen::kroneckerProduct(rzMatrix(1.0), ryMatrix(3.1)), Eigen::kroneckerProduct(Eigen::Matrix2cd::Identity(), rxMatrix(0.1)))); -INSTANTIATE_TEST_CASE_P( +INSTANTIATE_TEST_SUITE_P( TwoQubitMatrices, WeylDecompositionTest, ::testing::Values( rzzMatrix(2.0), ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0), @@ -132,7 +108,7 @@ INSTANTIATE_TEST_CASE_P( {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * Eigen::kroneckerProduct(IPX, IPY))); -INSTANTIATE_TEST_CASE_P( +INSTANTIATE_TEST_SUITE_P( SpecializedMatrices, WeylDecompositionTest, ::testing::Values( // id + controlled + general already covered by other parametrizations From aa3e9fa8fe170238c173eb4640939c3dd04fc90b Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 6 Feb 2026 23:02:56 +0100 Subject: [PATCH 203/237] add utils for tests --- mlir/unittests/Passes/Decomposition/utils.h | 32 +++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 mlir/unittests/Passes/Decomposition/utils.h diff --git a/mlir/unittests/Passes/Decomposition/utils.h b/mlir/unittests/Passes/Decomposition/utils.h new file mode 100644 index 0000000000..e3f54738a6 --- /dev/null +++ b/mlir/unittests/Passes/Decomposition/utils.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Passes/Decomposition/Helpers.h" + +#include +#include +#include +#include +#include + +template [[nodiscard]] MatrixType randomUnitaryMatrix() { + [[maybe_unused]] static auto initializeRandom = []() { + // Eigen uses std::rand() internally, use fixed seed for deterministic + // testing behavior + std::srand(123456UL); + return true; + }(); + const MatrixType randomMatrix = MatrixType::Random(); + Eigen::HouseholderQR qr{}; // NOLINT(misc-include-cleaner) + qr.compute(randomMatrix); + const MatrixType unitaryMatrix = qr.householderQ(); + assert(mlir::qco::helpers::isUnitaryMatrix(unitaryMatrix)); + return unitaryMatrix; +} From b8ec913afd34955fecbc2dd94c790bbac6e5a307 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 6 Feb 2026 23:23:18 +0100 Subject: [PATCH 204/237] coderabbit feedback --- .../mlir/Passes/Decomposition/BasisDecomposer.h | 1 + .../mlir/Passes/Decomposition/GateSequence.h | 11 +---------- .../mlir/Passes/Decomposition/WeylDecomposition.h | 4 ++-- .../Passes/Patterns/GateDecompositionPattern.cpp | 14 ++++++++++++++ .../Decomposition/test_euler_decomposition.cpp | 2 +- mlir/unittests/Passes/Decomposition/utils.h | 4 ++-- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index 672f018e4f..0ded4475e5 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -267,6 +267,7 @@ class TwoQubitBasisDecomposer { llvm::reportFatalInternalError( "Invalid number of basis gates to use in basis decomposition (" + llvm::Twine(bestNbasis) + ")!"); + llvm_unreachable(""); }; auto decomposition = chooseDecomposition(); llvm::SmallVector, 8> diff --git a/mlir/include/mlir/Passes/Decomposition/GateSequence.h b/mlir/include/mlir/Passes/Decomposition/GateSequence.h index ec5c2ea4c6..2ac6f0ec08 100644 --- a/mlir/include/mlir/Passes/Decomposition/GateSequence.h +++ b/mlir/include/mlir/Passes/Decomposition/GateSequence.h @@ -19,17 +19,8 @@ #include #include #include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include namespace mlir::qco::decomposition { /** diff --git a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index ef03571e98..6c2aa89b22 100644 --- a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -310,13 +310,13 @@ class TwoQubitWeylDecomposition { */ [[nodiscard]] double a() const { return a_; } /** - * First parameter of canonical gate. + * Second parameter of canonical gate. * * @note must be multiplied by -2.0 for rotation angle of RYY gate */ [[nodiscard]] double b() const { return b_; } /** - * First parameter of canonical gate. + * Third parameter of canonical gate. * * @note must be multiplied by -2.0 for rotation angle of RZZ gate */ diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 13c43e7e14..46f16825fa 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -302,6 +302,10 @@ struct GateDecompositionPattern final } else { return std::nullopt; } + } else { + llvm::reportFatalInternalError( + "Gate in series has neither one nor two qubits - decomposition " + "not possible!"); } unitaryMatrix = gateMatrix * unitaryMatrix; } @@ -548,7 +552,9 @@ struct GateDecompositionPattern final createGate(rewriter, location, sequence.globalPhase); } +#ifndef NDEBUG Eigen::Matrix4cd unitaryMatrix = Eigen::Matrix4cd::Identity(); +#endif // NDEBUG for (auto&& gate : sequence.gates) { // TODO: need to add each basis gate we want to use if (gate.type == qc::X && gate.qubitId.size() > 1) { @@ -557,36 +563,44 @@ struct GateDecompositionPattern final auto newGate = createControlledGate(rewriter, location, {inQubits[gate.qubitId[1]]}, inQubits[gate.qubitId[0]]); +#ifndef NDEBUG unitaryMatrix = decomposition::fixTwoQubitMatrixQubitOrder( newGate.getUnitaryMatrix().value(), gate.qubitId) * unitaryMatrix; +#endif // NDEBUG updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RX) { assert(gate.qubitId.size() == 1); auto newGate = createGate( rewriter, location, inQubits[gate.qubitId[0]], gate.parameter[0]); +#ifndef NDEBUG unitaryMatrix = decomposition::expandToTwoQubits(newGate.getUnitaryMatrix().value(), gate.qubitId[0]) * unitaryMatrix; +#endif // NDEBUG updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RY) { assert(gate.qubitId.size() == 1); auto newGate = createGate( rewriter, location, inQubits[gate.qubitId[0]], gate.parameter[0]); +#ifndef NDEBUG unitaryMatrix = decomposition::expandToTwoQubits(newGate.getUnitaryMatrix().value(), gate.qubitId[0]) * unitaryMatrix; +#endif // NDEBUG updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RZ) { assert(gate.qubitId.size() == 1); auto newGate = createGate( rewriter, location, inQubits[gate.qubitId[0]], gate.parameter[0]); +#ifndef NDEBUG unitaryMatrix = decomposition::expandToTwoQubits(newGate.getUnitaryMatrix().value(), gate.qubitId[0]) * unitaryMatrix; +#endif // NDEBUG updateInQubits(gate.qubitId, newGate); } else { llvm::reportFatalInternalError("Unsupported gate type in decomposition " diff --git a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp index 51b5a44884..3e256d22e2 100644 --- a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp @@ -30,7 +30,7 @@ class EulerDecompositionTest : public testing::TestWithParam> { public: [[nodiscard]] static Eigen::Matrix2cd - restore(const TwoQubitGateSequence& sequence) { + restore(const OneQubitGateSequence& sequence) { Eigen::Matrix2cd matrix = Eigen::Matrix2cd::Identity(); for (auto&& gate : sequence.gates) { matrix = getSingleQubitMatrix(gate) * matrix; diff --git a/mlir/unittests/Passes/Decomposition/utils.h b/mlir/unittests/Passes/Decomposition/utils.h index e3f54738a6..a2dcada254 100644 --- a/mlir/unittests/Passes/Decomposition/utils.h +++ b/mlir/unittests/Passes/Decomposition/utils.h @@ -8,13 +8,13 @@ * Licensed under the MIT License */ +#pragma once + #include "mlir/Passes/Decomposition/Helpers.h" #include #include #include -#include -#include template [[nodiscard]] MatrixType randomUnitaryMatrix() { [[maybe_unused]] static auto initializeRandom = []() { From b8e868cceafe43e1d8d6d99f245fc99e964aa79f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 6 Feb 2026 23:31:52 +0100 Subject: [PATCH 205/237] fix includes --- mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h | 4 ++++ mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp | 3 ++- mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp | 3 +++ .../Passes/Decomposition/test_weyl_decomposition.cpp | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index 469ada0a3a..c57ed14446 100644 --- a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -13,6 +13,10 @@ #include "Gate.h" #include "ir/operations/OpType.hpp" +#include +#include +#include +#include #include #include #include diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 46f16825fa..93695f96f7 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -25,10 +25,10 @@ #include #include #include +#include #include #include #include -#include #include #include #include @@ -37,6 +37,7 @@ #include #include #include +#include #include namespace mlir::qco { diff --git a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp index 8e3d6f7fda..91d1c19781 100644 --- a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp +++ b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp @@ -22,8 +22,11 @@ #include #include #include +#include #include +#include #include +#include #include #include diff --git a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp index ec599bec0d..69004f3cb8 100644 --- a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include From a5af55883353a4ae0c173d101f414039cda2459b Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 7 Feb 2026 14:05:27 +0100 Subject: [PATCH 206/237] separate definitions into source files --- .../Passes/Decomposition/BasisDecomposer.h | 366 +-------- .../mlir/Passes/Decomposition/EulerBasis.h | 37 +- .../Passes/Decomposition/EulerDecomposition.h | 148 +--- .../mlir/Passes/Decomposition/GateSequence.h | 33 +- .../mlir/Passes/Decomposition/Helpers.h | 84 +-- .../Passes/Decomposition/UnitaryMatrices.h | 135 +--- .../Passes/Decomposition/WeylDecomposition.h | 648 +--------------- .../Decomposition/BasisDecomposer.cpp | 401 ++++++++++ .../Patterns/Decomposition/EulerBasis.cpp | 52 ++ .../Decomposition/EulerDecomposition.cpp | 182 +++++ .../Patterns/Decomposition/GateSequence.cpp | 54 ++ .../Passes/Patterns/Decomposition/Helpers.cpp | 73 ++ .../Decomposition/UnitaryMatrices.cpp | 141 ++++ .../Decomposition/WeylDecomposition.cpp | 696 ++++++++++++++++++ .../Passes/Decomposition/CMakeLists.txt | 3 +- 15 files changed, 1674 insertions(+), 1379 deletions(-) create mode 100644 mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp create mode 100644 mlir/lib/Passes/Patterns/Decomposition/EulerBasis.cpp create mode 100644 mlir/lib/Passes/Patterns/Decomposition/EulerDecomposition.cpp create mode 100644 mlir/lib/Passes/Patterns/Decomposition/GateSequence.cpp create mode 100644 mlir/lib/Passes/Patterns/Decomposition/Helpers.cpp create mode 100644 mlir/lib/Passes/Patterns/Decomposition/UnitaryMatrices.cpp create mode 100644 mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index 0ded4475e5..7564487b00 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -11,31 +11,15 @@ #pragma once #include "EulerBasis.h" -#include "EulerDecomposition.h" #include "GateSequence.h" -#include "Helpers.h" -#include "UnitaryMatrices.h" #include "WeylDecomposition.h" -#include "ir/Definitions.hpp" #include #include #include -#include #include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include #include @@ -56,145 +40,8 @@ class TwoQubitBasisDecomposer { * decomposition (different order of the qubits also counts as a different * basis gate). */ - static TwoQubitBasisDecomposer create(const Gate& basisGate, - double basisFidelity) { - using namespace std::complex_literals; - - auto relativeEq = [](auto&& lhs, auto&& rhs, auto&& epsilon, - auto&& maxRelative) { - // Handle same infinities - if (lhs == rhs) { - return true; - } - - // Handle remaining infinities - if (std::isinf(lhs) || std::isinf(rhs)) { - return false; - } - - auto absDiff = std::abs(lhs - rhs); - - // For when the numbers are really close together - if (absDiff <= epsilon) { - return true; - } - - auto absLhs = std::abs(lhs); - auto absRhs = std::abs(rhs); - if (absRhs > absLhs) { - return absDiff <= absRhs * maxRelative; - } - return absDiff <= absLhs * maxRelative; - }; - const Eigen::Matrix2cd k12RArr{ - {1i * FRAC1_SQRT2, FRAC1_SQRT2}, - {-FRAC1_SQRT2, -1i * FRAC1_SQRT2}, - }; - const Eigen::Matrix2cd k12LArr{ - {{0.5, 0.5}, {0.5, 0.5}}, - {{-0.5, 0.5}, {0.5, -0.5}}, - }; - - const auto basisDecomposer = - decomposition::TwoQubitWeylDecomposition::create( - getTwoQubitMatrix(basisGate), basisFidelity); - const auto isSuperControlled = - relativeEq(basisDecomposer.a(), qc::PI_4, 1e-13, 1e-09) && - relativeEq(basisDecomposer.c(), 0.0, 1e-13, 1e-09); - - // Create some useful matrices U1, U2, U3 are equivalent to the basis, - // expand as Ui = Ki1.Ubasis.Ki2 - auto b = basisDecomposer.b(); - std::complex temp{0.5, -0.5}; - const Eigen::Matrix2cd k11l{ - {temp * (-1i * std::exp(-1i * b)), temp * std::exp(-1i * b)}, - {temp * (-1i * std::exp(1i * b)), temp * -std::exp(1i * b)}}; - const Eigen::Matrix2cd k11r{{FRAC1_SQRT2 * (1i * std::exp(-1i * b)), - FRAC1_SQRT2 * -std::exp(-1i * b)}, - {FRAC1_SQRT2 * std::exp(1i * b), - FRAC1_SQRT2 * (-1i * std::exp(1i * b))}}; - const Eigen::Matrix2cd k32lK21l{ - {FRAC1_SQRT2 * std::complex{1., std::cos(2. * b)}, - FRAC1_SQRT2 * (1i * std::sin(2. * b))}, - {FRAC1_SQRT2 * (1i * std::sin(2. * b)), - FRAC1_SQRT2 * std::complex{1., -std::cos(2. * b)}}}; - temp = std::complex{0.5, 0.5}; - const Eigen::Matrix2cd k21r{ - {temp * (-1i * std::exp(-2i * b)), temp * std::exp(-2i * b)}, - {temp * (1i * std::exp(2i * b)), temp * std::exp(2i * b)}, - }; - const Eigen::Matrix2cd k22l{ - {FRAC1_SQRT2, -FRAC1_SQRT2}, - {FRAC1_SQRT2, FRAC1_SQRT2}, - }; - const Eigen::Matrix2cd k22r{{0, 1}, {-1, 0}}; - const Eigen::Matrix2cd k31l{ - {FRAC1_SQRT2 * std::exp(-1i * b), FRAC1_SQRT2 * std::exp(-1i * b)}, - {FRAC1_SQRT2 * -std::exp(1i * b), FRAC1_SQRT2 * std::exp(1i * b)}, - }; - const Eigen::Matrix2cd k31r{ - {1i * std::exp(1i * b), 0}, - {0, -1i * std::exp(-1i * b)}, - }; - temp = std::complex{0.5, 0.5}; - const Eigen::Matrix2cd k32r{ - {temp * std::exp(1i * b), temp * -std::exp(-1i * b)}, - {temp * (-1i * std::exp(1i * b)), temp * (-1i * std::exp(-1i * b))}, - }; - auto k1lDagger = basisDecomposer.k1l().transpose().conjugate(); - auto k1rDagger = basisDecomposer.k1r().transpose().conjugate(); - auto k2lDagger = basisDecomposer.k2l().transpose().conjugate(); - auto k2rDagger = basisDecomposer.k2r().transpose().conjugate(); - // Pre-build the fixed parts of the matrices used in 3-part - // decomposition - auto u0l = k31l * k1lDagger; - auto u0r = k31r * k1rDagger; - auto u1l = k2lDagger * k32lK21l * k1lDagger; - auto u1ra = k2rDagger * k32r; - auto u1rb = k21r * k1rDagger; - auto u2la = k2lDagger * k22l; - auto u2lb = k11l * k1lDagger; - auto u2ra = k2rDagger * k22r; - auto u2rb = k11r * k1rDagger; - auto u3l = k2lDagger * k12LArr; - auto u3r = k2rDagger * k12RArr; - // Pre-build the fixed parts of the matrices used in the 2-part - // decomposition - auto q0l = k12LArr.transpose().conjugate() * k1lDagger; - auto q0r = k12RArr.transpose().conjugate() * IPZ * k1rDagger; - auto q1la = k2lDagger * k11l.transpose().conjugate(); - auto q1lb = k11l * k1lDagger; - auto q1ra = k2rDagger * IPZ * k11r.transpose().conjugate(); - auto q1rb = k11r * k1rDagger; - auto q2l = k2lDagger * k12LArr; - auto q2r = k2rDagger * k12RArr; - - return TwoQubitBasisDecomposer{ - basisGate, - basisFidelity, - basisDecomposer, - isSuperControlled, - u0l, - u0r, - u1l, - u1ra, - u1rb, - u2la, - u2lb, - u2ra, - u2rb, - u3l, - u3r, - q0l, - q0r, - q1la, - q1lb, - q1ra, - q1rb, - q2l, - q2r, - }; - } + [[nodiscard]] static TwoQubitBasisDecomposer create(const Gate& basisGate, + double basisFidelity); /** * Perform decomposition using the basis gate of this decomposer. @@ -216,116 +63,7 @@ class TwoQubitBasisDecomposer { const decomposition::TwoQubitWeylDecomposition& targetDecomposition, const llvm::SmallVector& target1qEulerBases, std::optional basisFidelity, bool approximate, - std::optional numBasisGateUses) const { - if (target1qEulerBases.empty()) { - llvm::reportFatalUsageError( - "Unable to perform two-qubit basis decomposition without at least " - "one euler basis!"); - } - - auto getBasisFidelity = [&]() { - if (approximate) { - return basisFidelity.value_or(this->basisFidelity); - } - return 1.0; - }; - double actualBasisFidelity = getBasisFidelity(); - auto traces = this->traces(targetDecomposition); - auto getDefaultNbasis = [&]() { - // determine smallest number of basis gates required to fulfill given - // basis fidelity constraint - auto bestValue = std::numeric_limits::lowest(); - auto bestIndex = -1; - for (int i = 0; std::cmp_less(i, traces.size()); ++i) { - // lower basis fidelity means it becomes easier to use fewer basis gates - // through a rougher approximation - auto value = helpers::traceToFidelity(traces[i]) * - std::pow(actualBasisFidelity, i); - if (value > bestValue) { - bestIndex = i; - bestValue = value; - } - } - // index in traces equals number of basis gates - return bestIndex; - }; - // number of basis gates that need to be used in the decomposition - auto bestNbasis = numBasisGateUses.value_or(getDefaultNbasis()); - auto chooseDecomposition = [&]() { - if (bestNbasis == 0) { - return decomp0(targetDecomposition); - } - if (bestNbasis == 1) { - return decomp1(targetDecomposition); - } - if (bestNbasis == 2) { - return decomp2Supercontrolled(targetDecomposition); - } - if (bestNbasis == 3) { - return decomp3Supercontrolled(targetDecomposition); - } - llvm::reportFatalInternalError( - "Invalid number of basis gates to use in basis decomposition (" + - llvm::Twine(bestNbasis) + ")!"); - llvm_unreachable(""); - }; - auto decomposition = chooseDecomposition(); - llvm::SmallVector, 8> - eulerDecompositions; - for (auto&& decomp : decomposition) { - assert(helpers::isUnitaryMatrix(decomp)); - auto eulerDecomp = unitaryToGateSequence(decomp, target1qEulerBases, 0, - true, std::nullopt); - eulerDecompositions.push_back(eulerDecomp); - } - TwoQubitGateSequence gates{ - .gates = {}, - .globalPhase = targetDecomposition.globalPhase(), - }; - // Worst case length is 5x 1q gates for each 1q decomposition + 1x 2q - // gate We might overallocate a bit if the euler basis is different but - // the worst case is just 16 extra elements with just a String and 2 - // smallvecs each. This is only transient though as the circuit - // sequences aren't long lived and are just used to create a - // QuantumCircuit or DAGCircuit when we return to Python space. - constexpr auto twoQubitSequenceDefaultCapacity = 21; - gates.gates.reserve(twoQubitSequenceDefaultCapacity); - gates.globalPhase -= bestNbasis * basisDecomposer.globalPhase(); - if (bestNbasis == 2) { - gates.globalPhase += qc::PI; - } - - auto addEulerDecomposition = [&](std::size_t index, QubitId qubitId) { - if (auto&& eulerDecomp = eulerDecompositions[index]) { - for (auto&& gate : eulerDecomp->gates) { - gates.gates.push_back({.type = gate.type, - .parameter = gate.parameter, - .qubitId = {qubitId}}); - } - gates.globalPhase += eulerDecomp->globalPhase; - } - }; - - for (std::size_t i = 0; i < bestNbasis; ++i) { - // add single-qubit decompositions before basis gate - addEulerDecomposition(2 * i, 0); - addEulerDecomposition((2 * i) + 1, 1); - - // add basis gate - gates.gates.push_back(basisGate); - } - - // add single-qubit decompositions after basis gate - addEulerDecomposition(2UL * bestNbasis, 0); - addEulerDecomposition((2UL * bestNbasis) + 1, 1); - - // large global phases can be generated by the decomposition, thus limit - // it to [0, +2*pi); TODO: can be removed, should be done by something - // like constant folding - gates.globalPhase = helpers::remEuclid(gates.globalPhase, qc::TAU); - - return gates; - } + std::optional numBasisGateUses) const; protected: // NOLINTBEGIN(modernize-pass-by-value) @@ -367,12 +105,7 @@ class TwoQubitBasisDecomposer { * which is optimal for all targets and bases */ [[nodiscard]] static llvm::SmallVector - decomp0(const decomposition::TwoQubitWeylDecomposition& target) { - return { - target.k1r() * target.k2r(), - target.k1l() * target.k2l(), - }; - } + decomp0(const decomposition::TwoQubitWeylDecomposition& target); /** * Calculate decompositions when one basis gate is required. @@ -389,15 +122,7 @@ class TwoQubitBasisDecomposer { * which is optimal for all targets and bases with ``z==0`` or ``c==0``. */ [[nodiscard]] llvm::SmallVector - decomp1(const decomposition::TwoQubitWeylDecomposition& target) const { - // may not work for z != 0 and c != 0 (not always in Weyl chamber) - return { - basisDecomposer.k2r().transpose().conjugate() * target.k2r(), - basisDecomposer.k2l().transpose().conjugate() * target.k2l(), - target.k1r() * basisDecomposer.k1r().transpose().conjugate(), - target.k1l() * basisDecomposer.k1l().transpose().conjugate(), - }; - } + decomp1(const decomposition::TwoQubitWeylDecomposition& target) const; /** * Calculate decompositions when two basis gates are required. @@ -423,21 +148,7 @@ class TwoQubitBasisDecomposer { * non-supercontrolled basis. */ [[nodiscard]] llvm::SmallVector decomp2Supercontrolled( - const decomposition::TwoQubitWeylDecomposition& target) const { - if (!isSuperControlled) { - llvm::reportFatalInternalError( - "Basis gate of TwoQubitBasisDecomposer is not super-controlled " - "- no guarantee for exact decomposition with two basis gates"); - } - return { - q2r * target.k2r(), - q2l * target.k2l(), - q1ra * rzMatrix(2. * target.b()) * q1rb, - q1la * rzMatrix(-2. * target.a()) * q1lb, - target.k1r() * q0r, - target.k1l() * q0l, - }; - } + const decomposition::TwoQubitWeylDecomposition& target) const; /** * Calculate decompositions when three basis gates are required. @@ -449,23 +160,7 @@ class TwoQubitBasisDecomposer { * non-supercontrolled basis. */ [[nodiscard]] llvm::SmallVector decomp3Supercontrolled( - const decomposition::TwoQubitWeylDecomposition& target) const { - if (!isSuperControlled) { - llvm::reportFatalInternalError( - "Basis gate of TwoQubitBasisDecomposer is not super-controlled " - "- no guarantee for exact decomposition with three basis gates"); - } - return { - u3r * target.k2r(), - u3l * target.k2l(), - u2ra * rzMatrix(2. * target.b()) * u2rb, - u2la * rzMatrix(-2. * target.a()) * u2lb, - u1ra * rzMatrix(-2. * target.c()) * u1rb, - u1l, - target.k1r() * u0r, - target.k1l() * u0l, - }; - } + const decomposition::TwoQubitWeylDecomposition& target) const; /** * Calculate traces for a combination of the parameters of the canonical @@ -474,25 +169,7 @@ class TwoQubitBasisDecomposer { * necessary to construct an equivalent to the canonical gate. */ [[nodiscard]] std::array, 4> - traces(const decomposition::TwoQubitWeylDecomposition& target) const { - return { - 4. * std::complex{std::cos(target.a()) * std::cos(target.b()) * - std::cos(target.c()), - std::sin(target.a()) * std::sin(target.b()) * - std::sin(target.c())}, - 4. * - std::complex{ - std::cos(qc::PI_4 - target.a()) * - std::cos(basisDecomposer.b() - target.b()) * - std::cos(target.c()), - std::sin(qc::PI_4 - target.a()) * - std::sin(basisDecomposer.b() - target.b()) * - std::sin(target.c())}, - std::complex{4. * std::cos(target.c()), 0.}, - std::complex{4., 0.}, - }; - } - + traces(const decomposition::TwoQubitWeylDecomposition& target) const; /** * Decompose a single-qubit unitary matrix into a single-qubit gate * sequence. Multiple euler bases may be specified and the one with the @@ -504,30 +181,7 @@ class TwoQubitBasisDecomposer { QubitId /*qubit*/, // TODO: add error map here: per qubit a mapping of // operation to error value for better calculateError() - bool simplify, std::optional atol) { - assert(!targetBasisList.empty()); - - auto calculateError = [](const OneQubitGateSequence& sequence) -> double { - return static_cast(sequence.complexity()); - }; - - auto minError = std::numeric_limits::max(); - OneQubitGateSequence bestCircuit; - for (auto targetBasis : targetBasisList) { - auto circuit = EulerDecomposition::generateCircuit( - targetBasis, unitaryMat, simplify, atol); - // check top-left 2x2 matrix of generated circuit since the circuit - // operates only on one qubit - assert((circuit.getUnitaryMatrix().block<2, 2>(0, 0).isApprox( - unitaryMat, SANITY_CHECK_PRECISION))); - auto error = calculateError(circuit); - if (error < minError) { - bestCircuit = circuit; - minError = error; - } - } - return bestCircuit; - } + bool simplify, std::optional atol); private: // basis gate of this decomposer instance diff --git a/mlir/include/mlir/Passes/Decomposition/EulerBasis.h b/mlir/include/mlir/Passes/Decomposition/EulerBasis.h index 01e97896c0..25d0fa99e2 100644 --- a/mlir/include/mlir/Passes/Decomposition/EulerBasis.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerBasis.h @@ -40,36 +40,11 @@ enum class EulerBasis : std::uint8_t { ZSX = 11, }; -[[nodiscard]] inline llvm::SmallVector -getGateTypesForEulerBasis(EulerBasis eulerBasis) { - switch (eulerBasis) { - case EulerBasis::ZYZ: - return {qc::RZ, qc::RY}; - case EulerBasis::ZXZ: - return {qc::RZ, qc::RX}; - case EulerBasis::XZX: - return {qc::RX, qc::RZ}; - case EulerBasis::XYX: - return {qc::RX, qc::RY}; - case EulerBasis::U3: - [[fallthrough]]; - case EulerBasis::U321: - [[fallthrough]]; - case EulerBasis::U: - return {qc::U}; - case EulerBasis::RR: - return {qc::R}; - case EulerBasis::ZSXX: - [[fallthrough]]; - case EulerBasis::ZSX: - return {qc::RZ, qc::SX}; - case EulerBasis::PSX: - [[fallthrough]]; - case EulerBasis::U1X: - return {qc::RZ, qc::P}; - } - llvm::reportFatalInternalError( - "Unsupported euler basis for translation to gate types"); -} +/** + * Get list of gates potentially involved in euler basis after euler + * decomposition. + */ +[[nodiscard]] llvm::SmallVector +getGateTypesForEulerBasis(EulerBasis eulerBasis); } // namespace mlir::qco::decomposition diff --git a/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h index 735696b5ce..9250cb7f4c 100644 --- a/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h @@ -12,17 +12,10 @@ #include "EulerBasis.h" #include "GateSequence.h" -#include "Helpers.h" -#include "ir/Definitions.hpp" #include "ir/operations/OpType.hpp" #include #include -#include -#include -#include -#include -#include #include namespace mlir::qco::decomposition { @@ -42,28 +35,7 @@ class EulerDecomposition { */ [[nodiscard]] static OneQubitGateSequence generateCircuit(EulerBasis targetBasis, const Eigen::Matrix2cd& unitaryMatrix, - bool simplify, std::optional atol) { - auto [theta, phi, lambda, phase] = - anglesFromUnitary(unitaryMatrix, targetBasis); - - switch (targetBasis) { - case EulerBasis::ZYZ: - return decomposeKAK(theta, phi, lambda, phase, qc::RZ, qc::RY, simplify, - atol); - case EulerBasis::ZXZ: - return decomposeKAK(theta, phi, lambda, phase, qc::RZ, qc::RX, simplify, - atol); - case EulerBasis::XZX: - return decomposeKAK(theta, phi, lambda, phase, qc::RX, qc::RZ, simplify, - atol); - case EulerBasis::XYX: - return decomposeKAK(theta, phi, lambda, phase, qc::RX, qc::RY, simplify, - atol); - default: - llvm::reportFatalInternalError("Unsupported euler basis for circuit " - "generation in decomposition!"); - } - } + bool simplify, std::optional atol); /** * Calculate angles of a single-qubit matrix according to the given @@ -72,76 +44,16 @@ class EulerDecomposition { * @return array containing (theta, phi, lambda, phase) in this order */ static std::array anglesFromUnitary(const Eigen::Matrix2cd& matrix, - EulerBasis basis) { - if (basis == EulerBasis::XYX) { - return paramsXyxInner(matrix); - } - if (basis == EulerBasis::XZX) { - return paramsXzxInner(matrix); - } - if (basis == EulerBasis::ZYZ) { - return paramsZyzInner(matrix); - } - if (basis == EulerBasis::ZXZ) { - return paramsZxzInner(matrix); - } - llvm::reportFatalInternalError( - "Unsupported euler basis for angle computation in decomposition!"); - } + EulerBasis basis); private: - static std::array paramsZyzInner(const Eigen::Matrix2cd& matrix) { - const auto detArg = std::arg(matrix.determinant()); - const auto phase = 0.5 * detArg; - const auto theta = - 2. * std::atan2(std::abs(matrix(1, 0)), std::abs(matrix(0, 0))); - const auto ang1 = std::arg(matrix(1, 1)); - const auto ang2 = std::arg(matrix(1, 0)); - const auto phi = ang1 + ang2 - detArg; - const auto lam = ang1 - ang2; - return {theta, phi, lam, phase}; - } + static std::array paramsZyz(const Eigen::Matrix2cd& matrix); - static std::array paramsZxzInner(const Eigen::Matrix2cd& matrix) { - const auto [theta, phi, lam, phase] = paramsZyzInner(matrix); - return {theta, phi + (qc::PI / 2.), lam - (qc::PI / 2.), phase}; - } + static std::array paramsZxz(const Eigen::Matrix2cd& matrix); - static std::array paramsXyxInner(const Eigen::Matrix2cd& matrix) { - const Eigen::Matrix2cd matZyz{ - {0.5 * (matrix(0, 0) + matrix(0, 1) + matrix(1, 0) + matrix(1, 1)), - 0.5 * (matrix(0, 0) - matrix(0, 1) + matrix(1, 0) - matrix(1, 1))}, - {0.5 * (matrix(0, 0) + matrix(0, 1) - matrix(1, 0) - matrix(1, 1)), - 0.5 * (matrix(0, 0) - matrix(0, 1) - matrix(1, 0) + matrix(1, 1))}, - }; - auto [theta, phi, lam, phase] = paramsZyzInner(matZyz); - auto newPhi = helpers::mod2pi(phi + qc::PI, 0.); - auto newLam = helpers::mod2pi(lam + qc::PI, 0.); - return { - theta, - newPhi, - newLam, - phase + ((newPhi + newLam - phi - lam) / 2.), - }; - } + static std::array paramsXyx(const Eigen::Matrix2cd& matrix); - static std::array paramsXzxInner(const Eigen::Matrix2cd& matrix) { - auto det = matrix.determinant(); - auto phase = std::imag(std::log(det)) / 2.0; - auto sqrtDet = std::sqrt(det); - const Eigen::Matrix2cd matZxz{ - { - {(matrix(0, 0) / sqrtDet).real(), (matrix(1, 0) / sqrtDet).imag()}, - {(matrix(1, 0) / sqrtDet).real(), (matrix(0, 0) / sqrtDet).imag()}, - }, - { - {-(matrix(1, 0) / sqrtDet).real(), (matrix(0, 0) / sqrtDet).imag()}, - {(matrix(0, 0) / sqrtDet).real(), -(matrix(1, 0) / sqrtDet).imag()}, - }, - }; - auto [theta, phi, lam, phase_zxz] = paramsZxzInner(matZxz); - return {theta, phi, lam, phase + phase_zxz}; - } + static std::array paramsXzx(const Eigen::Matrix2cd& matrix); /** * @note Adapted from circuit_kak() in the IBM Qiskit framework. @@ -159,52 +71,6 @@ class EulerDecomposition { [[nodiscard]] static OneQubitGateSequence decomposeKAK(double theta, double phi, double lambda, double phase, qc::OpType kGate, qc::OpType aGate, bool simplify, - std::optional atol) { - double angleZeroEpsilon = atol.value_or(DEFAULT_ATOL); - if (!simplify) { - // setting atol to negative value to make all angle checks true; this will - // effectively disable the simplification since all rotations appear to be - // "necessary" - angleZeroEpsilon = -1.0; - } - - OneQubitGateSequence sequence{ - .gates = {}, - .globalPhase = phase - ((phi + lambda) / 2.), - }; - if (std::abs(theta) <= angleZeroEpsilon) { - lambda += phi; - lambda = helpers::mod2pi(lambda); - if (std::abs(lambda) > angleZeroEpsilon) { - sequence.gates.push_back({.type = kGate, .parameter = {lambda}}); - sequence.globalPhase += lambda / 2.0; - } - return sequence; - } - - if (std::abs(theta - qc::PI) <= angleZeroEpsilon) { - sequence.globalPhase += phi; - lambda -= phi; - phi = 0.0; - } - if (std::abs(helpers::mod2pi(lambda + qc::PI)) <= angleZeroEpsilon || - std::abs(helpers::mod2pi(phi + qc::PI)) <= angleZeroEpsilon) { - lambda += qc::PI; - theta = -theta; - phi += qc::PI; - } - lambda = helpers::mod2pi(lambda); - if (std::abs(lambda) > angleZeroEpsilon) { - sequence.globalPhase += lambda / 2.0; - sequence.gates.push_back({.type = kGate, .parameter = {lambda}}); - } - sequence.gates.push_back({.type = aGate, .parameter = {theta}}); - phi = helpers::mod2pi(phi); - if (std::abs(phi) > angleZeroEpsilon) { - sequence.globalPhase += phi / 2.0; - sequence.gates.push_back({.type = kGate, .parameter = {phi}}); - } - return sequence; - } + std::optional atol); }; } // namespace mlir::qco::decomposition diff --git a/mlir/include/mlir/Passes/Decomposition/GateSequence.h b/mlir/include/mlir/Passes/Decomposition/GateSequence.h index 2ac6f0ec08..b9a90b8ceb 100644 --- a/mlir/include/mlir/Passes/Decomposition/GateSequence.h +++ b/mlir/include/mlir/Passes/Decomposition/GateSequence.h @@ -10,15 +10,9 @@ #pragma once -#include "EulerBasis.h" #include "Gate.h" -#include "Helpers.h" -#include "UnitaryMatrices.h" -#include "ir/operations/OpType.hpp" #include -#include -#include #include #include @@ -39,38 +33,17 @@ struct QubitGateSequence { /** * @return true if the global phase adjustment is not zero. */ - [[nodiscard]] bool hasGlobalPhase() const { - return std::abs(globalPhase) > DEFAULT_ATOL; - } + [[nodiscard]] bool hasGlobalPhase() const; /** * Calculate complexity of sequence according to getComplexity(). */ - [[nodiscard]] std::size_t complexity() const { - std::size_t c{}; - for (auto&& gate : gates) { - c += helpers::getComplexity(gate.type, gate.qubitId.size()); - } - if (hasGlobalPhase()) { - // need to add a global phase gate if a global phase needs to be applied - c += helpers::getComplexity(qc::GPhase, 0); - } - return c; - } + [[nodiscard]] std::size_t complexity() const; /** * Calculate overall unitary matrix of the sequence. */ - [[nodiscard]] Eigen::Matrix4cd getUnitaryMatrix() const { - Eigen::Matrix4cd unitaryMatrix = Eigen::Matrix4cd::Identity(); - for (auto&& gate : gates) { - auto gateMatrix = getTwoQubitMatrix(gate); - unitaryMatrix = gateMatrix * unitaryMatrix; - } - unitaryMatrix *= helpers::globalPhaseFactor(globalPhase); - assert(helpers::isUnitaryMatrix(unitaryMatrix)); - return unitaryMatrix; - } + [[nodiscard]] Eigen::Matrix4cd getUnitaryMatrix() const; }; /** * Helper type to show that a gate sequence is supposed to only contain diff --git a/mlir/include/mlir/Passes/Decomposition/Helpers.h b/mlir/include/mlir/Passes/Decomposition/Helpers.h index 5195e7b5af..ac5b1b08b5 100644 --- a/mlir/include/mlir/Passes/Decomposition/Helpers.h +++ b/mlir/include/mlir/Passes/Decomposition/Helpers.h @@ -10,36 +10,16 @@ #pragma once -#include "ir/Definitions.hpp" #include "ir/operations/OpType.hpp" #include "mlir/Dialect/QCO/IR/QCODialect.h" -#include // NOLINT(misc-include-cleaner) -#include // NOLINT(misc-include-cleaner) -#include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include // NOLINT(misc-include-cleaner) namespace mlir::qco::helpers { -[[nodiscard]] inline qc::OpType getQcType(UnitaryOpInterface op) { - try { - auto type = op.getBaseSymbol(); - if (type == "ctrl") { - type = llvm::cast(op).getBodyUnitary().getBaseSymbol(); - } - return qc::opTypeFromString(type.str()); - } catch (const std::invalid_argument& /*exception*/) { - return qc::OpType::None; - } -} +[[nodiscard]] qc::OpType getQcType(UnitaryOpInterface op); // NOLINTBEGIN(misc-include-cleaner) template @@ -58,45 +38,31 @@ template } // NOLINTEND(misc-include-cleaner) -[[nodiscard]] inline double remEuclid(double a, double b) { - auto r = std::fmod(a, b); - return (r < 0.0) ? r + std::abs(b) : r; -} +/** + * Euclidean remainder of a modulo b. + * The returned value is never negative. + */ +[[nodiscard]] double remEuclid(double a, double b); -// Wrap angle into interval [-π,π). If within atol of the endpoint, clamp -// to -π -[[nodiscard]] inline double mod2pi(double angle, - double angleZeroEpsilon = 1e-13) { - // remEuclid() isn't exactly the same as Python's % operator, but - // because the RHS here is a constant and positive it is effectively - // equivalent for this case - auto wrapped = remEuclid(angle + qc::PI, qc::TAU) - qc::PI; - if (std::abs(wrapped - qc::PI) < angleZeroEpsilon) { - return -qc::PI; - } - return wrapped; -} +/** + * Wrap angle into interval [-π,π). If within atol of the endpoint, clamp to -π. + */ +[[nodiscard]] double mod2pi(double angle, double angleZeroEpsilon = 1e-13); -[[nodiscard]] inline double traceToFidelity(const std::complex& x) { - auto xAbs = std::abs(x); - return (4.0 + xAbs * xAbs) / 20.0; -} +/** + * Calculate fidelity value of given trace. + */ +[[nodiscard]] double traceToFidelity(const std::complex& x); -[[nodiscard]] inline std::size_t getComplexity(qc::OpType type, - std::size_t numOfQubits) { - if (numOfQubits > 1) { - constexpr std::size_t multiQubitFactor = 10; - return (numOfQubits - 1) * multiQubitFactor; - } - if (type == qc::GPhase) { - return 0; - } - return 1; -} +/** + * Get complexity of gate operating on given number of qubits. + */ +[[nodiscard]] std::size_t getComplexity(qc::OpType type, + std::size_t numOfQubits); -[[nodiscard]] inline std::complex -globalPhaseFactor(double globalPhase) { - return std::exp(std::complex{0, 1} * globalPhase); -} +/** + * Return complex factor which can be multiplied with the operation matrix. + */ +[[nodiscard]] std::complex globalPhaseFactor(double globalPhase); } // namespace mlir::qco::helpers diff --git a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index c57ed14446..972a73446e 100644 --- a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -11,15 +11,10 @@ #pragma once #include "Gate.h" -#include "ir/operations/OpType.hpp" #include -#include #include #include -#include -#include -#include namespace mlir::qco::decomposition { @@ -28,7 +23,7 @@ inline constexpr double FRAC1_SQRT2 = 0.707106781186547524400844362104849039284835937688474036588L; [[nodiscard]] constexpr Eigen::Matrix2cd -uMatrix(const double lambda, const double phi, const double theta) { +uMatrix(double lambda, double phi, double theta) { return Eigen::Matrix2cd{{{{std::cos(theta / 2.), 0.}, {-std::cos(lambda) * std::sin(theta / 2.), -std::sin(lambda) * std::sin(theta / 2.)}}, @@ -38,8 +33,8 @@ uMatrix(const double lambda, const double phi, const double theta) { std::sin(lambda + phi) * std::cos(theta / 2.)}}}}; } -[[nodiscard]] constexpr Eigen::Matrix2cd u2Matrix(const double lambda, - const double phi) { +[[nodiscard]] constexpr Eigen::Matrix2cd u2Matrix(double lambda, + double phi) { return Eigen::Matrix2cd{ {FRAC1_SQRT2, {-std::cos(lambda) * FRAC1_SQRT2, -std::sin(lambda) * FRAC1_SQRT2}}, @@ -67,7 +62,7 @@ uMatrix(const double lambda, const double phi, const double theta) { {0, {std::cos(theta / 2.), std::sin(theta / 2.)}}}; } -[[nodiscard]] constexpr Eigen::Matrix4cd rxxMatrix(const double theta) { +[[nodiscard]] constexpr Eigen::Matrix4cd rxxMatrix(double theta) { const auto cosTheta = std::cos(theta / 2.); const auto sinTheta = std::sin(theta / 2.); @@ -77,7 +72,7 @@ uMatrix(const double lambda, const double phi, const double theta) { {{0., -sinTheta}, 0, 0, cosTheta}}; } -[[nodiscard]] constexpr Eigen::Matrix4cd ryyMatrix(const double theta) { +[[nodiscard]] constexpr Eigen::Matrix4cd ryyMatrix(double theta) { const auto cosTheta = std::cos(theta / 2.); const auto sinTheta = std::sin(theta / 2.); @@ -87,7 +82,7 @@ uMatrix(const double lambda, const double phi, const double theta) { {{0., sinTheta}, 0, 0, cosTheta}}}; } -[[nodiscard]] constexpr Eigen::Matrix4cd rzzMatrix(const double theta) { +[[nodiscard]] constexpr Eigen::Matrix4cd rzzMatrix(double theta) { const auto cosTheta = std::cos(theta / 2.); const auto sinTheta = std::sin(theta / 2.); @@ -97,7 +92,7 @@ uMatrix(const double lambda, const double phi, const double theta) { {0, 0, 0, {cosTheta, -sinTheta}}}; } -[[nodiscard]] constexpr Eigen::Matrix2cd pMatrix(const double lambda) { +[[nodiscard]] constexpr Eigen::Matrix2cd pMatrix(double lambda) { return Eigen::Matrix2cd{{1, 0}, {0, {std::cos(lambda), std::sin(lambda)}}}; } @@ -109,120 +104,16 @@ inline constexpr Eigen::Matrix2cd IPZ{{{0, 1}, 0}, {0, {0, -1}}}; inline constexpr Eigen::Matrix2cd IPY{{0, 1}, {-1, 0}}; inline constexpr Eigen::Matrix2cd IPX{{0, {0, 1}}, {{0, 1}, 0}}; -[[nodiscard]] inline Eigen::Matrix4cd -expandToTwoQubits(const Eigen::Matrix2cd& singleQubitMatrix, QubitId qubitId) { - if (qubitId == 0) { - return Eigen::kroneckerProduct(Eigen::Matrix2cd::Identity(), - singleQubitMatrix); - } - if (qubitId == 1) { - return Eigen::kroneckerProduct(singleQubitMatrix, - Eigen::Matrix2cd::Identity()); - } - llvm::reportFatalInternalError("Invalid qubit id for single-qubit expansion"); -} +[[nodiscard]] Eigen::Matrix4cd +expandToTwoQubits(const Eigen::Matrix2cd& singleQubitMatrix, QubitId qubitId); -[[nodiscard]] inline Eigen::Matrix4cd +[[nodiscard]] Eigen::Matrix4cd fixTwoQubitMatrixQubitOrder(const Eigen::Matrix4cd& twoQubitMatrix, - const llvm::SmallVector& qubitIds) { - if (qubitIds == llvm::SmallVector{1, 0}) { - // since UnitaryOpInterface::getUnitaryMatrix() does have a static - // qubit order, adjust if we need the other direction of the gate - return decomposition::SWAP_GATE * twoQubitMatrix * decomposition::SWAP_GATE; - } - if (qubitIds == llvm::SmallVector{0, 1}) { - return twoQubitMatrix; - } - llvm::reportFatalInternalError( - "Invalid qubit IDs for fixing two-qubit matrix"); -} + const llvm::SmallVector& qubitIds); -[[nodiscard]] inline Eigen::Matrix2cd getSingleQubitMatrix(const Gate& gate) { - if (gate.type == qc::SX) { - return Eigen::Matrix2cd{ - {std::complex{0.5, 0.5}, std::complex{0.5, -0.5}}, - {std::complex{0.5, -0.5}, std::complex{0.5, 0.5}}}; - } - if (gate.type == qc::RX) { - assert(gate.parameter.size() == 1); - return rxMatrix(gate.parameter[0]); - } - if (gate.type == qc::RY) { - assert(gate.parameter.size() == 1); - return ryMatrix(gate.parameter[0]); - } - if (gate.type == qc::RZ) { - assert(gate.parameter.size() == 1); - return rzMatrix(gate.parameter[0]); - } - if (gate.type == qc::X) { - return Eigen::Matrix2cd{{0, 1}, {1, 0}}; - } - if (gate.type == qc::I) { - return Eigen::Matrix2cd::Identity(); - } - if (gate.type == qc::P) { - assert(gate.parameter.size() == 1); - return pMatrix(gate.parameter[0]); - } - if (gate.type == qc::U) { - assert(gate.parameter.size() == 3); - return uMatrix(gate.parameter[0], gate.parameter[1], gate.parameter[2]); - } - if (gate.type == qc::U2) { - assert(gate.parameter.size() == 2); - return u2Matrix(gate.parameter[0], gate.parameter[1]); - } - if (gate.type == qc::H) { - return H_GATE; - } - llvm::reportFatalInternalError( - llvm::StringRef("unsupported gate type for single qubit matrix (" + - qc::toString(gate.type) + ")")); -} +[[nodiscard]] Eigen::Matrix2cd getSingleQubitMatrix(const Gate& gate); // TODO: remove? only used for verification of circuit and in unittests -[[nodiscard]] inline Eigen::Matrix4cd getTwoQubitMatrix(const Gate& gate) { - if (gate.qubitId.empty()) { - return Eigen::Matrix4cd::Identity(); - } - if (gate.qubitId.size() == 1) { - return expandToTwoQubits(getSingleQubitMatrix(gate), gate.qubitId[0]); - } - if (gate.qubitId.size() == 2) { - if (gate.type == qc::X) { - // controlled X (CX) - if (gate.qubitId == llvm::SmallVector{0, 1}) { - return Eigen::Matrix4cd{ - {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}}; - } - if (gate.qubitId == llvm::SmallVector{1, 0}) { - return Eigen::Matrix4cd{ - {1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}, {0, 1, 0, 0}}; - } - llvm::reportFatalInternalError("Invalid qubit IDs for CX gate"); - } - if (gate.type == qc::RXX) { - assert(gate.parameter.size() == 1); - return rxxMatrix(gate.parameter[0]); - } - if (gate.type == qc::RYY) { - assert(gate.parameter.size() == 1); - return ryyMatrix(gate.parameter[0]); - } - if (gate.type == qc::RZZ) { - assert(gate.parameter.size() == 1); - return rzzMatrix(gate.parameter[0]); - } - if (gate.type == qc::I) { - return Eigen::Matrix4cd::Identity(); - } - llvm::reportFatalInternalError( - llvm::StringRef("Unsupported gate type for two qubit matrix (" + - qc::toString(gate.type) + ")")); - } - llvm::reportFatalInternalError( - "Invalid number of qubit IDs in compute_unitary"); -} +[[nodiscard]] Eigen::Matrix4cd getTwoQubitMatrix(const Gate& gate); } // namespace mlir::qco::decomposition diff --git a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index 6c2aa89b22..cec1cabc34 100644 --- a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -11,34 +11,13 @@ #pragma once #include "EulerBasis.h" -#include "EulerDecomposition.h" -#include "Helpers.h" -#include "UnitaryMatrices.h" -#include "ir/Definitions.hpp" -#include "ir/operations/OpType.hpp" #include // NOLINT(misc-include-cleaner) -#include -#include #include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include #include -#include #include namespace mlir::qco::decomposition { @@ -68,227 +47,7 @@ class TwoQubitWeylDecomposition { * gates. */ static TwoQubitWeylDecomposition create(const Eigen::Matrix4cd& unitaryMatrix, - std::optional fidelity) { - auto u = unitaryMatrix; - auto detU = u.determinant(); - auto detPow = std::pow(detU, -0.25); - u *= detPow; // remove global phase from unitary matrix - auto globalPhase = std::arg(detU) / 4.; - - // this should have normalized determinant of u, so that u ∈ SU(4) - assert(std::abs(u.determinant() - 1.0) < SANITY_CHECK_PRECISION); - - // transform unitary matrix to magic basis; this enables two properties: - // 1. if uP ∈ SO(4), V = A ⊗ B (SO(4) → SU(2) ⊗ SU(2)) - // 2. magic basis diagonalizes canonical gate, allowing calculation of - // canonical gate parameters later on - auto uP = magicBasisTransform(u, MagicBasisTransform::OutOf); - const Eigen::Matrix4cd m2 = uP.transpose() * uP; - - // diagonalization yields eigenvectors (p) and eigenvalues (d); - // p is used to calculate K1/K2 (and thus the single-qubit gates - // surrounding the canonical gate); d is is used to determine the weyl - // coordinates and thus the parameters of the canonical gate - // TODO: it may be possible to lower the precision - auto [p, d] = diagonalizeComplexSymmetric(m2, 1e-13); - - // extract Weyl coordinates from eigenvalues, map to [0, 2*pi) - // NOLINTNEXTLINE(misc-include-cleaner) - Eigen::Vector3d cs; - Eigen::Vector4d dReal = -1.0 * d.cwiseArg() / 2.0; - dReal(3) = -dReal(0) - dReal(1) - dReal(2); - for (int i = 0; i < cs.size(); ++i) { - assert(i < dReal.size()); - cs[i] = helpers::remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); - } - - // re-order coordinates and according to min(a, pi/2 - a) with - // a = x mod pi/2 for each weyl coordinate x - decltype(cs) cstemp; - llvm::transform(cs, cstemp.begin(), [](auto&& x) { - auto tmp = helpers::remEuclid(x, qc::PI_2); - return std::min(tmp, qc::PI_2 - tmp); - }); - std::array order{0, 1, 2}; - llvm::stable_sort(order, - [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); - std::tie(order[0], order[1], order[2]) = - std::tuple{order[1], order[2], order[0]}; - std::tie(cs[0], cs[1], cs[2]) = - std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; - std::tie(dReal(0), dReal(1), dReal(2)) = - std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2])}; - - // update eigenvectors (columns of p) according to new order of - // weyl coordinates - Eigen::Matrix4cd pOrig = p; - for (int i = 0; std::cmp_less(i, order.size()); ++i) { - p.col(i) = pOrig.col(order[i]); - } - // apply correction for determinant if necessary - if (p.determinant().real() < 0.0) { - auto lastColumnIndex = p.cols() - 1; - p.col(lastColumnIndex) *= -1.0; - } - assert(std::abs(p.determinant() - 1.0) < SANITY_CHECK_PRECISION); - - // re-create complex eigenvalue matrix; this matrix contains the - // parameters of the canonical gate which is later used in the - // verification - Eigen::Matrix4cd temp = dReal.asDiagonal(); - temp *= std::complex{0, 1}; - // since the matrix is diagonal, matrix exponential is equivalent to - // element-wise exponential function - temp.diagonal() = temp.diagonal().array().exp().matrix(); - - // combined matrix k1 of 1q gates after canonical gate - Eigen::Matrix4cd k1 = uP * p * temp; - assert((k1.transpose() * k1).isIdentity()); // k1 must be orthogonal - assert(k1.determinant().real() > 0.0); - k1 = magicBasisTransform(k1, MagicBasisTransform::Into); - - // combined matrix k2 of 1q gates before canonical gate - Eigen::Matrix4cd k2 = p.transpose().conjugate(); - assert((k2.transpose() * k2).isIdentity()); // k2 must be orthogonal - assert(k2.determinant().real() > 0.0); - k2 = magicBasisTransform(k2, MagicBasisTransform::Into); - - // ensure k1 and k2 are correct (when combined with the canonical gate - // parameters in-between, they are equivalent to u) - assert( - (k1 * magicBasisTransform(temp.conjugate(), MagicBasisTransform::Into) * - k2) - .isApprox(u, SANITY_CHECK_PRECISION)); - - // calculate k1 = K1l ⊗ K1r - auto [K1l, K1r, phase_l] = decomposeTwoQubitProductGate(k1); - // decompose k2 = K2l ⊗ K2r - auto [K2l, K2r, phase_r] = decomposeTwoQubitProductGate(k2); - assert( - Eigen::kroneckerProduct(K1l, K1r).isApprox(k1, SANITY_CHECK_PRECISION)); - assert( - Eigen::kroneckerProduct(K2l, K2r).isApprox(k2, SANITY_CHECK_PRECISION)); - // accumulate global phase - globalPhase += phase_l + phase_r; - - // Flip into Weyl chamber - if (cs[0] > qc::PI_2) { - cs[0] -= 3.0 * qc::PI_2; - K1l = K1l * IPY; - K1r = K1r * IPY; - globalPhase += qc::PI_2; - } - if (cs[1] > qc::PI_2) { - cs[1] -= 3.0 * qc::PI_2; - K1l = K1l * IPX; - K1r = K1r * IPX; - globalPhase += qc::PI_2; - } - auto conjs = 0; - if (cs[0] > qc::PI_4) { - cs[0] = qc::PI_2 - cs[0]; - K1l = K1l * IPY; - K2r = IPY * K2r; - conjs += 1; - globalPhase -= qc::PI_2; - } - if (cs[1] > qc::PI_4) { - cs[1] = qc::PI_2 - cs[1]; - K1l = K1l * IPX; - K2r = IPX * K2r; - conjs += 1; - globalPhase += qc::PI_2; - if (conjs == 1) { - globalPhase -= qc::PI; - } - } - if (cs[2] > qc::PI_2) { - cs[2] -= 3.0 * qc::PI_2; - K1l = K1l * IPZ; - K1r = K1r * IPZ; - globalPhase += qc::PI_2; - if (conjs == 1) { - globalPhase -= qc::PI; - } - } - if (conjs == 1) { - cs[2] = qc::PI_2 - cs[2]; - K1l = K1l * IPZ; - K2r = IPZ * K2r; - globalPhase += qc::PI_2; - } - if (cs[2] > qc::PI_4) { - cs[2] -= qc::PI_2; - K1l = K1l * IPZ; - K1r = K1r * IPZ; - globalPhase -= qc::PI_2; - } - - // bind weyl coordinates as parameters of canonical gate - auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); - - TwoQubitWeylDecomposition decomposition; - decomposition.a_ = a; - decomposition.b_ = b; - decomposition.c_ = c; - decomposition.globalPhase_ = globalPhase; - decomposition.k1l_ = K1l; - decomposition.k2l_ = K2l; - decomposition.k1r_ = K1r; - decomposition.k2r_ = K2r; - decomposition.specialization = Specialization::General; - decomposition.defaultEulerBasis = EulerBasis::ZYZ; - decomposition.requestedFidelity = fidelity; - // will be calculated if a specialization is used; set to -1 for now - decomposition.calculatedFidelity = -1.0; - decomposition.unitaryMatrix = unitaryMatrix; - - // make sure decomposition is equal to input - assert((Eigen::kroneckerProduct(K1l, K1r) * - decomposition.getCanonicalMatrix() * - Eigen::kroneckerProduct(K2l, K2r) * - helpers::globalPhaseFactor(globalPhase)) - .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); - - // determine actual specialization of canonical gate so that the 1q - // matrices can potentially be simplified - auto flippedFromOriginal = decomposition.applySpecialization(); - - auto getTrace = [&]() { - if (flippedFromOriginal) { - return TwoQubitWeylDecomposition::getTrace( - qc::PI_2 - a, b, -c, decomposition.a_, decomposition.b_, - decomposition.c_); - } - return TwoQubitWeylDecomposition::getTrace( - a, b, c, decomposition.a_, decomposition.b_, decomposition.c_); - }; - // use trace to calculate fidelity of applied specialization and - // adjust global phase - auto trace = getTrace(); - decomposition.calculatedFidelity = helpers::traceToFidelity(trace); - // final check if specialization is close enough to the original matrix to - // satisfy the requested fidelity; since no forced specialization is - // allowed, this should never fail - if (decomposition.requestedFidelity && - decomposition.calculatedFidelity + 1.0e-13 < - *decomposition.requestedFidelity) { - llvm::reportFatalInternalError(llvm::formatv( - "TwoQubitWeylDecomposition: Calculated fidelity of " - "specialization is worse than requested fidelity ({0:F4} vs {1:F4})!", - decomposition.calculatedFidelity, *decomposition.requestedFidelity)); - } - decomposition.globalPhase_ += std::arg(trace); - - // final check if decomposition is still valid after specialization - assert((Eigen::kroneckerProduct(decomposition.k1l_, decomposition.k1r_) * - decomposition.getCanonicalMatrix() * - Eigen::kroneckerProduct(decomposition.k2l_, decomposition.k2r_) * - helpers::globalPhaseFactor(decomposition.globalPhase_)) - .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); - - return decomposition; - } + std::optional fidelity); TwoQubitWeylDecomposition(const TwoQubitWeylDecomposition&) = default; TwoQubitWeylDecomposition(TwoQubitWeylDecomposition&&) = default; @@ -363,24 +122,7 @@ class TwoQubitWeylDecomposition { * Calculate matrix of canonical gate based on given parameters a, b, c. */ [[nodiscard]] static Eigen::Matrix4cd getCanonicalMatrix(double a, double b, - double c) { - auto xx = getTwoQubitMatrix({ - .type = qc::RXX, - .parameter = {-2.0 * a}, - .qubitId = {0, 1}, - }); - auto yy = getTwoQubitMatrix({ - .type = qc::RYY, - .parameter = {-2.0 * b}, - .qubitId = {0, 1}, - }); - auto zz = getTwoQubitMatrix({ - .type = qc::RZZ, - .parameter = {-2.0 * c}, - .qubitId = {0, 1}, - }); - return zz * yy * xx; - } + double c); protected: enum class Specialization : std::uint8_t { @@ -407,36 +149,9 @@ class TwoQubitWeylDecomposition { TwoQubitWeylDecomposition() = default; static Eigen::Matrix4cd magicBasisTransform(const Eigen::Matrix4cd& unitary, - MagicBasisTransform direction) { - using namespace std::complex_literals; - const Eigen::Matrix4cd bNonNormalized{ - {1, 1i, 0, 0}, - {0, 0, 1i, 1}, - {0, 0, 1i, -1}, - {1, -1i, 0, 0}, - }; - - const Eigen::Matrix4cd bNonNormalizedDagger{ - {0.5, 0, 0, 0.5}, - {-0.5i, 0, 0, 0.5i}, - {0, -0.5i, -0.5i, 0}, - {0, 0.5, -0.5, 0}, - }; - if (direction == MagicBasisTransform::OutOf) { - return bNonNormalizedDagger * unitary * bNonNormalized; - } - if (direction == MagicBasisTransform::Into) { - return bNonNormalized * unitary * bNonNormalizedDagger; - } - llvm::reportFatalInternalError("Unknown MagicBasisTransform direction!"); - } + MagicBasisTransform direction); - static double closestPartialSwap(double a, double b, double c) { - auto m = (a + b + c) / 3.; - auto [am, bm, cm] = std::array{a - m, b - m, c - m}; - auto [ab, bc, ca] = std::array{a - b, b - c, c - a}; - return m + (am * bm * cm * (6. + ab * ab + bc * bc + ca * ca) / 18.); - } + static double closestPartialSwap(double a, double b, double c); /** * Diagonalize given complex symmetric matrix M into (P, d) using a @@ -453,60 +168,7 @@ class TwoQubitWeylDecomposition { * @return pair of (P, D.diagonal()) */ [[nodiscard]] static std::pair - diagonalizeComplexSymmetric(const Eigen::Matrix4cd& m, double precision) { - // We can't use raw `eig` directly because it isn't guaranteed to give - // us real or orthogonal eigenvectors. Instead, since `M` is - // complex-symmetric, - // M = A + iB - // for real-symmetric `A` and `B`, and as - // M^+ @ M2 = A^2 + B^2 + i [A, B] = 1 - // we must have `A` and `B` commute, and consequently they are - // simultaneously diagonalizable. Mixing them together _should_ account - // for any degeneracy problems, but it's not guaranteed, so we repeat it - // a little bit. The fixed seed is to make failures deterministic; the - // value is not important. - auto state = std::mt19937{2023}; - std::normal_distribution dist; - - constexpr auto maxDiagonalizationAttempts = 100; - for (int i = 0; i < maxDiagonalizationAttempts; ++i) { - double randA{}; - double randB{}; - // For debugging the algorithm use the same RNG values as the - // Qiskit implementation for the first random trial. - // In most cases this loop only executes a single iteration and - // using the same rng values rules out possible RNG differences - // as the root cause of a test failure - if (i == 0) { - randA = 1.2602066112249388; - randB = 0.22317849046722027; - } else { - randA = dist(state); - randB = dist(state); - } - const Eigen::Matrix4d m2Real = randA * m.real() + randB * m.imag(); - auto&& pReal = helpers::selfAdjointEvd(m2Real).first; - const Eigen::Matrix4cd p = pReal; - auto&& d = (p.transpose() * m * p).diagonal(); - - auto&& compare = p * d.asDiagonal() * p.transpose(); - if (compare.isApprox(m, precision)) { - // p are the eigenvectors which are decomposed into the - // single-qubit gates surrounding the canonical gate - // d is the sqrt of the eigenvalues that are used to determine the - // weyl coordinates and thus the parameters of the canonical gate - // check that p is in SO(4) - assert((p.transpose() * p).isIdentity(SANITY_CHECK_PRECISION)); - // make sure determinant of eigenvalues is 1.0 - assert(std::abs(Eigen::Matrix4cd{d.asDiagonal()}.determinant() - 1.0) < - SANITY_CHECK_PRECISION); - return std::make_pair(p, d); - } - } - llvm::reportFatalInternalError( - "TwoQubitWeylDecomposition: failed to diagonalize M2 (" + - llvm::Twine(maxDiagonalizationAttempts) + " iterations)."); - } + diagonalizeComplexSymmetric(const Eigen::Matrix4cd& m, double precision); /** * Decompose a special unitary matrix C that is the combination of two @@ -520,318 +182,26 @@ class TwoQubitWeylDecomposition { * global phase adjustment */ static std::tuple - decomposeTwoQubitProductGate(const Eigen::Matrix4cd& specialUnitary) { - // for alternative approaches, see - // pennylane's math.decomposition.su2su2_to_tensor_products - // or quantumflow.kronecker_decomposition - - // first quadrant - Eigen::Matrix2cd r{{specialUnitary(0, 0), specialUnitary(0, 1)}, - {specialUnitary(1, 0), specialUnitary(1, 1)}}; - auto detR = r.determinant(); - if (std::abs(detR) < 0.1) { - // third quadrant - r = Eigen::Matrix2cd{{specialUnitary(2, 0), specialUnitary(2, 1)}, - {specialUnitary(3, 0), specialUnitary(3, 1)}}; - detR = r.determinant(); - } - if (std::abs(detR) < 0.1) { - llvm::reportFatalInternalError( - "decompose_two_qubit_product_gate: unable to decompose: det_r < 0.1"); - } - r /= std::sqrt(detR); - // transpose with complex conjugate of each element - const Eigen::Matrix2cd rTConj = r.transpose().conjugate(); - - Eigen::Matrix4cd temp = - Eigen::kroneckerProduct(Eigen::Matrix2cd::Identity(), rTConj); - temp = specialUnitary * temp; - - // [[a, b, c, d], - // [e, f, g, h], => [[a, c], - // [i, j, k, l], [i, k]] - // [m, n, o, p]] - Eigen::Matrix2cd l{{temp(0, 0), temp(0, 2)}, {temp(2, 0), temp(2, 2)}}; - auto detL = l.determinant(); - if (std::abs(detL) < 0.9) { - llvm::reportFatalInternalError( - "decompose_two_qubit_product_gate: unable to decompose: detL < 0.9"); - } - l /= std::sqrt(detL); - auto phase = std::arg(detL) / 2.; - - return {l, r, phase}; - } + decomposeTwoQubitProductGate(const Eigen::Matrix4cd& specialUnitary); /** * Calculate trace of two sets of parameters for the canonical gate. * The trace has been defined in: https://arxiv.org/abs/1811.12926 */ [[nodiscard]] static std::complex - getTrace(double a, double b, double c, double ap, double bp, double cp) { - auto da = a - ap; - auto db = b - bp; - auto dc = c - cp; - return 4. * - std::complex{std::cos(da) * std::cos(db) * std::cos(dc), - std::sin(da) * std::sin(db) * std::sin(dc)}; - } + getTrace(double a, double b, double c, double ap, double bp, double cp); /** * Choose the best specialization for the for the canonical gate. * This will use the requestedFidelity to determine if a specialization is * close enough to the actual canonical gate matrix. */ - [[nodiscard]] Specialization bestSpecialization() const { - auto isClose = [this](double ap, double bp, double cp) -> bool { - auto tr = getTrace(a_, b_, c_, ap, bp, cp); - if (requestedFidelity) { - return helpers::traceToFidelity(tr) >= *requestedFidelity; - } - return false; - }; - - auto closestAbc = closestPartialSwap(a_, b_, c_); - auto closestAbMinusC = closestPartialSwap(a_, b_, -c_); - - if (isClose(0., 0., 0.)) { - return Specialization::IdEquiv; - } - if (isClose(qc::PI_4, qc::PI_4, qc::PI_4) || - isClose(qc::PI_4, qc::PI_4, -qc::PI_4)) { - return Specialization::SWAPEquiv; - } - if (isClose(closestAbc, closestAbc, closestAbc)) { - return Specialization::PartialSWAPEquiv; - } - if (isClose(closestAbMinusC, closestAbMinusC, -closestAbMinusC)) { - return Specialization::PartialSWAPFlipEquiv; - } - if (isClose(a_, 0., 0.)) { - return Specialization::ControlledEquiv; - } - if (isClose(qc::PI_4, qc::PI_4, c_)) { - return Specialization::MirrorControlledEquiv; - } - if (isClose((a_ + b_) / 2., (a_ + b_) / 2., c_)) { - return Specialization::FSimaabEquiv; - } - if (isClose(a_, (b_ + c_) / 2., (b_ + c_) / 2.)) { - return Specialization::FSimabbEquiv; - } - if (isClose(a_, (b_ - c_) / 2., (c_ - b_) / 2.)) { - return Specialization::FSimabmbEquiv; - } - return Specialization::General; - } + [[nodiscard]] Specialization bestSpecialization() const; /** * @return true if the specialization flipped the original decomposition */ - bool applySpecialization() { - if (specialization != Specialization::General) { - llvm::reportFatalInternalError( - "Application of specialization only works on " - "general Weyl decompositions!"); - } - bool flippedFromOriginal = false; - auto newSpecialization = bestSpecialization(); - if (newSpecialization == Specialization::General) { - // U has no special symmetry. - // - // This gate binds all 6 possible parameters, so there is no need to - // make the single-qubit pre-/post-gates canonical. - return flippedFromOriginal; - } - specialization = newSpecialization; - - if (newSpecialization == Specialization::IdEquiv) { - // :math:`U \sim U_d(0,0,0)` - // Thus, :math:`\sim Id` - // - // This gate binds 0 parameters, we make it canonical by setting: - // - // :math:`K2_l = Id` , :math:`K2_r = Id`. - a_ = 0.; - b_ = 0.; - c_ = 0.; - // unmodified global phase - k1l_ = k1l_ * k2l_; - k2l_ = Eigen::Matrix2cd::Identity(); - k1r_ = k1r_ * k2r_; - k2r_ = Eigen::Matrix2cd::Identity(); - } else if (newSpecialization == Specialization::SWAPEquiv) { - // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, -\pi/4)` - // Thus, :math:`U \sim \text{SWAP}` - // - // This gate binds 0 parameters, we make it canonical by setting: - // - // :math:`K2_l = Id` , :math:`K2_r = Id`. - if (c_ > 0.) { - // unmodified global phase - k1l_ = k1l_ * k2r_; - k1r_ = k1r_ * k2l_; - k2l_ = Eigen::Matrix2cd::Identity(); - k2r_ = Eigen::Matrix2cd::Identity(); - } else { - flippedFromOriginal = true; - - globalPhase_ += qc::PI_2; - k1l_ = k1l_ * IPZ * k2r_; - k1r_ = k1r_ * IPZ * k2l_; - k2l_ = Eigen::Matrix2cd::Identity(); - k2r_ = Eigen::Matrix2cd::Identity(); - } - a_ = qc::PI_4; - b_ = qc::PI_4; - c_ = qc::PI_4; - } else if (newSpecialization == Specialization::PartialSWAPEquiv) { - // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4)` - // Thus, :math:`U \sim \text{SWAP}^\alpha` - // - // This gate binds 3 parameters, we make it canonical by setting: - // - // :math:`K2_l = Id`. - auto closest = closestPartialSwap(a_, b_, c_); - auto k2lDagger = k2l_.transpose().conjugate(); - - a_ = closest; - b_ = closest; - c_ = closest; - // unmodified global phase - k1l_ = k1l_ * k2l_; - k1r_ = k1r_ * k2l_; - k2r_ = k2lDagger * k2r_; - k2l_ = Eigen::Matrix2cd::Identity(); - } else if (newSpecialization == Specialization::PartialSWAPFlipEquiv) { - // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4)` - // Thus, :math:`U \sim \text{SWAP}^\alpha` - // - // (a non-equivalent root of SWAP from the TwoQubitWeylPartialSWAPEquiv - // similar to how :math:`x = (\pm \sqrt(x))^2`) - // - // This gate binds 3 parameters, we make it canonical by setting: - // - // :math:`K2_l = Id` - auto closest = closestPartialSwap(a_, b_, -c_); - auto k2lDagger = k2l_.transpose().conjugate(); - - a_ = closest; - b_ = closest; - c_ = -closest; - // unmodified global phase - k1l_ = k1l_ * k2l_; - k1r_ = k1r_ * IPZ * k2l_ * IPZ; - k2r_ = IPZ * k2lDagger * IPZ * k2r_; - k2l_ = Eigen::Matrix2cd::Identity(); - } else if (newSpecialization == Specialization::ControlledEquiv) { - // :math:`U \sim U_d(\alpha, 0, 0)` - // Thus, :math:`U \sim \text{Ctrl-U}` - // - // This gate binds 4 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l) Rx(\lambda_l)` - // :math:`K2_r = Ry(\theta_r) Rx(\lambda_r)` - auto eulerBasis = EulerBasis::XYX; - auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - EulerDecomposition::anglesFromUnitary(k2l_, eulerBasis); - auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = - EulerDecomposition::anglesFromUnitary(k2r_, eulerBasis); - - // unmodified parameter a - b_ = 0.; - c_ = 0.; - globalPhase_ = globalPhase_ + k2lphase + k2rphase; - k1l_ = k1l_ * rxMatrix(k2lphi); - k2l_ = ryMatrix(k2ltheta) * rxMatrix(k2llambda); - k1r_ = k1r_ * rxMatrix(k2rphi); - k2r_ = ryMatrix(k2rtheta) * rxMatrix(k2rlambda); - defaultEulerBasis = eulerBasis; - } else if (newSpecialization == Specialization::MirrorControlledEquiv) { - // :math:`U \sim U_d(\pi/4, \pi/4, \alpha)` - // Thus, :math:`U \sim \text{SWAP} \cdot \text{Ctrl-U}` - // - // This gate binds 4 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` - // :math:`K2_r = Ry(\theta_r)\cdot Rz(\lambda_r)` - auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - EulerDecomposition::anglesFromUnitary(k2l_, EulerBasis::ZYZ); - auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = - EulerDecomposition::anglesFromUnitary(k2r_, EulerBasis::ZYZ); - - a_ = qc::PI_4; - b_ = qc::PI_4; - // unmodified parameter c - globalPhase_ = globalPhase_ + k2lphase + k2rphase; - k1l_ = k1l_ * rzMatrix(k2rphi); - k2l_ = ryMatrix(k2ltheta) * rzMatrix(k2llambda); - k1r_ = k1r_ * rzMatrix(k2lphi); - k2r_ = ryMatrix(k2rtheta) * rzMatrix(k2rlambda); - } else if (newSpecialization == Specialization::FSimaabEquiv) { - // :math:`U \sim U_d(\alpha, \alpha, \beta), \alpha \geq |\beta|` - // - // This gate binds 5 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)`. - auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - EulerDecomposition::anglesFromUnitary(k2l_, EulerBasis::ZYZ); - auto ab = (a_ + b_) / 2.; - - a_ = ab; - b_ = ab; - // unmodified parameter c - globalPhase_ = globalPhase_ + k2lphase; - k1l_ = k1l_ * rzMatrix(k2lphi); - k2l_ = ryMatrix(k2ltheta) * rzMatrix(k2llambda); - k1r_ = k1r_ * rzMatrix(k2lphi); - k2r_ = rzMatrix(-k2lphi) * k2r_; - } else if (newSpecialization == Specialization::FSimabbEquiv) { - // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` - // - // This gate binds 5 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` - auto eulerBasis = EulerBasis::XYX; - auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - EulerDecomposition::anglesFromUnitary(k2l_, eulerBasis); - auto bc = (b_ + c_) / 2.; - - // unmodified parameter a - b_ = bc; - c_ = bc; - globalPhase_ = globalPhase_ + k2lphase; - k1l_ = k1l_ * rxMatrix(k2lphi); - k2l_ = ryMatrix(k2ltheta) * rxMatrix(k2llambda); - k1r_ = k1r_ * rxMatrix(k2lphi); - k2r_ = rxMatrix(-k2lphi) * k2r_; - defaultEulerBasis = eulerBasis; - } else if (newSpecialization == Specialization::FSimabmbEquiv) { - // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` - // - // This gate binds 5 parameters, we make it canonical by setting: - // - // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` - auto eulerBasis = EulerBasis::XYX; - auto [k2ltheta, k2lphi, k2llambda, k2lphase] = - EulerDecomposition::anglesFromUnitary(k2l_, eulerBasis); - auto bc = (b_ - c_) / 2.; - - // unmodified parameter a - b_ = bc; - c_ = -bc; - globalPhase_ = globalPhase_ + k2lphase; - k1l_ = k1l_ * rxMatrix(k2lphi); - k2l_ = ryMatrix(k2ltheta) * rxMatrix(k2llambda); - k1r_ = k1r_ * IPZ * rxMatrix(k2lphi) * IPZ; - k2r_ = IPZ * rxMatrix(-k2lphi) * IPZ * k2r_; - defaultEulerBasis = eulerBasis; - } else { - llvm::reportFatalInternalError( - "Unknown specialization for Weyl decomposition!"); - } - return flippedFromOriginal; - } + bool applySpecialization(); private: // a, b, c are the parameters of the canonical gate (CAN) diff --git a/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp b/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp new file mode 100644 index 0000000000..765e83397e --- /dev/null +++ b/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Passes/Decomposition/BasisDecomposer.h" + +#include "ir/Definitions.hpp" +#include "mlir/Passes/Decomposition/EulerBasis.h" +#include "mlir/Passes/Decomposition/EulerDecomposition.h" +#include "mlir/Passes/Decomposition/GateSequence.h" +#include "mlir/Passes/Decomposition/Helpers.h" +#include "mlir/Passes/Decomposition/UnitaryMatrices.h" +#include "mlir/Passes/Decomposition/WeylDecomposition.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mlir::qco::decomposition { +TwoQubitBasisDecomposer TwoQubitBasisDecomposer::create(const Gate& basisGate, + double basisFidelity) { + using namespace std::complex_literals; + + auto relativeEq = [](auto&& lhs, auto&& rhs, auto&& epsilon, + auto&& maxRelative) { + // Handle same infinities + if (lhs == rhs) { + return true; + } + + // Handle remaining infinities + if (std::isinf(lhs) || std::isinf(rhs)) { + return false; + } + + auto absDiff = std::abs(lhs - rhs); + + // For when the numbers are really close together + if (absDiff <= epsilon) { + return true; + } + + auto absLhs = std::abs(lhs); + auto absRhs = std::abs(rhs); + if (absRhs > absLhs) { + return absDiff <= absRhs * maxRelative; + } + return absDiff <= absLhs * maxRelative; + }; + const Eigen::Matrix2cd k12RArr{ + {1i * FRAC1_SQRT2, FRAC1_SQRT2}, + {-FRAC1_SQRT2, -1i * FRAC1_SQRT2}, + }; + const Eigen::Matrix2cd k12LArr{ + {{0.5, 0.5}, {0.5, 0.5}}, + {{-0.5, 0.5}, {0.5, -0.5}}, + }; + + const auto basisDecomposer = decomposition::TwoQubitWeylDecomposition::create( + getTwoQubitMatrix(basisGate), basisFidelity); + const auto isSuperControlled = + relativeEq(basisDecomposer.a(), qc::PI_4, 1e-13, 1e-09) && + relativeEq(basisDecomposer.c(), 0.0, 1e-13, 1e-09); + + // Create some useful matrices U1, U2, U3 are equivalent to the basis, + // expand as Ui = Ki1.Ubasis.Ki2 + auto b = basisDecomposer.b(); + std::complex temp{0.5, -0.5}; + const Eigen::Matrix2cd k11l{ + {temp * (-1i * std::exp(-1i * b)), temp * std::exp(-1i * b)}, + {temp * (-1i * std::exp(1i * b)), temp * -std::exp(1i * b)}}; + const Eigen::Matrix2cd k11r{ + {FRAC1_SQRT2 * (1i * std::exp(-1i * b)), + FRAC1_SQRT2 * -std::exp(-1i * b)}, + {FRAC1_SQRT2 * std::exp(1i * b), FRAC1_SQRT2 * (-1i * std::exp(1i * b))}}; + const Eigen::Matrix2cd k32lK21l{ + {FRAC1_SQRT2 * std::complex{1., std::cos(2. * b)}, + FRAC1_SQRT2 * (1i * std::sin(2. * b))}, + {FRAC1_SQRT2 * (1i * std::sin(2. * b)), + FRAC1_SQRT2 * std::complex{1., -std::cos(2. * b)}}}; + temp = std::complex{0.5, 0.5}; + const Eigen::Matrix2cd k21r{ + {temp * (-1i * std::exp(-2i * b)), temp * std::exp(-2i * b)}, + {temp * (1i * std::exp(2i * b)), temp * std::exp(2i * b)}, + }; + const Eigen::Matrix2cd k22l{ + {FRAC1_SQRT2, -FRAC1_SQRT2}, + {FRAC1_SQRT2, FRAC1_SQRT2}, + }; + const Eigen::Matrix2cd k22r{{0, 1}, {-1, 0}}; + const Eigen::Matrix2cd k31l{ + {FRAC1_SQRT2 * std::exp(-1i * b), FRAC1_SQRT2 * std::exp(-1i * b)}, + {FRAC1_SQRT2 * -std::exp(1i * b), FRAC1_SQRT2 * std::exp(1i * b)}, + }; + const Eigen::Matrix2cd k31r{ + {1i * std::exp(1i * b), 0}, + {0, -1i * std::exp(-1i * b)}, + }; + temp = std::complex{0.5, 0.5}; + const Eigen::Matrix2cd k32r{ + {temp * std::exp(1i * b), temp * -std::exp(-1i * b)}, + {temp * (-1i * std::exp(1i * b)), temp * (-1i * std::exp(-1i * b))}, + }; + auto k1lDagger = basisDecomposer.k1l().transpose().conjugate(); + auto k1rDagger = basisDecomposer.k1r().transpose().conjugate(); + auto k2lDagger = basisDecomposer.k2l().transpose().conjugate(); + auto k2rDagger = basisDecomposer.k2r().transpose().conjugate(); + // Pre-build the fixed parts of the matrices used in 3-part + // decomposition + auto u0l = k31l * k1lDagger; + auto u0r = k31r * k1rDagger; + auto u1l = k2lDagger * k32lK21l * k1lDagger; + auto u1ra = k2rDagger * k32r; + auto u1rb = k21r * k1rDagger; + auto u2la = k2lDagger * k22l; + auto u2lb = k11l * k1lDagger; + auto u2ra = k2rDagger * k22r; + auto u2rb = k11r * k1rDagger; + auto u3l = k2lDagger * k12LArr; + auto u3r = k2rDagger * k12RArr; + // Pre-build the fixed parts of the matrices used in the 2-part + // decomposition + auto q0l = k12LArr.transpose().conjugate() * k1lDagger; + auto q0r = k12RArr.transpose().conjugate() * IPZ * k1rDagger; + auto q1la = k2lDagger * k11l.transpose().conjugate(); + auto q1lb = k11l * k1lDagger; + auto q1ra = k2rDagger * IPZ * k11r.transpose().conjugate(); + auto q1rb = k11r * k1rDagger; + auto q2l = k2lDagger * k12LArr; + auto q2r = k2rDagger * k12RArr; + + return TwoQubitBasisDecomposer{ + basisGate, + basisFidelity, + basisDecomposer, + isSuperControlled, + u0l, + u0r, + u1l, + u1ra, + u1rb, + u2la, + u2lb, + u2ra, + u2rb, + u3l, + u3r, + q0l, + q0r, + q1la, + q1lb, + q1ra, + q1rb, + q2l, + q2r, + }; +} + +std::optional TwoQubitBasisDecomposer::twoQubitDecompose( + const decomposition::TwoQubitWeylDecomposition& targetDecomposition, + const llvm::SmallVector& target1qEulerBases, + std::optional basisFidelity, bool approximate, + std::optional numBasisGateUses) const { + if (target1qEulerBases.empty()) { + llvm::reportFatalUsageError( + "Unable to perform two-qubit basis decomposition without at least " + "one euler basis!"); + } + + auto getBasisFidelity = [&]() { + if (approximate) { + return basisFidelity.value_or(this->basisFidelity); + } + return 1.0; + }; + double actualBasisFidelity = getBasisFidelity(); + auto traces = this->traces(targetDecomposition); + auto getDefaultNbasis = [&]() { + // determine smallest number of basis gates required to fulfill given + // basis fidelity constraint + auto bestValue = std::numeric_limits::lowest(); + auto bestIndex = -1; + for (int i = 0; std::cmp_less(i, traces.size()); ++i) { + // lower basis fidelity means it becomes easier to use fewer basis gates + // through a rougher approximation + auto value = helpers::traceToFidelity(traces[i]) * + std::pow(actualBasisFidelity, i); + if (value > bestValue) { + bestIndex = i; + bestValue = value; + } + } + // index in traces equals number of basis gates + return bestIndex; + }; + // number of basis gates that need to be used in the decomposition + auto bestNbasis = numBasisGateUses.value_or(getDefaultNbasis()); + auto chooseDecomposition = [&]() { + if (bestNbasis == 0) { + return decomp0(targetDecomposition); + } + if (bestNbasis == 1) { + return decomp1(targetDecomposition); + } + if (bestNbasis == 2) { + return decomp2Supercontrolled(targetDecomposition); + } + if (bestNbasis == 3) { + return decomp3Supercontrolled(targetDecomposition); + } + llvm::reportFatalInternalError( + "Invalid number of basis gates to use in basis decomposition (" + + llvm::Twine(bestNbasis) + ")!"); + llvm_unreachable(""); + }; + auto decomposition = chooseDecomposition(); + llvm::SmallVector, 8> eulerDecompositions; + for (auto&& decomp : decomposition) { + assert(helpers::isUnitaryMatrix(decomp)); + auto eulerDecomp = unitaryToGateSequence(decomp, target1qEulerBases, 0, + true, std::nullopt); + eulerDecompositions.push_back(eulerDecomp); + } + TwoQubitGateSequence gates{ + .gates = {}, + .globalPhase = targetDecomposition.globalPhase(), + }; + // Worst case length is 5x 1q gates for each 1q decomposition + 1x 2q + // gate We might overallocate a bit if the euler basis is different but + // the worst case is just 16 extra elements with just a String and 2 + // smallvecs each. This is only transient though as the circuit + // sequences aren't long lived and are just used to create a + // QuantumCircuit or DAGCircuit when we return to Python space. + constexpr auto twoQubitSequenceDefaultCapacity = 21; + gates.gates.reserve(twoQubitSequenceDefaultCapacity); + gates.globalPhase -= bestNbasis * basisDecomposer.globalPhase(); + if (bestNbasis == 2) { + gates.globalPhase += qc::PI; + } + + auto addEulerDecomposition = [&](std::size_t index, QubitId qubitId) { + if (auto&& eulerDecomp = eulerDecompositions[index]) { + for (auto&& gate : eulerDecomp->gates) { + gates.gates.push_back({.type = gate.type, + .parameter = gate.parameter, + .qubitId = {qubitId}}); + } + gates.globalPhase += eulerDecomp->globalPhase; + } + }; + + for (std::size_t i = 0; i < bestNbasis; ++i) { + // add single-qubit decompositions before basis gate + addEulerDecomposition(2 * i, 0); + addEulerDecomposition((2 * i) + 1, 1); + + // add basis gate + gates.gates.push_back(basisGate); + } + + // add single-qubit decompositions after basis gate + addEulerDecomposition(2UL * bestNbasis, 0); + addEulerDecomposition((2UL * bestNbasis) + 1, 1); + + // large global phases can be generated by the decomposition, thus limit + // it to [0, +2*pi); TODO: can be removed, should be done by something + // like constant folding + gates.globalPhase = helpers::remEuclid(gates.globalPhase, qc::TAU); + + return gates; +} + +llvm::SmallVector +TwoQubitBasisDecomposer::decomp0(const TwoQubitWeylDecomposition& target) { + return { + target.k1r() * target.k2r(), + target.k1l() * target.k2l(), + }; +} + +llvm::SmallVector TwoQubitBasisDecomposer::decomp1( + const TwoQubitWeylDecomposition& target) const { + // may not work for z != 0 and c != 0 (not always in Weyl chamber) + return { + basisDecomposer.k2r().transpose().conjugate() * target.k2r(), + basisDecomposer.k2l().transpose().conjugate() * target.k2l(), + target.k1r() * basisDecomposer.k1r().transpose().conjugate(), + target.k1l() * basisDecomposer.k1l().transpose().conjugate(), + }; +} + +llvm::SmallVector +TwoQubitBasisDecomposer::decomp2Supercontrolled( + const TwoQubitWeylDecomposition& target) const { + if (!isSuperControlled) { + llvm::reportFatalInternalError( + "Basis gate of TwoQubitBasisDecomposer is not super-controlled " + "- no guarantee for exact decomposition with two basis gates"); + } + return { + q2r * target.k2r(), + q2l * target.k2l(), + q1ra * rzMatrix(2. * target.b()) * q1rb, + q1la * rzMatrix(-2. * target.a()) * q1lb, + target.k1r() * q0r, + target.k1l() * q0l, + }; +} + +llvm::SmallVector +TwoQubitBasisDecomposer::decomp3Supercontrolled( + const TwoQubitWeylDecomposition& target) const { + if (!isSuperControlled) { + llvm::reportFatalInternalError( + "Basis gate of TwoQubitBasisDecomposer is not super-controlled " + "- no guarantee for exact decomposition with three basis gates"); + } + return { + u3r * target.k2r(), + u3l * target.k2l(), + u2ra * rzMatrix(2. * target.b()) * u2rb, + u2la * rzMatrix(-2. * target.a()) * u2lb, + u1ra * rzMatrix(-2. * target.c()) * u1rb, + u1l, + target.k1r() * u0r, + target.k1l() * u0l, + }; +} + +std::array, 4> +TwoQubitBasisDecomposer::traces(const TwoQubitWeylDecomposition& target) const { + return { + 4. * std::complex{std::cos(target.a()) * std::cos(target.b()) * + std::cos(target.c()), + std::sin(target.a()) * std::sin(target.b()) * + std::sin(target.c())}, + 4. * std::complex{std::cos(qc::PI_4 - target.a()) * + std::cos(basisDecomposer.b() - target.b()) * + std::cos(target.c()), + std::sin(qc::PI_4 - target.a()) * + std::sin(basisDecomposer.b() - target.b()) * + std::sin(target.c())}, + std::complex{4. * std::cos(target.c()), 0.}, + std::complex{4., 0.}, + }; +} + +OneQubitGateSequence TwoQubitBasisDecomposer::unitaryToGateSequence( + const Eigen::Matrix2cd& unitaryMat, + const llvm::SmallVector& targetBasisList, QubitId /*qubit*/, + // TODO: add error map here: per qubit a mapping of + // operation to error value for better calculateError() + bool simplify, std::optional atol) { + assert(!targetBasisList.empty()); + + auto calculateError = [](const OneQubitGateSequence& sequence) -> double { + return static_cast(sequence.complexity()); + }; + + auto minError = std::numeric_limits::max(); + OneQubitGateSequence bestCircuit; + for (auto targetBasis : targetBasisList) { + auto circuit = EulerDecomposition::generateCircuit(targetBasis, unitaryMat, + simplify, atol); + // check top-left 2x2 matrix of generated circuit since the circuit + // operates only on one qubit + assert((circuit.getUnitaryMatrix().block<2, 2>(0, 0).isApprox( + unitaryMat, SANITY_CHECK_PRECISION))); + auto error = calculateError(circuit); + if (error < minError) { + bestCircuit = circuit; + minError = error; + } + } + return bestCircuit; +} + +} // namespace mlir::qco::decomposition diff --git a/mlir/lib/Passes/Patterns/Decomposition/EulerBasis.cpp b/mlir/lib/Passes/Patterns/Decomposition/EulerBasis.cpp new file mode 100644 index 0000000000..eaf496dfce --- /dev/null +++ b/mlir/lib/Passes/Patterns/Decomposition/EulerBasis.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Passes/Decomposition/EulerBasis.h" + +#include "ir/operations/OpType.hpp" + +#include +#include + +namespace mlir::qco::decomposition { + +[[nodiscard]] llvm::SmallVector +getGateTypesForEulerBasis(EulerBasis eulerBasis) { + switch (eulerBasis) { + case EulerBasis::ZYZ: + return {qc::RZ, qc::RY}; + case EulerBasis::ZXZ: + return {qc::RZ, qc::RX}; + case EulerBasis::XZX: + return {qc::RX, qc::RZ}; + case EulerBasis::XYX: + return {qc::RX, qc::RY}; + case EulerBasis::U3: + [[fallthrough]]; + case EulerBasis::U321: + [[fallthrough]]; + case EulerBasis::U: + return {qc::U}; + case EulerBasis::RR: + return {qc::R}; + case EulerBasis::ZSXX: + [[fallthrough]]; + case EulerBasis::ZSX: + return {qc::RZ, qc::SX}; + case EulerBasis::PSX: + [[fallthrough]]; + case EulerBasis::U1X: + return {qc::RZ, qc::P}; + } + llvm::reportFatalInternalError( + "Unsupported euler basis for translation to gate types"); +} + +} // namespace mlir::qco::decomposition diff --git a/mlir/lib/Passes/Patterns/Decomposition/EulerDecomposition.cpp b/mlir/lib/Passes/Patterns/Decomposition/EulerDecomposition.cpp new file mode 100644 index 0000000000..3d7fef125c --- /dev/null +++ b/mlir/lib/Passes/Patterns/Decomposition/EulerDecomposition.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Passes/Decomposition/EulerDecomposition.h" + +#include "ir/Definitions.hpp" +#include "ir/operations/OpType.hpp" +#include "mlir/Passes/Decomposition/EulerBasis.h" +#include "mlir/Passes/Decomposition/GateSequence.h" +#include "mlir/Passes/Decomposition/Helpers.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mlir::qco::decomposition { + +OneQubitGateSequence +EulerDecomposition::generateCircuit(EulerBasis targetBasis, + const Eigen::Matrix2cd& unitaryMatrix, + bool simplify, std::optional atol) { + auto [theta, phi, lambda, phase] = + anglesFromUnitary(unitaryMatrix, targetBasis); + + switch (targetBasis) { + case EulerBasis::ZYZ: + return decomposeKAK(theta, phi, lambda, phase, qc::RZ, qc::RY, simplify, + atol); + case EulerBasis::ZXZ: + return decomposeKAK(theta, phi, lambda, phase, qc::RZ, qc::RX, simplify, + atol); + case EulerBasis::XZX: + return decomposeKAK(theta, phi, lambda, phase, qc::RX, qc::RZ, simplify, + atol); + case EulerBasis::XYX: + return decomposeKAK(theta, phi, lambda, phase, qc::RX, qc::RY, simplify, + atol); + default: + llvm::reportFatalInternalError("Unsupported euler basis for circuit " + "generation in decomposition!"); + } +} + +std::array +EulerDecomposition::anglesFromUnitary(const Eigen::Matrix2cd& matrix, + EulerBasis basis) { + if (basis == EulerBasis::XYX) { + return paramsXyx(matrix); + } + if (basis == EulerBasis::XZX) { + return paramsXzx(matrix); + } + if (basis == EulerBasis::ZYZ) { + return paramsZyz(matrix); + } + if (basis == EulerBasis::ZXZ) { + return paramsZxz(matrix); + } + llvm::reportFatalInternalError( + "Unsupported euler basis for angle computation in decomposition!"); +} + +std::array +EulerDecomposition::paramsZyz(const Eigen::Matrix2cd& matrix) { + const auto detArg = std::arg(matrix.determinant()); + const auto phase = 0.5 * detArg; + const auto theta = + 2. * std::atan2(std::abs(matrix(1, 0)), std::abs(matrix(0, 0))); + const auto ang1 = std::arg(matrix(1, 1)); + const auto ang2 = std::arg(matrix(1, 0)); + const auto phi = ang1 + ang2 - detArg; + const auto lam = ang1 - ang2; + return {theta, phi, lam, phase}; +} + +std::array +EulerDecomposition::paramsZxz(const Eigen::Matrix2cd& matrix) { + const auto [theta, phi, lam, phase] = paramsZyz(matrix); + return {theta, phi + (qc::PI / 2.), lam - (qc::PI / 2.), phase}; +} + +std::array +EulerDecomposition::paramsXyx(const Eigen::Matrix2cd& matrix) { + const Eigen::Matrix2cd matZyz{ + {0.5 * (matrix(0, 0) + matrix(0, 1) + matrix(1, 0) + matrix(1, 1)), + 0.5 * (matrix(0, 0) - matrix(0, 1) + matrix(1, 0) - matrix(1, 1))}, + {0.5 * (matrix(0, 0) + matrix(0, 1) - matrix(1, 0) - matrix(1, 1)), + 0.5 * (matrix(0, 0) - matrix(0, 1) - matrix(1, 0) + matrix(1, 1))}, + }; + auto [theta, phi, lam, phase] = paramsZyz(matZyz); + auto newPhi = helpers::mod2pi(phi + qc::PI, 0.); + auto newLam = helpers::mod2pi(lam + qc::PI, 0.); + return { + theta, + newPhi, + newLam, + phase + ((newPhi + newLam - phi - lam) / 2.), + }; +} + +std::array +EulerDecomposition::paramsXzx(const Eigen::Matrix2cd& matrix) { + auto det = matrix.determinant(); + auto phase = std::imag(std::log(det)) / 2.0; + auto sqrtDet = std::sqrt(det); + const Eigen::Matrix2cd matZxz{ + { + {(matrix(0, 0) / sqrtDet).real(), (matrix(1, 0) / sqrtDet).imag()}, + {(matrix(1, 0) / sqrtDet).real(), (matrix(0, 0) / sqrtDet).imag()}, + }, + { + {-(matrix(1, 0) / sqrtDet).real(), (matrix(0, 0) / sqrtDet).imag()}, + {(matrix(0, 0) / sqrtDet).real(), -(matrix(1, 0) / sqrtDet).imag()}, + }, + }; + auto [theta, phi, lam, phase_zxz] = paramsZxz(matZxz); + return {theta, phi, lam, phase + phase_zxz}; +} + +OneQubitGateSequence EulerDecomposition::decomposeKAK( + double theta, double phi, double lambda, double phase, qc::OpType kGate, + qc::OpType aGate, bool simplify, std::optional atol) { + double angleZeroEpsilon = atol.value_or(DEFAULT_ATOL); + if (!simplify) { + // setting atol to negative value to make all angle checks true; this will + // effectively disable the simplification since all rotations appear to be + // "necessary" + angleZeroEpsilon = -1.0; + } + + OneQubitGateSequence sequence{ + .gates = {}, + .globalPhase = phase - ((phi + lambda) / 2.), + }; + if (std::abs(theta) <= angleZeroEpsilon) { + lambda += phi; + lambda = helpers::mod2pi(lambda); + if (std::abs(lambda) > angleZeroEpsilon) { + sequence.gates.push_back({.type = kGate, .parameter = {lambda}}); + sequence.globalPhase += lambda / 2.0; + } + return sequence; + } + + if (std::abs(theta - qc::PI) <= angleZeroEpsilon) { + sequence.globalPhase += phi; + lambda -= phi; + phi = 0.0; + } + if (std::abs(helpers::mod2pi(lambda + qc::PI)) <= angleZeroEpsilon || + std::abs(helpers::mod2pi(phi + qc::PI)) <= angleZeroEpsilon) { + lambda += qc::PI; + theta = -theta; + phi += qc::PI; + } + lambda = helpers::mod2pi(lambda); + if (std::abs(lambda) > angleZeroEpsilon) { + sequence.globalPhase += lambda / 2.0; + sequence.gates.push_back({.type = kGate, .parameter = {lambda}}); + } + sequence.gates.push_back({.type = aGate, .parameter = {theta}}); + phi = helpers::mod2pi(phi); + if (std::abs(phi) > angleZeroEpsilon) { + sequence.globalPhase += phi / 2.0; + sequence.gates.push_back({.type = kGate, .parameter = {phi}}); + } + return sequence; +} + +} // namespace mlir::qco::decomposition diff --git a/mlir/lib/Passes/Patterns/Decomposition/GateSequence.cpp b/mlir/lib/Passes/Patterns/Decomposition/GateSequence.cpp new file mode 100644 index 0000000000..3cf5274f5c --- /dev/null +++ b/mlir/lib/Passes/Patterns/Decomposition/GateSequence.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Passes/Decomposition/GateSequence.h" + +#include "ir/operations/OpType.hpp" +#include "mlir/Passes/Decomposition/EulerBasis.h" +#include "mlir/Passes/Decomposition/Gate.h" +#include "mlir/Passes/Decomposition/Helpers.h" +#include "mlir/Passes/Decomposition/UnitaryMatrices.h" + +#include +#include +#include +#include +#include + +namespace mlir::qco::decomposition { + +bool QubitGateSequence::hasGlobalPhase() const { + return std::abs(globalPhase) > DEFAULT_ATOL; +} + +std::size_t QubitGateSequence::complexity() const { + std::size_t c{}; + for (auto&& gate : gates) { + c += helpers::getComplexity(gate.type, gate.qubitId.size()); + } + if (hasGlobalPhase()) { + // need to add a global phase gate if a global phase needs to be applied + c += helpers::getComplexity(qc::GPhase, 0); + } + return c; +} + +Eigen::Matrix4cd QubitGateSequence::getUnitaryMatrix() const { + Eigen::Matrix4cd unitaryMatrix = Eigen::Matrix4cd::Identity(); + for (auto&& gate : gates) { + auto gateMatrix = getTwoQubitMatrix(gate); + unitaryMatrix = gateMatrix * unitaryMatrix; + } + unitaryMatrix *= helpers::globalPhaseFactor(globalPhase); + assert(helpers::isUnitaryMatrix(unitaryMatrix)); + return unitaryMatrix; +} + +} // namespace mlir::qco::decomposition diff --git a/mlir/lib/Passes/Patterns/Decomposition/Helpers.cpp b/mlir/lib/Passes/Patterns/Decomposition/Helpers.cpp new file mode 100644 index 0000000000..9e01448f35 --- /dev/null +++ b/mlir/lib/Passes/Patterns/Decomposition/Helpers.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Passes/Decomposition/Helpers.h" + +#include "ir/operations/OpType.hpp" +#include "mlir/Dialect/QCO/IR/QCODialect.h" + +#include +#include +#include +#include +#include + +namespace mlir::qco::helpers { + +qc::OpType getQcType(UnitaryOpInterface op) { + try { + auto type = op.getBaseSymbol(); + if (type == "ctrl") { + type = llvm::cast(op).getBodyUnitary().getBaseSymbol(); + } + return qc::opTypeFromString(type.str()); + } catch (const std::invalid_argument& /*exception*/) { + return qc::OpType::None; + } +} + +double remEuclid(double a, double b) { + auto r = std::fmod(a, b); + return (r < 0.0) ? r + std::abs(b) : r; +} + +double mod2pi(double angle, double angleZeroEpsilon) { + // remEuclid() isn't exactly the same as Python's % operator, but + // because the RHS here is a constant and positive it is effectively + // equivalent for this case + auto wrapped = remEuclid(angle + std::numbers::pi, 2 * std::numbers::pi) - + std::numbers::pi; + if (std::abs(wrapped - std::numbers::pi) < angleZeroEpsilon) { + return -std::numbers::pi; + } + return wrapped; +} + +double traceToFidelity(const std::complex& x) { + auto xAbs = std::abs(x); + return (4.0 + xAbs * xAbs) / 20.0; +} + +std::size_t getComplexity(qc::OpType type, std::size_t numOfQubits) { + if (numOfQubits > 1) { + constexpr std::size_t multiQubitFactor = 10; + return (numOfQubits - 1) * multiQubitFactor; + } + if (type == qc::GPhase) { + return 0; + } + return 1; +} + +std::complex globalPhaseFactor(double globalPhase) { + return std::exp(std::complex{0, 1} * globalPhase); +} + +} // namespace mlir::qco::helpers diff --git a/mlir/lib/Passes/Patterns/Decomposition/UnitaryMatrices.cpp b/mlir/lib/Passes/Patterns/Decomposition/UnitaryMatrices.cpp new file mode 100644 index 0000000000..b34bff6d7d --- /dev/null +++ b/mlir/lib/Passes/Patterns/Decomposition/UnitaryMatrices.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Passes/Decomposition/UnitaryMatrices.h" + +#include "ir/operations/OpType.hpp" +#include "mlir/Passes/Decomposition/Gate.h" + +#include +#include +#include +#include +#include +#include + +namespace mlir::qco::decomposition { + +Eigen::Matrix4cd expandToTwoQubits(const Eigen::Matrix2cd& singleQubitMatrix, + QubitId qubitId) { + if (qubitId == 0) { + return Eigen::kroneckerProduct(Eigen::Matrix2cd::Identity(), + singleQubitMatrix); + } + if (qubitId == 1) { + return Eigen::kroneckerProduct(singleQubitMatrix, + Eigen::Matrix2cd::Identity()); + } + llvm::reportFatalInternalError("Invalid qubit id for single-qubit expansion"); +} + +Eigen::Matrix4cd +fixTwoQubitMatrixQubitOrder(const Eigen::Matrix4cd& twoQubitMatrix, + const llvm::SmallVector& qubitIds) { + if (qubitIds == llvm::SmallVector{1, 0}) { + // since UnitaryOpInterface::getUnitaryMatrix() does have a static + // qubit order, adjust if we need the other direction of the gate + return decomposition::SWAP_GATE * twoQubitMatrix * decomposition::SWAP_GATE; + } + if (qubitIds == llvm::SmallVector{0, 1}) { + return twoQubitMatrix; + } + llvm::reportFatalInternalError( + "Invalid qubit IDs for fixing two-qubit matrix"); +} + +Eigen::Matrix2cd getSingleQubitMatrix(const Gate& gate) { + if (gate.type == qc::SX) { + return Eigen::Matrix2cd{ + {std::complex{0.5, 0.5}, std::complex{0.5, -0.5}}, + {std::complex{0.5, -0.5}, std::complex{0.5, 0.5}}}; + } + if (gate.type == qc::RX) { + assert(gate.parameter.size() == 1); + return rxMatrix(gate.parameter[0]); + } + if (gate.type == qc::RY) { + assert(gate.parameter.size() == 1); + return ryMatrix(gate.parameter[0]); + } + if (gate.type == qc::RZ) { + assert(gate.parameter.size() == 1); + return rzMatrix(gate.parameter[0]); + } + if (gate.type == qc::X) { + return Eigen::Matrix2cd{{0, 1}, {1, 0}}; + } + if (gate.type == qc::I) { + return Eigen::Matrix2cd::Identity(); + } + if (gate.type == qc::P) { + assert(gate.parameter.size() == 1); + return pMatrix(gate.parameter[0]); + } + if (gate.type == qc::U) { + assert(gate.parameter.size() == 3); + return uMatrix(gate.parameter[0], gate.parameter[1], gate.parameter[2]); + } + if (gate.type == qc::U2) { + assert(gate.parameter.size() == 2); + return u2Matrix(gate.parameter[0], gate.parameter[1]); + } + if (gate.type == qc::H) { + return H_GATE; + } + llvm::reportFatalInternalError( + llvm::StringRef("unsupported gate type for single qubit matrix (" + + qc::toString(gate.type) + ")")); +} + +// TODO: remove? only used for verification of circuit and in unittests +Eigen::Matrix4cd getTwoQubitMatrix(const Gate& gate) { + if (gate.qubitId.empty()) { + return Eigen::Matrix4cd::Identity(); + } + if (gate.qubitId.size() == 1) { + return expandToTwoQubits(getSingleQubitMatrix(gate), gate.qubitId[0]); + } + if (gate.qubitId.size() == 2) { + if (gate.type == qc::X) { + // controlled X (CX) + if (gate.qubitId == llvm::SmallVector{0, 1}) { + return Eigen::Matrix4cd{ + {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}}; + } + if (gate.qubitId == llvm::SmallVector{1, 0}) { + return Eigen::Matrix4cd{ + {1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 1, 0}, {0, 1, 0, 0}}; + } + llvm::reportFatalInternalError("Invalid qubit IDs for CX gate"); + } + if (gate.type == qc::RXX) { + assert(gate.parameter.size() == 1); + return rxxMatrix(gate.parameter[0]); + } + if (gate.type == qc::RYY) { + assert(gate.parameter.size() == 1); + return ryyMatrix(gate.parameter[0]); + } + if (gate.type == qc::RZZ) { + assert(gate.parameter.size() == 1); + return rzzMatrix(gate.parameter[0]); + } + if (gate.type == qc::I) { + return Eigen::Matrix4cd::Identity(); + } + llvm::reportFatalInternalError( + llvm::StringRef("Unsupported gate type for two qubit matrix (" + + qc::toString(gate.type) + ")")); + } + llvm::reportFatalInternalError( + "Invalid number of qubit IDs in compute_unitary"); +} + +} // namespace mlir::qco::decomposition diff --git a/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp b/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp new file mode 100644 index 0000000000..691b095230 --- /dev/null +++ b/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp @@ -0,0 +1,696 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Passes/Decomposition/WeylDecomposition.h" + +#include "ir/Definitions.hpp" +#include "ir/operations/OpType.hpp" +#include "mlir/Passes/Decomposition/EulerBasis.h" +#include "mlir/Passes/Decomposition/EulerDecomposition.h" +#include "mlir/Passes/Decomposition/Helpers.h" +#include "mlir/Passes/Decomposition/UnitaryMatrices.h" + +#include // NOLINT(misc-include-cleaner) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mlir::qco::decomposition { +TwoQubitWeylDecomposition +TwoQubitWeylDecomposition::create(const Eigen::Matrix4cd& unitaryMatrix, + std::optional fidelity) { + auto u = unitaryMatrix; + auto detU = u.determinant(); + auto detPow = std::pow(detU, -0.25); + u *= detPow; // remove global phase from unitary matrix + auto globalPhase = std::arg(detU) / 4.; + + // this should have normalized determinant of u, so that u ∈ SU(4) + assert(std::abs(u.determinant() - 1.0) < SANITY_CHECK_PRECISION); + + // transform unitary matrix to magic basis; this enables two properties: + // 1. if uP ∈ SO(4), V = A ⊗ B (SO(4) → SU(2) ⊗ SU(2)) + // 2. magic basis diagonalizes canonical gate, allowing calculation of + // canonical gate parameters later on + auto uP = magicBasisTransform(u, MagicBasisTransform::OutOf); + const Eigen::Matrix4cd m2 = uP.transpose() * uP; + + // diagonalization yields eigenvectors (p) and eigenvalues (d); + // p is used to calculate K1/K2 (and thus the single-qubit gates + // surrounding the canonical gate); d is is used to determine the weyl + // coordinates and thus the parameters of the canonical gate + // TODO: it may be possible to lower the precision + auto [p, d] = diagonalizeComplexSymmetric(m2, 1e-13); + + // extract Weyl coordinates from eigenvalues, map to [0, 2*pi) + // NOLINTNEXTLINE(misc-include-cleaner) + Eigen::Vector3d cs; + Eigen::Vector4d dReal = -1.0 * d.cwiseArg() / 2.0; + dReal(3) = -dReal(0) - dReal(1) - dReal(2); + for (int i = 0; i < cs.size(); ++i) { + assert(i < dReal.size()); + cs[i] = helpers::remEuclid((dReal(i) + dReal(3)) / 2.0, qc::TAU); + } + + // re-order coordinates and according to min(a, pi/2 - a) with + // a = x mod pi/2 for each weyl coordinate x + decltype(cs) cstemp; + llvm::transform(cs, cstemp.begin(), [](auto&& x) { + auto tmp = helpers::remEuclid(x, qc::PI_2); + return std::min(tmp, qc::PI_2 - tmp); + }); + std::array order{0, 1, 2}; + llvm::stable_sort(order, + [&](auto a, auto b) { return cstemp[a] < cstemp[b]; }); + std::tie(order[0], order[1], order[2]) = + std::tuple{order[1], order[2], order[0]}; + std::tie(cs[0], cs[1], cs[2]) = + std::tuple{cs[order[0]], cs[order[1]], cs[order[2]]}; + std::tie(dReal(0), dReal(1), dReal(2)) = + std::tuple{dReal(order[0]), dReal(order[1]), dReal(order[2])}; + + // update eigenvectors (columns of p) according to new order of + // weyl coordinates + Eigen::Matrix4cd pOrig = p; + for (int i = 0; std::cmp_less(i, order.size()); ++i) { + p.col(i) = pOrig.col(order[i]); + } + // apply correction for determinant if necessary + if (p.determinant().real() < 0.0) { + auto lastColumnIndex = p.cols() - 1; + p.col(lastColumnIndex) *= -1.0; + } + assert(std::abs(p.determinant() - 1.0) < SANITY_CHECK_PRECISION); + + // re-create complex eigenvalue matrix; this matrix contains the + // parameters of the canonical gate which is later used in the + // verification + Eigen::Matrix4cd temp = dReal.asDiagonal(); + temp *= std::complex{0, 1}; + // since the matrix is diagonal, matrix exponential is equivalent to + // element-wise exponential function + temp.diagonal() = temp.diagonal().array().exp().matrix(); + + // combined matrix k1 of 1q gates after canonical gate + Eigen::Matrix4cd k1 = uP * p * temp; + assert((k1.transpose() * k1).isIdentity()); // k1 must be orthogonal + assert(k1.determinant().real() > 0.0); + k1 = magicBasisTransform(k1, MagicBasisTransform::Into); + + // combined matrix k2 of 1q gates before canonical gate + Eigen::Matrix4cd k2 = p.transpose().conjugate(); + assert((k2.transpose() * k2).isIdentity()); // k2 must be orthogonal + assert(k2.determinant().real() > 0.0); + k2 = magicBasisTransform(k2, MagicBasisTransform::Into); + + // ensure k1 and k2 are correct (when combined with the canonical gate + // parameters in-between, they are equivalent to u) + assert((k1 * + magicBasisTransform(temp.conjugate(), MagicBasisTransform::Into) * k2) + .isApprox(u, SANITY_CHECK_PRECISION)); + + // calculate k1 = K1l ⊗ K1r + auto [K1l, K1r, phase_l] = decomposeTwoQubitProductGate(k1); + // decompose k2 = K2l ⊗ K2r + auto [K2l, K2r, phase_r] = decomposeTwoQubitProductGate(k2); + assert( + Eigen::kroneckerProduct(K1l, K1r).isApprox(k1, SANITY_CHECK_PRECISION)); + assert( + Eigen::kroneckerProduct(K2l, K2r).isApprox(k2, SANITY_CHECK_PRECISION)); + // accumulate global phase + globalPhase += phase_l + phase_r; + + // Flip into Weyl chamber + if (cs[0] > qc::PI_2) { + cs[0] -= 3.0 * qc::PI_2; + K1l = K1l * IPY; + K1r = K1r * IPY; + globalPhase += qc::PI_2; + } + if (cs[1] > qc::PI_2) { + cs[1] -= 3.0 * qc::PI_2; + K1l = K1l * IPX; + K1r = K1r * IPX; + globalPhase += qc::PI_2; + } + auto conjs = 0; + if (cs[0] > qc::PI_4) { + cs[0] = qc::PI_2 - cs[0]; + K1l = K1l * IPY; + K2r = IPY * K2r; + conjs += 1; + globalPhase -= qc::PI_2; + } + if (cs[1] > qc::PI_4) { + cs[1] = qc::PI_2 - cs[1]; + K1l = K1l * IPX; + K2r = IPX * K2r; + conjs += 1; + globalPhase += qc::PI_2; + if (conjs == 1) { + globalPhase -= qc::PI; + } + } + if (cs[2] > qc::PI_2) { + cs[2] -= 3.0 * qc::PI_2; + K1l = K1l * IPZ; + K1r = K1r * IPZ; + globalPhase += qc::PI_2; + if (conjs == 1) { + globalPhase -= qc::PI; + } + } + if (conjs == 1) { + cs[2] = qc::PI_2 - cs[2]; + K1l = K1l * IPZ; + K2r = IPZ * K2r; + globalPhase += qc::PI_2; + } + if (cs[2] > qc::PI_4) { + cs[2] -= qc::PI_2; + K1l = K1l * IPZ; + K1r = K1r * IPZ; + globalPhase -= qc::PI_2; + } + + // bind weyl coordinates as parameters of canonical gate + auto [a, b, c] = std::tie(cs[1], cs[0], cs[2]); + + TwoQubitWeylDecomposition decomposition; + decomposition.a_ = a; + decomposition.b_ = b; + decomposition.c_ = c; + decomposition.globalPhase_ = globalPhase; + decomposition.k1l_ = K1l; + decomposition.k2l_ = K2l; + decomposition.k1r_ = K1r; + decomposition.k2r_ = K2r; + decomposition.specialization = Specialization::General; + decomposition.defaultEulerBasis = EulerBasis::ZYZ; + decomposition.requestedFidelity = fidelity; + // will be calculated if a specialization is used; set to -1 for now + decomposition.calculatedFidelity = -1.0; + decomposition.unitaryMatrix = unitaryMatrix; + + // make sure decomposition is equal to input + assert( + (Eigen::kroneckerProduct(K1l, K1r) * decomposition.getCanonicalMatrix() * + Eigen::kroneckerProduct(K2l, K2r) * + helpers::globalPhaseFactor(globalPhase)) + .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); + + // determine actual specialization of canonical gate so that the 1q + // matrices can potentially be simplified + auto flippedFromOriginal = decomposition.applySpecialization(); + + auto getTrace = [&]() { + if (flippedFromOriginal) { + return TwoQubitWeylDecomposition::getTrace( + qc::PI_2 - a, b, -c, decomposition.a_, decomposition.b_, + decomposition.c_); + } + return TwoQubitWeylDecomposition::getTrace( + a, b, c, decomposition.a_, decomposition.b_, decomposition.c_); + }; + // use trace to calculate fidelity of applied specialization and + // adjust global phase + auto trace = getTrace(); + decomposition.calculatedFidelity = helpers::traceToFidelity(trace); + // final check if specialization is close enough to the original matrix to + // satisfy the requested fidelity; since no forced specialization is + // allowed, this should never fail + if (decomposition.requestedFidelity && + decomposition.calculatedFidelity + 1.0e-13 < + *decomposition.requestedFidelity) { + llvm::reportFatalInternalError(llvm::formatv( + "TwoQubitWeylDecomposition: Calculated fidelity of " + "specialization is worse than requested fidelity ({0:F4} vs {1:F4})!", + decomposition.calculatedFidelity, *decomposition.requestedFidelity)); + } + decomposition.globalPhase_ += std::arg(trace); + + // final check if decomposition is still valid after specialization + assert((Eigen::kroneckerProduct(decomposition.k1l_, decomposition.k1r_) * + decomposition.getCanonicalMatrix() * + Eigen::kroneckerProduct(decomposition.k2l_, decomposition.k2r_) * + helpers::globalPhaseFactor(decomposition.globalPhase_)) + .isApprox(unitaryMatrix, SANITY_CHECK_PRECISION)); + + return decomposition; +} + +Eigen::Matrix4cd +TwoQubitWeylDecomposition::getCanonicalMatrix(double a, double b, double c) { + auto xx = getTwoQubitMatrix({ + .type = qc::RXX, + .parameter = {-2.0 * a}, + .qubitId = {0, 1}, + }); + auto yy = getTwoQubitMatrix({ + .type = qc::RYY, + .parameter = {-2.0 * b}, + .qubitId = {0, 1}, + }); + auto zz = getTwoQubitMatrix({ + .type = qc::RZZ, + .parameter = {-2.0 * c}, + .qubitId = {0, 1}, + }); + return zz * yy * xx; +} + +Eigen::Matrix4cd +TwoQubitWeylDecomposition::magicBasisTransform(const Eigen::Matrix4cd& unitary, + MagicBasisTransform direction) { + using namespace std::complex_literals; + const Eigen::Matrix4cd bNonNormalized{ + {1, 1i, 0, 0}, + {0, 0, 1i, 1}, + {0, 0, 1i, -1}, + {1, -1i, 0, 0}, + }; + + const Eigen::Matrix4cd bNonNormalizedDagger{ + {0.5, 0, 0, 0.5}, + {-0.5i, 0, 0, 0.5i}, + {0, -0.5i, -0.5i, 0}, + {0, 0.5, -0.5, 0}, + }; + if (direction == MagicBasisTransform::OutOf) { + return bNonNormalizedDagger * unitary * bNonNormalized; + } + if (direction == MagicBasisTransform::Into) { + return bNonNormalized * unitary * bNonNormalizedDagger; + } + llvm::reportFatalInternalError("Unknown MagicBasisTransform direction!"); +} + +double TwoQubitWeylDecomposition::closestPartialSwap(double a, double b, + double c) { + auto m = (a + b + c) / 3.; + auto [am, bm, cm] = std::array{a - m, b - m, c - m}; + auto [ab, bc, ca] = std::array{a - b, b - c, c - a}; + return m + (am * bm * cm * (6. + ab * ab + bc * bc + ca * ca) / 18.); +} + +/** + * Diagonalize given complex symmetric matrix M into (P, d) using a + * randomized algorithm. + * This approach is used in both qiskit and quantumflow. + * + * P is the matrix of real or orthogonal eigenvectors of M with P ∈ SO(4) + * d is a vector containing sqrt(eigenvalues) of M with unit-magnitude + * elements (for each element, complex magnitude is 1.0). + * D is d as a diagonal matrix. + * + * M = P * D * P^T + * + * @return pair of (P, D.diagonal()) + */ +std::pair +TwoQubitWeylDecomposition::diagonalizeComplexSymmetric( + const Eigen::Matrix4cd& m, double precision) { + // We can't use raw `eig` directly because it isn't guaranteed to give + // us real or orthogonal eigenvectors. Instead, since `M` is + // complex-symmetric, + // M = A + iB + // for real-symmetric `A` and `B`, and as + // M^+ @ M2 = A^2 + B^2 + i [A, B] = 1 + // we must have `A` and `B` commute, and consequently they are + // simultaneously diagonalizable. Mixing them together _should_ account + // for any degeneracy problems, but it's not guaranteed, so we repeat it + // a little bit. The fixed seed is to make failures deterministic; the + // value is not important. + auto state = std::mt19937{2023}; + std::normal_distribution dist; + + constexpr auto maxDiagonalizationAttempts = 100; + for (int i = 0; i < maxDiagonalizationAttempts; ++i) { + double randA{}; + double randB{}; + // For debugging the algorithm use the same RNG values as the + // Qiskit implementation for the first random trial. + // In most cases this loop only executes a single iteration and + // using the same rng values rules out possible RNG differences + // as the root cause of a test failure + if (i == 0) { + randA = 1.2602066112249388; + randB = 0.22317849046722027; + } else { + randA = dist(state); + randB = dist(state); + } + const Eigen::Matrix4d m2Real = randA * m.real() + randB * m.imag(); + auto&& pReal = helpers::selfAdjointEvd(m2Real).first; + const Eigen::Matrix4cd p = pReal; + auto&& d = (p.transpose() * m * p).diagonal(); + + auto&& compare = p * d.asDiagonal() * p.transpose(); + if (compare.isApprox(m, precision)) { + // p are the eigenvectors which are decomposed into the + // single-qubit gates surrounding the canonical gate + // d is the sqrt of the eigenvalues that are used to determine the + // weyl coordinates and thus the parameters of the canonical gate + // check that p is in SO(4) + assert((p.transpose() * p).isIdentity(SANITY_CHECK_PRECISION)); + // make sure determinant of eigenvalues is 1.0 + assert(std::abs(Eigen::Matrix4cd{d.asDiagonal()}.determinant() - 1.0) < + SANITY_CHECK_PRECISION); + return std::make_pair(p, d); + } + } + llvm::reportFatalInternalError( + "TwoQubitWeylDecomposition: failed to diagonalize M2 (" + + llvm::Twine(maxDiagonalizationAttempts) + " iterations)."); +} + +std::tuple +TwoQubitWeylDecomposition::decomposeTwoQubitProductGate( + const Eigen::Matrix4cd& specialUnitary) { + // for alternative approaches, see + // pennylane's math.decomposition.su2su2_to_tensor_products + // or quantumflow.kronecker_decomposition + + // first quadrant + Eigen::Matrix2cd r{{specialUnitary(0, 0), specialUnitary(0, 1)}, + {specialUnitary(1, 0), specialUnitary(1, 1)}}; + auto detR = r.determinant(); + if (std::abs(detR) < 0.1) { + // third quadrant + r = Eigen::Matrix2cd{{specialUnitary(2, 0), specialUnitary(2, 1)}, + {specialUnitary(3, 0), specialUnitary(3, 1)}}; + detR = r.determinant(); + } + if (std::abs(detR) < 0.1) { + llvm::reportFatalInternalError( + "decompose_two_qubit_product_gate: unable to decompose: det_r < 0.1"); + } + r /= std::sqrt(detR); + // transpose with complex conjugate of each element + const Eigen::Matrix2cd rTConj = r.transpose().conjugate(); + + Eigen::Matrix4cd temp = + Eigen::kroneckerProduct(Eigen::Matrix2cd::Identity(), rTConj); + temp = specialUnitary * temp; + + // [[a, b, c, d], + // [e, f, g, h], => [[a, c], + // [i, j, k, l], [i, k]] + // [m, n, o, p]] + Eigen::Matrix2cd l{{temp(0, 0), temp(0, 2)}, {temp(2, 0), temp(2, 2)}}; + auto detL = l.determinant(); + if (std::abs(detL) < 0.9) { + llvm::reportFatalInternalError( + "decompose_two_qubit_product_gate: unable to decompose: detL < 0.9"); + } + l /= std::sqrt(detL); + auto phase = std::arg(detL) / 2.; + + return {l, r, phase}; +} + +std::complex TwoQubitWeylDecomposition::getTrace(double a, double b, + double c, double ap, + double bp, double cp) { + auto da = a - ap; + auto db = b - bp; + auto dc = c - cp; + return 4. * std::complex{std::cos(da) * std::cos(db) * std::cos(dc), + std::sin(da) * std::sin(db) * std::sin(dc)}; +} + +TwoQubitWeylDecomposition::Specialization +TwoQubitWeylDecomposition::bestSpecialization() const { + auto isClose = [this](double ap, double bp, double cp) -> bool { + auto tr = getTrace(a_, b_, c_, ap, bp, cp); + if (requestedFidelity) { + return helpers::traceToFidelity(tr) >= *requestedFidelity; + } + return false; + }; + + auto closestAbc = closestPartialSwap(a_, b_, c_); + auto closestAbMinusC = closestPartialSwap(a_, b_, -c_); + + if (isClose(0., 0., 0.)) { + return Specialization::IdEquiv; + } + if (isClose(qc::PI_4, qc::PI_4, qc::PI_4) || + isClose(qc::PI_4, qc::PI_4, -qc::PI_4)) { + return Specialization::SWAPEquiv; + } + if (isClose(closestAbc, closestAbc, closestAbc)) { + return Specialization::PartialSWAPEquiv; + } + if (isClose(closestAbMinusC, closestAbMinusC, -closestAbMinusC)) { + return Specialization::PartialSWAPFlipEquiv; + } + if (isClose(a_, 0., 0.)) { + return Specialization::ControlledEquiv; + } + if (isClose(qc::PI_4, qc::PI_4, c_)) { + return Specialization::MirrorControlledEquiv; + } + if (isClose((a_ + b_) / 2., (a_ + b_) / 2., c_)) { + return Specialization::FSimaabEquiv; + } + if (isClose(a_, (b_ + c_) / 2., (b_ + c_) / 2.)) { + return Specialization::FSimabbEquiv; + } + if (isClose(a_, (b_ - c_) / 2., (c_ - b_) / 2.)) { + return Specialization::FSimabmbEquiv; + } + return Specialization::General; +} + +bool TwoQubitWeylDecomposition::applySpecialization() { + if (specialization != Specialization::General) { + llvm::reportFatalInternalError( + "Application of specialization only works on " + "general Weyl decompositions!"); + } + bool flippedFromOriginal = false; + auto newSpecialization = bestSpecialization(); + if (newSpecialization == Specialization::General) { + // U has no special symmetry. + // + // This gate binds all 6 possible parameters, so there is no need to + // make the single-qubit pre-/post-gates canonical. + return flippedFromOriginal; + } + specialization = newSpecialization; + + if (newSpecialization == Specialization::IdEquiv) { + // :math:`U \sim U_d(0,0,0)` + // Thus, :math:`\sim Id` + // + // This gate binds 0 parameters, we make it canonical by setting: + // + // :math:`K2_l = Id` , :math:`K2_r = Id`. + a_ = 0.; + b_ = 0.; + c_ = 0.; + // unmodified global phase + k1l_ = k1l_ * k2l_; + k2l_ = Eigen::Matrix2cd::Identity(); + k1r_ = k1r_ * k2r_; + k2r_ = Eigen::Matrix2cd::Identity(); + } else if (newSpecialization == Specialization::SWAPEquiv) { + // :math:`U \sim U_d(\pi/4, \pi/4, \pi/4) \sim U(\pi/4, \pi/4, -\pi/4)` + // Thus, :math:`U \sim \text{SWAP}` + // + // This gate binds 0 parameters, we make it canonical by setting: + // + // :math:`K2_l = Id` , :math:`K2_r = Id`. + if (c_ > 0.) { + // unmodified global phase + k1l_ = k1l_ * k2r_; + k1r_ = k1r_ * k2l_; + k2l_ = Eigen::Matrix2cd::Identity(); + k2r_ = Eigen::Matrix2cd::Identity(); + } else { + flippedFromOriginal = true; + + globalPhase_ += qc::PI_2; + k1l_ = k1l_ * IPZ * k2r_; + k1r_ = k1r_ * IPZ * k2l_; + k2l_ = Eigen::Matrix2cd::Identity(); + k2r_ = Eigen::Matrix2cd::Identity(); + } + a_ = qc::PI_4; + b_ = qc::PI_4; + c_ = qc::PI_4; + } else if (newSpecialization == Specialization::PartialSWAPEquiv) { + // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, \alpha\pi/4)` + // Thus, :math:`U \sim \text{SWAP}^\alpha` + // + // This gate binds 3 parameters, we make it canonical by setting: + // + // :math:`K2_l = Id`. + auto closest = closestPartialSwap(a_, b_, c_); + auto k2lDagger = k2l_.transpose().conjugate(); + + a_ = closest; + b_ = closest; + c_ = closest; + // unmodified global phase + k1l_ = k1l_ * k2l_; + k1r_ = k1r_ * k2l_; + k2r_ = k2lDagger * k2r_; + k2l_ = Eigen::Matrix2cd::Identity(); + } else if (newSpecialization == Specialization::PartialSWAPFlipEquiv) { + // :math:`U \sim U_d(\alpha\pi/4, \alpha\pi/4, -\alpha\pi/4)` + // Thus, :math:`U \sim \text{SWAP}^\alpha` + // + // (a non-equivalent root of SWAP from the TwoQubitWeylPartialSWAPEquiv + // similar to how :math:`x = (\pm \sqrt(x))^2`) + // + // This gate binds 3 parameters, we make it canonical by setting: + // + // :math:`K2_l = Id` + auto closest = closestPartialSwap(a_, b_, -c_); + auto k2lDagger = k2l_.transpose().conjugate(); + + a_ = closest; + b_ = closest; + c_ = -closest; + // unmodified global phase + k1l_ = k1l_ * k2l_; + k1r_ = k1r_ * IPZ * k2l_ * IPZ; + k2r_ = IPZ * k2lDagger * IPZ * k2r_; + k2l_ = Eigen::Matrix2cd::Identity(); + } else if (newSpecialization == Specialization::ControlledEquiv) { + // :math:`U \sim U_d(\alpha, 0, 0)` + // Thus, :math:`U \sim \text{Ctrl-U}` + // + // This gate binds 4 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l) Rx(\lambda_l)` + // :math:`K2_r = Ry(\theta_r) Rx(\lambda_r)` + auto eulerBasis = EulerBasis::XYX; + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + EulerDecomposition::anglesFromUnitary(k2l_, eulerBasis); + auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = + EulerDecomposition::anglesFromUnitary(k2r_, eulerBasis); + + // unmodified parameter a + b_ = 0.; + c_ = 0.; + globalPhase_ = globalPhase_ + k2lphase + k2rphase; + k1l_ = k1l_ * rxMatrix(k2lphi); + k2l_ = ryMatrix(k2ltheta) * rxMatrix(k2llambda); + k1r_ = k1r_ * rxMatrix(k2rphi); + k2r_ = ryMatrix(k2rtheta) * rxMatrix(k2rlambda); + defaultEulerBasis = eulerBasis; + } else if (newSpecialization == Specialization::MirrorControlledEquiv) { + // :math:`U \sim U_d(\pi/4, \pi/4, \alpha)` + // Thus, :math:`U \sim \text{SWAP} \cdot \text{Ctrl-U}` + // + // This gate binds 4 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)` + // :math:`K2_r = Ry(\theta_r)\cdot Rz(\lambda_r)` + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + EulerDecomposition::anglesFromUnitary(k2l_, EulerBasis::ZYZ); + auto [k2rtheta, k2rphi, k2rlambda, k2rphase] = + EulerDecomposition::anglesFromUnitary(k2r_, EulerBasis::ZYZ); + + a_ = qc::PI_4; + b_ = qc::PI_4; + // unmodified parameter c + globalPhase_ = globalPhase_ + k2lphase + k2rphase; + k1l_ = k1l_ * rzMatrix(k2rphi); + k2l_ = ryMatrix(k2ltheta) * rzMatrix(k2llambda); + k1r_ = k1r_ * rzMatrix(k2lphi); + k2r_ = ryMatrix(k2rtheta) * rzMatrix(k2rlambda); + } else if (newSpecialization == Specialization::FSimaabEquiv) { + // :math:`U \sim U_d(\alpha, \alpha, \beta), \alpha \geq |\beta|` + // + // This gate binds 5 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)`. + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + EulerDecomposition::anglesFromUnitary(k2l_, EulerBasis::ZYZ); + auto ab = (a_ + b_) / 2.; + + a_ = ab; + b_ = ab; + // unmodified parameter c + globalPhase_ = globalPhase_ + k2lphase; + k1l_ = k1l_ * rzMatrix(k2lphi); + k2l_ = ryMatrix(k2ltheta) * rzMatrix(k2llambda); + k1r_ = k1r_ * rzMatrix(k2lphi); + k2r_ = rzMatrix(-k2lphi) * k2r_; + } else if (newSpecialization == Specialization::FSimabbEquiv) { + // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` + // + // This gate binds 5 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` + auto eulerBasis = EulerBasis::XYX; + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + EulerDecomposition::anglesFromUnitary(k2l_, eulerBasis); + auto bc = (b_ + c_) / 2.; + + // unmodified parameter a + b_ = bc; + c_ = bc; + globalPhase_ = globalPhase_ + k2lphase; + k1l_ = k1l_ * rxMatrix(k2lphi); + k2l_ = ryMatrix(k2ltheta) * rxMatrix(k2llambda); + k1r_ = k1r_ * rxMatrix(k2lphi); + k2r_ = rxMatrix(-k2lphi) * k2r_; + defaultEulerBasis = eulerBasis; + } else if (newSpecialization == Specialization::FSimabmbEquiv) { + // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` + // + // This gate binds 5 parameters, we make it canonical by setting: + // + // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` + auto eulerBasis = EulerBasis::XYX; + auto [k2ltheta, k2lphi, k2llambda, k2lphase] = + EulerDecomposition::anglesFromUnitary(k2l_, eulerBasis); + auto bc = (b_ - c_) / 2.; + + // unmodified parameter a + b_ = bc; + c_ = -bc; + globalPhase_ = globalPhase_ + k2lphase; + k1l_ = k1l_ * rxMatrix(k2lphi); + k2l_ = ryMatrix(k2ltheta) * rxMatrix(k2llambda); + k1r_ = k1r_ * IPZ * rxMatrix(k2lphi) * IPZ; + k2r_ = IPZ * rxMatrix(-k2lphi) * IPZ * k2r_; + defaultEulerBasis = eulerBasis; + } else { + llvm::reportFatalInternalError( + "Unknown specialization for Weyl decomposition!"); + } + return flippedFromOriginal; +} + +} // namespace mlir::qco::decomposition diff --git a/mlir/unittests/Passes/Decomposition/CMakeLists.txt b/mlir/unittests/Passes/Decomposition/CMakeLists.txt index ca2cb4cd9d..878daea605 100644 --- a/mlir/unittests/Passes/Decomposition/CMakeLists.txt +++ b/mlir/unittests/Passes/Decomposition/CMakeLists.txt @@ -22,7 +22,8 @@ if(NOT TARGET ${testname}) MLIRTransforms MQTCompilerPipeline MQT::CoreIR - Eigen3::Eigen) + Eigen3::Eigen + QcoPasses) # discover tests gtest_discover_tests(${testname} DISCOVERY_TIMEOUT 60) set_target_properties(${testname} PROPERTIES FOLDER unittests) From bb27f476aa3a872394cf7932e2abd3b4284a595d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 7 Feb 2026 13:26:17 +0000 Subject: [PATCH 207/237] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index 972a73446e..99ebb81204 100644 --- a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -22,8 +22,8 @@ inline constexpr double SQRT2 = 1.414213562373095048801688724209698079L; inline constexpr double FRAC1_SQRT2 = 0.707106781186547524400844362104849039284835937688474036588L; -[[nodiscard]] constexpr Eigen::Matrix2cd -uMatrix(double lambda, double phi, double theta) { +[[nodiscard]] constexpr Eigen::Matrix2cd uMatrix(double lambda, double phi, + double theta) { return Eigen::Matrix2cd{{{{std::cos(theta / 2.), 0.}, {-std::cos(lambda) * std::sin(theta / 2.), -std::sin(lambda) * std::sin(theta / 2.)}}, @@ -33,8 +33,7 @@ uMatrix(double lambda, double phi, double theta) { std::sin(lambda + phi) * std::cos(theta / 2.)}}}}; } -[[nodiscard]] constexpr Eigen::Matrix2cd u2Matrix(double lambda, - double phi) { +[[nodiscard]] constexpr Eigen::Matrix2cd u2Matrix(double lambda, double phi) { return Eigen::Matrix2cd{ {FRAC1_SQRT2, {-std::cos(lambda) * FRAC1_SQRT2, -std::sin(lambda) * FRAC1_SQRT2}}, From be46de486deaa3644005c814c8e80fcbafd2fc28 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 7 Feb 2026 14:38:24 +0100 Subject: [PATCH 208/237] incorporate coderabbitai feedback and fix linter issues --- .../mlir/Passes/Decomposition/EulerBasis.h | 3 +-- mlir/include/mlir/Passes/Decomposition/Helpers.h | 1 + .../mlir/Passes/Decomposition/UnitaryMatrices.h | 4 ++-- .../Passes/Decomposition/WeylDecomposition.h | 3 ++- .../Patterns/Decomposition/BasisDecomposer.cpp | 16 +++++----------- .../Decomposition/EulerDecomposition.cpp | 3 +-- .../Passes/Patterns/Decomposition/Helpers.cpp | 1 + .../Patterns/Decomposition/UnitaryMatrices.cpp | 1 + .../Patterns/Decomposition/WeylDecomposition.cpp | 7 ------- .../Passes/Patterns/GateDecompositionPattern.cpp | 10 ++++++++++ 10 files changed, 24 insertions(+), 25 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/EulerBasis.h b/mlir/include/mlir/Passes/Decomposition/EulerBasis.h index 25d0fa99e2..9d9857c871 100644 --- a/mlir/include/mlir/Passes/Decomposition/EulerBasis.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerBasis.h @@ -14,13 +14,12 @@ #include #include -#include namespace mlir::qco::decomposition { /** * Largest number that will be assumed as zero for the euler decompositions. */ -static constexpr auto DEFAULT_ATOL = 1e-12; +inline constexpr auto DEFAULT_ATOL = 1e-12; /** * EulerBasis for a euler decomposition. diff --git a/mlir/include/mlir/Passes/Decomposition/Helpers.h b/mlir/include/mlir/Passes/Decomposition/Helpers.h index ac5b1b08b5..ec48ab1cb8 100644 --- a/mlir/include/mlir/Passes/Decomposition/Helpers.h +++ b/mlir/include/mlir/Passes/Decomposition/Helpers.h @@ -16,6 +16,7 @@ #include #include #include +#include namespace mlir::qco::helpers { diff --git a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index 99ebb81204..603a31fad3 100644 --- a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -97,8 +97,8 @@ inline constexpr double FRAC1_SQRT2 = inline constexpr Eigen::Matrix4cd SWAP_GATE{ {1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}; -inline constexpr Eigen::Matrix2cd H_GATE{{1.0 / SQRT2, 1.0 / SQRT2}, - {1.0 / SQRT2, -1.0 / SQRT2}}; +inline constexpr Eigen::Matrix2cd H_GATE{{FRAC1_SQRT2, FRAC1_SQRT2}, + {FRAC1_SQRT2, -FRAC1_SQRT2}}; inline constexpr Eigen::Matrix2cd IPZ{{{0, 1}, 0}, {0, {0, -1}}}; inline constexpr Eigen::Matrix2cd IPY{{0, 1}, {-1, 0}}; inline constexpr Eigen::Matrix2cd IPX{{0, {0, 1}}, {{0, 1}, 0}}; diff --git a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index cec1cabc34..b54330b3ea 100644 --- a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -49,6 +49,7 @@ class TwoQubitWeylDecomposition { static TwoQubitWeylDecomposition create(const Eigen::Matrix4cd& unitaryMatrix, std::optional fidelity); + ~TwoQubitWeylDecomposition() = default; TwoQubitWeylDecomposition(const TwoQubitWeylDecomposition&) = default; TwoQubitWeylDecomposition(TwoQubitWeylDecomposition&&) = default; TwoQubitWeylDecomposition& @@ -192,7 +193,7 @@ class TwoQubitWeylDecomposition { getTrace(double a, double b, double c, double ap, double bp, double cp); /** - * Choose the best specialization for the for the canonical gate. + * Choose the best specialization for the canonical gate. * This will use the requestedFidelity to determine if a specialization is * close enough to the actual canonical gate matrix. */ diff --git a/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp b/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp index 765e83397e..960e42d39b 100644 --- a/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp +++ b/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp @@ -13,6 +13,7 @@ #include "ir/Definitions.hpp" #include "mlir/Passes/Decomposition/EulerBasis.h" #include "mlir/Passes/Decomposition/EulerDecomposition.h" +#include "mlir/Passes/Decomposition/Gate.h" #include "mlir/Passes/Decomposition/GateSequence.h" #include "mlir/Passes/Decomposition/Helpers.h" #include "mlir/Passes/Decomposition/UnitaryMatrices.h" @@ -26,16 +27,8 @@ #include #include #include -#include #include -#include -#include -#include -#include -#include -#include #include -#include #include #include @@ -198,7 +191,7 @@ std::optional TwoQubitBasisDecomposer::twoQubitDecompose( }; double actualBasisFidelity = getBasisFidelity(); auto traces = this->traces(targetDecomposition); - auto getDefaultNbasis = [&]() { + auto getDefaultNbasis = [&]() -> std::uint8_t { // determine smallest number of basis gates required to fulfill given // basis fidelity constraint auto bestValue = std::numeric_limits::lowest(); @@ -213,8 +206,9 @@ std::optional TwoQubitBasisDecomposer::twoQubitDecompose( bestValue = value; } } - // index in traces equals number of basis gates - return bestIndex; + // index in traces equals number of basis gates; return -1/255 if no + // matching number of basis gates was found (should never happen) + return static_cast(bestIndex); }; // number of basis gates that need to be used in the decomposition auto bestNbasis = numBasisGateUses.value_or(getDefaultNbasis()); diff --git a/mlir/lib/Passes/Patterns/Decomposition/EulerDecomposition.cpp b/mlir/lib/Passes/Patterns/Decomposition/EulerDecomposition.cpp index 3d7fef125c..8c0f2eeeb1 100644 --- a/mlir/lib/Passes/Patterns/Decomposition/EulerDecomposition.cpp +++ b/mlir/lib/Passes/Patterns/Decomposition/EulerDecomposition.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -113,7 +112,7 @@ EulerDecomposition::paramsXyx(const Eigen::Matrix2cd& matrix) { std::array EulerDecomposition::paramsXzx(const Eigen::Matrix2cd& matrix) { auto det = matrix.determinant(); - auto phase = std::imag(std::log(det)) / 2.0; + auto phase = 0.5 * std::arg(det); auto sqrtDet = std::sqrt(det); const Eigen::Matrix2cd matZxz{ { diff --git a/mlir/lib/Passes/Patterns/Decomposition/Helpers.cpp b/mlir/lib/Passes/Patterns/Decomposition/Helpers.cpp index 9e01448f35..770d6d8816 100644 --- a/mlir/lib/Passes/Patterns/Decomposition/Helpers.cpp +++ b/mlir/lib/Passes/Patterns/Decomposition/Helpers.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include diff --git a/mlir/lib/Passes/Patterns/Decomposition/UnitaryMatrices.cpp b/mlir/lib/Passes/Patterns/Decomposition/UnitaryMatrices.cpp index b34bff6d7d..9c1bd664b0 100644 --- a/mlir/lib/Passes/Patterns/Decomposition/UnitaryMatrices.cpp +++ b/mlir/lib/Passes/Patterns/Decomposition/UnitaryMatrices.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp b/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp index 691b095230..e7aaf58b2a 100644 --- a/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp +++ b/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp @@ -26,14 +26,7 @@ #include #include #include -#include -#include -#include -#include -#include -#include #include -#include #include #include #include diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 93695f96f7..cb60f08f51 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -339,8 +339,15 @@ struct GateDecompositionPattern final TwoQubitSeries() = default; /** * Initialize TwoQubitSeries instance with given first operation. + * If the operation is not valid for a one- or two-qubit series, + * leave in/out qubits unitialized and the gate list empty. */ explicit TwoQubitSeries(UnitaryOpInterface initialOperation) { + if (isBarrier(initialOperation)) { + // not a valid single- or two-qubit series + // (barrier cannot be decomposed) + return; + } if (initialOperation.isSingleQubit()) { inQubits = {initialOperation.getInputQubit(0), mlir::Value{}}; outQubits = {initialOperation.getOutputQubit(0), mlir::Value{}}; @@ -351,6 +358,9 @@ struct GateDecompositionPattern final outQubits = {initialOperation.getOutputQubit(0), initialOperation.getOutputQubit(1)}; gates.push_back({.op = initialOperation, .qubitIds = {0, 1}}); + } else { + // not a valid single- or two-qubit series (more than two qubits) + return; } complexity += helpers::getComplexity(helpers::getQcType(initialOperation), initialOperation.getNumQubits()); From 4f608e57482776cc2f77442e365e98e67738a681 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 7 Feb 2026 15:01:31 +0100 Subject: [PATCH 209/237] remove constexpr from gate constants --- .../mlir/Passes/Decomposition/UnitaryMatrices.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index 603a31fad3..3ddfd73372 100644 --- a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -95,13 +95,13 @@ inline constexpr double FRAC1_SQRT2 = return Eigen::Matrix2cd{{1, 0}, {0, {std::cos(lambda), std::sin(lambda)}}}; } -inline constexpr Eigen::Matrix4cd SWAP_GATE{ +inline const Eigen::Matrix4cd SWAP_GATE{ {1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}; -inline constexpr Eigen::Matrix2cd H_GATE{{FRAC1_SQRT2, FRAC1_SQRT2}, - {FRAC1_SQRT2, -FRAC1_SQRT2}}; -inline constexpr Eigen::Matrix2cd IPZ{{{0, 1}, 0}, {0, {0, -1}}}; -inline constexpr Eigen::Matrix2cd IPY{{0, 1}, {-1, 0}}; -inline constexpr Eigen::Matrix2cd IPX{{0, {0, 1}}, {{0, 1}, 0}}; +inline const Eigen::Matrix2cd H_GATE{{FRAC1_SQRT2, FRAC1_SQRT2}, + {FRAC1_SQRT2, -FRAC1_SQRT2}}; +inline const Eigen::Matrix2cd IPZ{{{0, 1}, 0}, {0, {0, -1}}}; +inline const Eigen::Matrix2cd IPY{{0, 1}, {-1, 0}}; +inline const Eigen::Matrix2cd IPX{{0, {0, 1}}, {{0, 1}, 0}}; [[nodiscard]] Eigen::Matrix4cd expandToTwoQubits(const Eigen::Matrix2cd& singleQubitMatrix, QubitId qubitId); From e2cbf76b9a8d977b50a76b0eac330fef9b40965a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 7 Feb 2026 14:02:50 +0000 Subject: [PATCH 210/237] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index cb60f08f51..b128ae1ab9 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -340,7 +340,7 @@ struct GateDecompositionPattern final /** * Initialize TwoQubitSeries instance with given first operation. * If the operation is not valid for a one- or two-qubit series, - * leave in/out qubits unitialized and the gate list empty. + * leave in/out qubits uninitialized and the gate list empty. */ explicit TwoQubitSeries(UnitaryOpInterface initialOperation) { if (isBarrier(initialOperation)) { From 61140da0288ea256079128a9d7607f4db65a1bd8 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 7 Feb 2026 17:29:37 +0100 Subject: [PATCH 211/237] improve comments, other minor fixes --- .../Passes/Decomposition/BasisDecomposer.h | 3 + .../Passes/Decomposition/UnitaryMatrices.h | 91 ++++--------------- .../Passes/Decomposition/WeylDecomposition.h | 12 ++- .../Decomposition/BasisDecomposer.cpp | 53 +++++------ .../Decomposition/UnitaryMatrices.cpp | 73 +++++++++++++++ .../Decomposition/WeylDecomposition.cpp | 3 +- .../Patterns/GateDecompositionPattern.cpp | 67 ++++++++++---- 7 files changed, 177 insertions(+), 125 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index 7564487b00..d1500342b2 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -183,6 +183,9 @@ class TwoQubitBasisDecomposer { // operation to error value for better calculateError() bool simplify, std::optional atol); + [[nodiscard]] static bool relativeEq(double lhs, double rhs, double epsilon, + double maxRelative); + private: // basis gate of this decomposer instance Gate basisGate{}; diff --git a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h index 3ddfd73372..bfa08c8a5c 100644 --- a/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h +++ b/mlir/include/mlir/Passes/Decomposition/UnitaryMatrices.h @@ -13,8 +13,6 @@ #include "Gate.h" #include -#include -#include namespace mlir::qco::decomposition { @@ -22,78 +20,23 @@ inline constexpr double SQRT2 = 1.414213562373095048801688724209698079L; inline constexpr double FRAC1_SQRT2 = 0.707106781186547524400844362104849039284835937688474036588L; -[[nodiscard]] constexpr Eigen::Matrix2cd uMatrix(double lambda, double phi, - double theta) { - return Eigen::Matrix2cd{{{{std::cos(theta / 2.), 0.}, - {-std::cos(lambda) * std::sin(theta / 2.), - -std::sin(lambda) * std::sin(theta / 2.)}}, - {{std::cos(phi) * std::sin(theta / 2.), - std::sin(phi) * std::sin(theta / 2.)}, - {std::cos(lambda + phi) * std::cos(theta / 2.), - std::sin(lambda + phi) * std::cos(theta / 2.)}}}}; -} - -[[nodiscard]] constexpr Eigen::Matrix2cd u2Matrix(double lambda, double phi) { - return Eigen::Matrix2cd{ - {FRAC1_SQRT2, - {-std::cos(lambda) * FRAC1_SQRT2, -std::sin(lambda) * FRAC1_SQRT2}}, - {{std::cos(phi) * FRAC1_SQRT2, std::sin(phi) * FRAC1_SQRT2}, - {std::cos(lambda + phi) * FRAC1_SQRT2, - std::sin(lambda + phi) * FRAC1_SQRT2}}}; -} - -[[nodiscard]] constexpr Eigen::Matrix2cd rxMatrix(double theta) { - auto halfTheta = theta / 2.; - auto cos = std::complex{std::cos(halfTheta), 0.}; - auto isin = std::complex{0., -std::sin(halfTheta)}; - return Eigen::Matrix2cd{{cos, isin}, {isin, cos}}; -} - -[[nodiscard]] constexpr Eigen::Matrix2cd ryMatrix(double theta) { - auto halfTheta = theta / 2.; - std::complex cos{std::cos(halfTheta), 0.}; - std::complex sin{std::sin(halfTheta), 0.}; - return Eigen::Matrix2cd{{cos, -sin}, {sin, cos}}; -} - -[[nodiscard]] constexpr Eigen::Matrix2cd rzMatrix(double theta) { - return Eigen::Matrix2cd{{{std::cos(theta / 2.), -std::sin(theta / 2.)}, 0}, - {0, {std::cos(theta / 2.), std::sin(theta / 2.)}}}; -} - -[[nodiscard]] constexpr Eigen::Matrix4cd rxxMatrix(double theta) { - const auto cosTheta = std::cos(theta / 2.); - const auto sinTheta = std::sin(theta / 2.); - - return Eigen::Matrix4cd{{cosTheta, 0, 0, {0., -sinTheta}}, - {0, cosTheta, {0., -sinTheta}, 0}, - {0, {0., -sinTheta}, cosTheta, 0}, - {{0., -sinTheta}, 0, 0, cosTheta}}; -} - -[[nodiscard]] constexpr Eigen::Matrix4cd ryyMatrix(double theta) { - const auto cosTheta = std::cos(theta / 2.); - const auto sinTheta = std::sin(theta / 2.); - - return Eigen::Matrix4cd{{{cosTheta, 0, 0, {0., sinTheta}}, - {0, cosTheta, {0., -sinTheta}, 0}, - {0, {0., -sinTheta}, cosTheta, 0}, - {{0., sinTheta}, 0, 0, cosTheta}}}; -} - -[[nodiscard]] constexpr Eigen::Matrix4cd rzzMatrix(double theta) { - const auto cosTheta = std::cos(theta / 2.); - const auto sinTheta = std::sin(theta / 2.); - - return Eigen::Matrix4cd{{{cosTheta, -sinTheta}, 0, 0, 0}, - {0, {cosTheta, sinTheta}, 0, 0}, - {0, 0, {cosTheta, sinTheta}, 0}, - {0, 0, 0, {cosTheta, -sinTheta}}}; -} - -[[nodiscard]] constexpr Eigen::Matrix2cd pMatrix(double lambda) { - return Eigen::Matrix2cd{{1, 0}, {0, {std::cos(lambda), std::sin(lambda)}}}; -} +[[nodiscard]] Eigen::Matrix2cd uMatrix(double lambda, double phi, double theta); + +[[nodiscard]] Eigen::Matrix2cd u2Matrix(double lambda, double phi); + +[[nodiscard]] Eigen::Matrix2cd rxMatrix(double theta); + +[[nodiscard]] Eigen::Matrix2cd ryMatrix(double theta); + +[[nodiscard]] Eigen::Matrix2cd rzMatrix(double theta); + +[[nodiscard]] Eigen::Matrix4cd rxxMatrix(double theta); + +[[nodiscard]] Eigen::Matrix4cd ryyMatrix(double theta); + +[[nodiscard]] Eigen::Matrix4cd rzzMatrix(double theta); + +[[nodiscard]] Eigen::Matrix2cd pMatrix(double lambda); inline const Eigen::Matrix4cd SWAP_GATE{ {1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}; diff --git a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index b54330b3ea..55e7ecc935 100644 --- a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -147,12 +147,18 @@ class TwoQubitWeylDecomposition { OutOf, }; + /** + * Threshold for imprecision in computation of diagonalization. + */ + static constexpr auto DIAGONALIZATION_PRECISION = 1e-13; + TwoQubitWeylDecomposition() = default; - static Eigen::Matrix4cd magicBasisTransform(const Eigen::Matrix4cd& unitary, - MagicBasisTransform direction); + [[nodiscard]] static Eigen::Matrix4cd + magicBasisTransform(const Eigen::Matrix4cd& unitary, + MagicBasisTransform direction); - static double closestPartialSwap(double a, double b, double c); + [[nodiscard]] static double closestPartialSwap(double a, double b, double c); /** * Diagonalize given complex symmetric matrix M into (P, d) using a diff --git a/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp b/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp index 960e42d39b..6e47f3e916 100644 --- a/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp +++ b/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp @@ -37,32 +37,6 @@ TwoQubitBasisDecomposer TwoQubitBasisDecomposer::create(const Gate& basisGate, double basisFidelity) { using namespace std::complex_literals; - auto relativeEq = [](auto&& lhs, auto&& rhs, auto&& epsilon, - auto&& maxRelative) { - // Handle same infinities - if (lhs == rhs) { - return true; - } - - // Handle remaining infinities - if (std::isinf(lhs) || std::isinf(rhs)) { - return false; - } - - auto absDiff = std::abs(lhs - rhs); - - // For when the numbers are really close together - if (absDiff <= epsilon) { - return true; - } - - auto absLhs = std::abs(lhs); - auto absRhs = std::abs(rhs); - if (absRhs > absLhs) { - return absDiff <= absRhs * maxRelative; - } - return absDiff <= absLhs * maxRelative; - }; const Eigen::Matrix2cd k12RArr{ {1i * FRAC1_SQRT2, FRAC1_SQRT2}, {-FRAC1_SQRT2, -1i * FRAC1_SQRT2}, @@ -392,4 +366,31 @@ OneQubitGateSequence TwoQubitBasisDecomposer::unitaryToGateSequence( return bestCircuit; } +bool TwoQubitBasisDecomposer::relativeEq(double lhs, double rhs, double epsilon, + double maxRelative) { + // Handle same infinities + if (lhs == rhs) { + return true; + } + + // Handle remaining infinities + if (std::isinf(lhs) || std::isinf(rhs)) { + return false; + } + + auto absDiff = std::abs(lhs - rhs); + + // For when the numbers are really close together + if (absDiff <= epsilon) { + return true; + } + + auto absLhs = std::abs(lhs); + auto absRhs = std::abs(rhs); + if (absRhs > absLhs) { + return absDiff <= absRhs * maxRelative; + } + return absDiff <= absLhs * maxRelative; +} + } // namespace mlir::qco::decomposition diff --git a/mlir/lib/Passes/Patterns/Decomposition/UnitaryMatrices.cpp b/mlir/lib/Passes/Patterns/Decomposition/UnitaryMatrices.cpp index 9c1bd664b0..604e0ea5f3 100644 --- a/mlir/lib/Passes/Patterns/Decomposition/UnitaryMatrices.cpp +++ b/mlir/lib/Passes/Patterns/Decomposition/UnitaryMatrices.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -23,6 +24,78 @@ namespace mlir::qco::decomposition { +Eigen::Matrix2cd uMatrix(double lambda, double phi, double theta) { + return Eigen::Matrix2cd{{{{std::cos(theta / 2.), 0.}, + {-std::cos(lambda) * std::sin(theta / 2.), + -std::sin(lambda) * std::sin(theta / 2.)}}, + {{std::cos(phi) * std::sin(theta / 2.), + std::sin(phi) * std::sin(theta / 2.)}, + {std::cos(lambda + phi) * std::cos(theta / 2.), + std::sin(lambda + phi) * std::cos(theta / 2.)}}}}; +} + +Eigen::Matrix2cd u2Matrix(double lambda, double phi) { + return Eigen::Matrix2cd{ + {FRAC1_SQRT2, + {-std::cos(lambda) * FRAC1_SQRT2, -std::sin(lambda) * FRAC1_SQRT2}}, + {{std::cos(phi) * FRAC1_SQRT2, std::sin(phi) * FRAC1_SQRT2}, + {std::cos(lambda + phi) * FRAC1_SQRT2, + std::sin(lambda + phi) * FRAC1_SQRT2}}}; +} + +Eigen::Matrix2cd rxMatrix(double theta) { + auto halfTheta = theta / 2.; + auto cos = std::complex{std::cos(halfTheta), 0.}; + auto isin = std::complex{0., -std::sin(halfTheta)}; + return Eigen::Matrix2cd{{cos, isin}, {isin, cos}}; +} + +Eigen::Matrix2cd ryMatrix(double theta) { + auto halfTheta = theta / 2.; + std::complex cos{std::cos(halfTheta), 0.}; + std::complex sin{std::sin(halfTheta), 0.}; + return Eigen::Matrix2cd{{cos, -sin}, {sin, cos}}; +} + +Eigen::Matrix2cd rzMatrix(double theta) { + return Eigen::Matrix2cd{{{std::cos(theta / 2.), -std::sin(theta / 2.)}, 0}, + {0, {std::cos(theta / 2.), std::sin(theta / 2.)}}}; +} + +Eigen::Matrix4cd rxxMatrix(double theta) { + const auto cosTheta = std::cos(theta / 2.); + const auto sinTheta = std::sin(theta / 2.); + + return Eigen::Matrix4cd{{cosTheta, 0, 0, {0., -sinTheta}}, + {0, cosTheta, {0., -sinTheta}, 0}, + {0, {0., -sinTheta}, cosTheta, 0}, + {{0., -sinTheta}, 0, 0, cosTheta}}; +} + +Eigen::Matrix4cd ryyMatrix(double theta) { + const auto cosTheta = std::cos(theta / 2.); + const auto sinTheta = std::sin(theta / 2.); + + return Eigen::Matrix4cd{{{cosTheta, 0, 0, {0., sinTheta}}, + {0, cosTheta, {0., -sinTheta}, 0}, + {0, {0., -sinTheta}, cosTheta, 0}, + {{0., sinTheta}, 0, 0, cosTheta}}}; +} + +Eigen::Matrix4cd rzzMatrix(double theta) { + const auto cosTheta = std::cos(theta / 2.); + const auto sinTheta = std::sin(theta / 2.); + + return Eigen::Matrix4cd{{{cosTheta, -sinTheta}, 0, 0, 0}, + {0, {cosTheta, sinTheta}, 0, 0}, + {0, 0, {cosTheta, sinTheta}, 0}, + {0, 0, 0, {cosTheta, -sinTheta}}}; +} + +Eigen::Matrix2cd pMatrix(double lambda) { + return Eigen::Matrix2cd{{1, 0}, {0, {std::cos(lambda), std::sin(lambda)}}}; +} + Eigen::Matrix4cd expandToTwoQubits(const Eigen::Matrix2cd& singleQubitMatrix, QubitId qubitId) { if (qubitId == 0) { diff --git a/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp b/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp index e7aaf58b2a..bb38dab18f 100644 --- a/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp +++ b/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp @@ -57,8 +57,7 @@ TwoQubitWeylDecomposition::create(const Eigen::Matrix4cd& unitaryMatrix, // p is used to calculate K1/K2 (and thus the single-qubit gates // surrounding the canonical gate); d is is used to determine the weyl // coordinates and thus the parameters of the canonical gate - // TODO: it may be possible to lower the precision - auto [p, d] = diagonalizeComplexSymmetric(m2, 1e-13); + auto [p, d] = diagonalizeComplexSymmetric(m2, DIAGONALIZATION_PRECISION); // extract Weyl coordinates from eigenvalues, map to [0, 2*pi) // NOLINTNEXTLINE(misc-include-cleaner) diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index b128ae1ab9..e2da28a103 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -194,6 +194,9 @@ struct GateDecompositionPattern final decomposition::SANITY_CHECK_PRECISION; using QubitId = decomposition::QubitId; + /** + * Qubit series of MLIR operations involving up to two qubits. + */ struct TwoQubitSeries { /** * Complexity of series using getComplexity() for each gate. @@ -367,8 +370,11 @@ struct GateDecompositionPattern final } /** + * Add a single-qubit operation to the series. + * + * @param nextGate Gate to be added, must have exactly one qubit + * * @return true if series continues, otherwise false - * (will always return true) */ bool appendSingleQubitGate(UnitaryOpInterface nextGate) { if (isBarrier(nextGate)) { @@ -390,6 +396,10 @@ struct GateDecompositionPattern final } /** + * Add a two-qubit operation to the series. + * + * @param nextGate Gate to be added, must have exactly two qubits + * * @return true if series continues, otherwise false */ bool appendTwoQubitGate(UnitaryOpInterface nextGate) { @@ -483,11 +493,13 @@ struct GateDecompositionPattern final } [[nodiscard]] static bool isBarrier(UnitaryOpInterface op) { - return llvm::isa_and_nonnull(*op); + return llvm::isa_and_present(op); } /** - * + * Get user (should only be one due to dialect's one-use policy) of given + * qubit. If the filter returns false for this user, std::nullopt will be + * returned instead. */ template static std::optional getUser(mlir::Value qubit, @@ -508,14 +520,15 @@ struct GateDecompositionPattern final }; }; - template - static OpType createGate(mlir::PatternRewriter& rewriter, - mlir::Location location, - Args&&... inQubitsAndParams) { - return rewriter.create(location, - std::forward(inQubitsAndParams)...); - } - + /** + * Create controlled version of given gate operation type. + * + * @param rewriter Rewriter instance to apply modifications + * @param location Location for the created operations + * @param ctrlQubits Qubits that serve as controls + * @param inQubitsAndParams Qubits and parameters for inner gate + * (as required by the builder of the gate) + */ template static CtrlOp createControlledGate(mlir::PatternRewriter& rewriter, mlir::Location location, @@ -532,10 +545,16 @@ struct GateDecompositionPattern final (collectInQubits(inQubitsAndParams), ...); return rewriter.create( location, ctrlQubits, mlir::ValueRange{inQubits}, - createGate(rewriter, location, - std::forward(inQubitsAndParams)...)); + rewriter.create(location, + std::forward(inQubitsAndParams)...)); } + /** + * Replace given series by given sequence. + * This is done using the rewriter to create the MLIR operations described by + * the sequence between the input and output qubits of the series and then + * deleting all gates of the series. + */ static void applySeries(mlir::PatternRewriter& rewriter, TwoQubitSeries& series, const decomposition::TwoQubitGateSequence& sequence) { @@ -560,7 +579,7 @@ struct GateDecompositionPattern final }; if (sequence.hasGlobalPhase()) { - createGate(rewriter, location, sequence.globalPhase); + rewriter.create(location, sequence.globalPhase); } #ifndef NDEBUG @@ -582,8 +601,8 @@ struct GateDecompositionPattern final updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RX) { assert(gate.qubitId.size() == 1); - auto newGate = createGate( - rewriter, location, inQubits[gate.qubitId[0]], gate.parameter[0]); + auto newGate = rewriter.create( + location, inQubits[gate.qubitId[0]], gate.parameter[0]); #ifndef NDEBUG unitaryMatrix = decomposition::expandToTwoQubits(newGate.getUnitaryMatrix().value(), @@ -593,8 +612,8 @@ struct GateDecompositionPattern final updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RY) { assert(gate.qubitId.size() == 1); - auto newGate = createGate( - rewriter, location, inQubits[gate.qubitId[0]], gate.parameter[0]); + auto newGate = rewriter.create( + location, inQubits[gate.qubitId[0]], gate.parameter[0]); #ifndef NDEBUG unitaryMatrix = decomposition::expandToTwoQubits(newGate.getUnitaryMatrix().value(), @@ -604,8 +623,8 @@ struct GateDecompositionPattern final updateInQubits(gate.qubitId, newGate); } else if (gate.type == qc::RZ) { assert(gate.qubitId.size() == 1); - auto newGate = createGate( - rewriter, location, inQubits[gate.qubitId[0]], gate.parameter[0]); + auto newGate = rewriter.create( + location, inQubits[gate.qubitId[0]], gate.parameter[0]); #ifndef NDEBUG unitaryMatrix = decomposition::expandToTwoQubits(newGate.getUnitaryMatrix().value(), @@ -633,6 +652,14 @@ struct GateDecompositionPattern final } } + /** + * Get all gates that are potentially in the circuit after the decomposition. + * This is based on the euler bases and basis gates passed to the constructor. + * + * @return Array with the following two elements: + * * All possible single-qubit gate types + * * All possible two-qubit gate types + */ [[nodiscard]] std::array, 2> getDecompositionGates() const { llvm::SetVector eulerBasesGates; From 799ed7761730a0b8fd2c99bd98ec16b9d71dfd30 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 7 Feb 2026 17:57:13 +0100 Subject: [PATCH 212/237] add some caching for available gates and improve comments --- .../Decomposition/WeylDecomposition.cpp | 2 +- .../Patterns/GateDecompositionPattern.cpp | 36 ++++++++++++++----- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp b/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp index bb38dab18f..32768e5add 100644 --- a/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp +++ b/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp @@ -360,7 +360,7 @@ TwoQubitWeylDecomposition::diagonalizeComplexSymmetric( const Eigen::Matrix4d m2Real = randA * m.real() + randB * m.imag(); auto&& pReal = helpers::selfAdjointEvd(m2Real).first; const Eigen::Matrix4cd p = pReal; - auto&& d = (p.transpose() * m * p).diagonal(); + const Eigen::Vector4cd d = (p.transpose() * m * p).diagonal(); auto&& compare = p * d.asDiagonal() * p.transpose(); if (compare.isApprox(m, precision)) { diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index e2da28a103..83beb61b6c 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -85,6 +85,9 @@ struct GateDecompositionPattern final basisDecomposers.push_back(decomposition::TwoQubitBasisDecomposer::create( basisGate, DEFAULT_FIDELITY)); } + auto&& [singleQubitGates, twoQubitGates] = getDecompositionGates(); + availableSingleQubitGates = std::move(singleQubitGates); + availableTwoQubitGates = std::move(twoQubitGates); } mlir::LogicalResult @@ -108,9 +111,8 @@ struct GateDecompositionPattern final }; auto series = collectSeries(op, singleQubitOnly); - auto&& [singleQubitGates, twoQubitGates] = getDecompositionGates(); - auto containsForeignGates = - !series.containsOnlyGates(singleQubitGates, twoQubitGates); + auto containsForeignGates = !series.containsOnlyGates( + availableSingleQubitGates, availableTwoQubitGates); if (series.gates.empty() || (series.gates.size() < 3 && !(forceApplication && containsForeignGates))) { @@ -424,19 +426,23 @@ struct GateDecompositionPattern final auto it = // NOLINT(readability-qualified-auto) llvm::find(outQubits, mlir::Value{}); if (it == outQubits.end()) { + // series already has two qubits, thus it is finished because of this + // new qubit return false; } auto&& opInQubits = nextGate.getInputQubits(); - // iterator in the operation input of "old" qubit that already has - // previous single-qubit gates in this series + // iterator in the operation input of nextGate to "old" qubit that + // already has previous single-qubit gates in this series auto it2 = llvm::find(opInQubits, firstQubitIt != outQubits.end() ? *firstQubitIt : *secondQubitIt); + // operation is a user of the "old" qubit since it was found this way; + // thus should always succeed assert(it2 != opInQubits.end()); // new qubit ID based on position in outQubits const QubitId newInQubitId = std::distance(outQubits.begin(), it); - // position in operation input; since there are only two qubits, it must - // be the "not old" one + // position in operation input; since there are only two qubits, the + // other in qubit must be the "not old" one const QubitId newOpInQubitId = 1 - std::distance(opInQubits.begin(), it2); @@ -527,7 +533,9 @@ struct GateDecompositionPattern final * @param location Location for the created operations * @param ctrlQubits Qubits that serve as controls * @param inQubitsAndParams Qubits and parameters for inner gate - * (as required by the builder of the gate) + * (as required by the builder of the gate); + * all qubits must be of type mlir::Value + * and all parameters must have another type */ template static CtrlOp createControlledGate(mlir::PatternRewriter& rewriter, @@ -675,9 +683,19 @@ struct GateDecompositionPattern final } private: + // available basis gates llvm::SmallVector decomposerBasisGates; - llvm::SmallVector basisDecomposers; + // available euler bases llvm::SmallVector decomposerEulerBases; + + // cached basis decomposers; one for each basis gate + llvm::SmallVector basisDecomposers; + + // cached result of getDecompositionGates() + llvm::SetVector availableSingleQubitGates; + llvm::SetVector availableTwoQubitGates; + + // configuration of pattern bool singleQubitOnly; bool forceApplication; }; From 1a1a8fc724b5d766e46a9eea9d3aa675a20f302f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 7 Feb 2026 18:34:28 +0100 Subject: [PATCH 213/237] minor comment and one const fix --- .../Passes/Patterns/Decomposition/WeylDecomposition.cpp | 8 ++++---- mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp b/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp index 32768e5add..ec10c958e1 100644 --- a/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp +++ b/mlir/lib/Passes/Patterns/Decomposition/WeylDecomposition.cpp @@ -625,7 +625,7 @@ bool TwoQubitWeylDecomposition::applySpecialization() { // // This gate binds 5 parameters, we make it canonical by setting: // - // :math:`K2_l = Ry(\theta_l)\cdot Rz(\lambda_l)`. + // :math:`K2_l = Ry(\theta_l) \cdot Rz(\lambda_l)`. auto [k2ltheta, k2lphi, k2llambda, k2lphase] = EulerDecomposition::anglesFromUnitary(k2l_, EulerBasis::ZYZ); auto ab = (a_ + b_) / 2.; @@ -639,11 +639,11 @@ bool TwoQubitWeylDecomposition::applySpecialization() { k1r_ = k1r_ * rzMatrix(k2lphi); k2r_ = rzMatrix(-k2lphi) * k2r_; } else if (newSpecialization == Specialization::FSimabbEquiv) { - // :math:`U \sim U_d(\alpha, \beta, -\beta), \alpha \geq \beta \geq 0` + // :math:`U \sim U_d(\alpha, \beta, \beta), \alpha \geq \beta \geq 0` // // This gate binds 5 parameters, we make it canonical by setting: // - // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` + // :math:`K2_l = Ry(\theta_l) \cdot Rx(\lambda_l)` auto eulerBasis = EulerBasis::XYX; auto [k2ltheta, k2lphi, k2llambda, k2lphase] = EulerDecomposition::anglesFromUnitary(k2l_, eulerBasis); @@ -663,7 +663,7 @@ bool TwoQubitWeylDecomposition::applySpecialization() { // // This gate binds 5 parameters, we make it canonical by setting: // - // :math:`K2_l = Ry(\theta_l)Rx(\lambda_l)` + // :math:`K2_l = Ry(\theta_l) \cdot Rx(\lambda_l)` auto eulerBasis = EulerBasis::XYX; auto [k2ltheta, k2lphi, k2llambda, k2lphase] = EulerDecomposition::anglesFromUnitary(k2l_, eulerBasis); diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 83beb61b6c..102b1c3463 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -327,7 +327,7 @@ struct GateDecompositionPattern final [[nodiscard]] bool containsOnlyGates(const llvm::SetVector& singleQubitGates, - const llvm::SetVector& twoQubitGates) { + const llvm::SetVector& twoQubitGates) const { return llvm::all_of(gates, [&](auto&& gate) { auto&& gateType = helpers::getQcType(gate.op); return (gate.qubitIds.size() == 1 && @@ -407,7 +407,7 @@ struct GateDecompositionPattern final bool appendTwoQubitGate(UnitaryOpInterface nextGate) { if (isBarrier(nextGate)) { // a barrier operation should not be crossed for a decomposition; - // ignore possitility to backtrack (if this is the first two-qubit gate) + // ignore possibility to backtrack (if this is the first two-qubit gate) // since two single-qubit decompositions are less expensive than one // two-qubit decomposition return false; From 91c1f2aa860820d70b025c50c88d673b247d9c94 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 7 Feb 2026 18:55:58 +0100 Subject: [PATCH 214/237] fix annoying static initialization issue in gtest with Eigen --- .../Decomposition/test_basis_decomposer.cpp | 64 ++++++++----- .../test_euler_decomposition.cpp | 16 +++- .../Decomposition/test_weyl_decomposition.cpp | 95 ++++++++++++------- 3 files changed, 114 insertions(+), 61 deletions(-) diff --git a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp index 91d1c19781..5dcc4dc2f7 100644 --- a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp +++ b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp @@ -34,13 +34,13 @@ using namespace mlir::qco; using namespace mlir::qco::decomposition; class BasisDecomposerTest - : public testing::TestWithParam< - std::tuple, Eigen::Matrix4cd>> { + : public testing::TestWithParam, Eigen::Matrix4cd (*)()>> { public: void SetUp() override { basisGate = std::get<0>(GetParam()); eulerBases = std::get<1>(GetParam()); - target = std::get<2>(GetParam()); + target = std::get<2>(GetParam())(); targetDecomposition = std::make_unique( TwoQubitWeylDecomposition::create(target, 1.0)); } @@ -151,10 +151,15 @@ INSTANTIATE_TEST_SUITE_P( EulerBasis::XZX}, llvm::SmallVector{EulerBasis::XZX}), // targets to be decomposed - testing::Values(Eigen::Matrix4cd::Identity(), - Eigen::kroneckerProduct(rzMatrix(1.0), ryMatrix(3.1)), - Eigen::kroneckerProduct(Eigen::Matrix2cd::Identity(), - rxMatrix(0.1))))); + testing::Values( + []() -> Eigen::Matrix4cd { return Eigen::Matrix4cd::Identity(); }, + []() -> Eigen::Matrix4cd { + return Eigen::kroneckerProduct(rzMatrix(1.0), ryMatrix(3.1)); + }, + []() -> Eigen::Matrix4cd { + return Eigen::kroneckerProduct(Eigen::Matrix2cd::Identity(), + rxMatrix(0.1)); + }))); INSTANTIATE_TEST_SUITE_P( TwoQubitMatrices, BasisDecomposerTest, @@ -164,22 +169,33 @@ INSTANTIATE_TEST_SUITE_P( Gate{ .type = qc::X, .parameter = {}, .qubitId = {1, 0}}), // sets of euler bases - testing::Values(llvm::SmallVector{EulerBasis::ZYZ}, - llvm::SmallVector{ - EulerBasis::ZYZ, EulerBasis::ZXZ, EulerBasis::XYX, - EulerBasis::XZX}, - llvm::SmallVector{EulerBasis::XZX}), + testing::Values( + llvm::SmallVector{EulerBasis::ZYZ}, + llvm::SmallVector{EulerBasis::ZYZ, EulerBasis::ZXZ, + EulerBasis::XYX, EulerBasis::XZX}, + llvm::SmallVector{EulerBasis::XZX, EulerBasis::XYX}), // targets to be decomposed ::testing::Values( - rzzMatrix(2.0), ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0), - TwoQubitWeylDecomposition::getCanonicalMatrix(1.5, -0.2, 0.0) * - Eigen::kroneckerProduct(rxMatrix(1.0), - Eigen::Matrix2cd::Identity()), - Eigen::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * - TwoQubitWeylDecomposition::getCanonicalMatrix(1.1, 0.2, 3.0) * - Eigen::kroneckerProduct(rxMatrix(1.0), - Eigen::Matrix2cd::Identity()), - Eigen::kroneckerProduct(H_GATE, IPZ) * - getTwoQubitMatrix( - {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * - Eigen::kroneckerProduct(IPX, IPY)))); + []() -> Eigen::Matrix4cd { return rzzMatrix(2.0); }, + []() -> Eigen::Matrix4cd { + return ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0); + }, + []() -> Eigen::Matrix4cd { + return TwoQubitWeylDecomposition::getCanonicalMatrix(1.5, -0.2, + 0.0) * + Eigen::kroneckerProduct(rxMatrix(1.0), + Eigen::Matrix2cd::Identity()); + }, + []() -> Eigen::Matrix4cd { + return Eigen::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * + TwoQubitWeylDecomposition::getCanonicalMatrix(1.1, 0.2, + 3.0) * + Eigen::kroneckerProduct(rxMatrix(1.0), + Eigen::Matrix2cd::Identity()); + }, + []() -> Eigen::Matrix4cd { + return Eigen::kroneckerProduct(H_GATE, IPZ) * + getTwoQubitMatrix( + {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * + Eigen::kroneckerProduct(IPX, IPY); + }))); diff --git a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp index 3e256d22e2..8a6b9facb9 100644 --- a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp @@ -27,7 +27,8 @@ using namespace mlir::qco; using namespace mlir::qco::decomposition; class EulerDecompositionTest - : public testing::TestWithParam> { + : public testing::TestWithParam< + std::tuple> { public: [[nodiscard]] static Eigen::Matrix2cd restore(const OneQubitGateSequence& sequence) { @@ -42,7 +43,7 @@ class EulerDecompositionTest void SetUp() override { eulerBasis = std::get<0>(GetParam()); - originalMatrix = std::get<1>(GetParam()); + originalMatrix = std::get<1>(GetParam())(); } protected: @@ -84,6 +85,11 @@ INSTANTIATE_TEST_CASE_P( SingleQubitMatrices, EulerDecompositionTest, testing::Combine(testing::Values(EulerBasis::XYX, EulerBasis::XZX, EulerBasis::ZYZ, EulerBasis::ZXZ), - testing::Values(Eigen::Matrix2cd::Identity(), - ryMatrix(2.0), rxMatrix(0.5), - rzMatrix(3.14), H_GATE))); + testing::Values( + []() -> Eigen::Matrix2cd { + return Eigen::Matrix2cd::Identity(); + }, + []() -> Eigen::Matrix2cd { return ryMatrix(2.0); }, + []() -> Eigen::Matrix2cd { return rxMatrix(0.5); }, + []() -> Eigen::Matrix2cd { return rzMatrix(3.14); }, + []() -> Eigen::Matrix2cd { return H_GATE; }))); diff --git a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp index 69004f3cb8..c26bdfd3de 100644 --- a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp @@ -23,7 +23,8 @@ using namespace mlir::qco; using namespace mlir::qco::decomposition; -class WeylDecompositionTest : public testing::TestWithParam { +class WeylDecompositionTest + : public testing::TestWithParam { public: [[nodiscard]] static Eigen::Matrix4cd restore(const TwoQubitWeylDecomposition& decomposition) { @@ -50,7 +51,7 @@ class WeylDecompositionTest : public testing::TestWithParam { }; TEST_P(WeylDecompositionTest, TestExact) { - const auto& originalMatrix = GetParam(); + const auto& originalMatrix = GetParam()(); auto decomposition = TwoQubitWeylDecomposition::create(originalMatrix, 1.0); auto restoredMatrix = restore(decomposition); @@ -60,7 +61,7 @@ TEST_P(WeylDecompositionTest, TestExact) { } TEST_P(WeylDecompositionTest, TestApproximation) { - const auto& originalMatrix = GetParam(); + const auto& originalMatrix = GetParam()(); auto decomposition = TwoQubitWeylDecomposition::create(originalMatrix, 1.0 - 1e-12); auto restoredMatrix = restore(decomposition); @@ -88,48 +89,78 @@ TEST(WeylDecompositionTest, Random) { INSTANTIATE_TEST_SUITE_P( SingleQubitMatrices, WeylDecompositionTest, - ::testing::Values(Eigen::Matrix4cd::Identity(), - Eigen::kroneckerProduct(rzMatrix(1.0), ryMatrix(3.1)), - Eigen::kroneckerProduct(Eigen::Matrix2cd::Identity(), - rxMatrix(0.1)))); + ::testing::Values( + []() -> Eigen::Matrix4cd { return Eigen::Matrix4cd::Identity(); }, + []() -> Eigen::Matrix4cd { + return Eigen::kroneckerProduct(rzMatrix(1.0), ryMatrix(3.1)); + }, + []() -> Eigen::Matrix4cd { + return Eigen::kroneckerProduct(Eigen::Matrix2cd::Identity(), + rxMatrix(0.1)); + })); INSTANTIATE_TEST_SUITE_P( TwoQubitMatrices, WeylDecompositionTest, ::testing::Values( - rzzMatrix(2.0), ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0), - TwoQubitWeylDecomposition::getCanonicalMatrix(1.5, -0.2, 0.0) * - Eigen::kroneckerProduct(rxMatrix(1.0), - Eigen::Matrix2cd::Identity()), - Eigen::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * - TwoQubitWeylDecomposition::getCanonicalMatrix(1.1, 0.2, 3.0) * - Eigen::kroneckerProduct(rxMatrix(1.0), - Eigen::Matrix2cd::Identity()), - Eigen::kroneckerProduct(H_GATE, IPZ) * - getTwoQubitMatrix( - {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * - Eigen::kroneckerProduct(IPX, IPY))); + []() -> Eigen::Matrix4cd { return rzzMatrix(2.0); }, + []() -> Eigen::Matrix4cd { + return ryyMatrix(1.0) * rzzMatrix(3.0) * rxxMatrix(2.0); + }, + []() -> Eigen::Matrix4cd { + return TwoQubitWeylDecomposition::getCanonicalMatrix(1.5, -0.2, 0.0) * + Eigen::kroneckerProduct(rxMatrix(1.0), + Eigen::Matrix2cd::Identity()); + }, + []() -> Eigen::Matrix4cd { + return Eigen::kroneckerProduct(rxMatrix(1.0), ryMatrix(1.0)) * + TwoQubitWeylDecomposition::getCanonicalMatrix(1.1, 0.2, 3.0) * + Eigen::kroneckerProduct(rxMatrix(1.0), + Eigen::Matrix2cd::Identity()); + }, + []() -> Eigen::Matrix4cd { + return Eigen::kroneckerProduct(H_GATE, IPZ) * + getTwoQubitMatrix( + {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * + Eigen::kroneckerProduct(IPX, IPY); + })); INSTANTIATE_TEST_SUITE_P( SpecializedMatrices, WeylDecompositionTest, ::testing::Values( // id + controlled + general already covered by other parametrizations // swap equiv - getTwoQubitMatrix({.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * - getTwoQubitMatrix( - {.type = qc::X, .parameter = {}, .qubitId = {1, 0}}) * - getTwoQubitMatrix( - {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}), + []() -> Eigen::Matrix4cd { + return getTwoQubitMatrix( + {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * + getTwoQubitMatrix( + {.type = qc::X, .parameter = {}, .qubitId = {1, 0}}) * + getTwoQubitMatrix( + {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}); + }, // partial swap equiv - TwoQubitWeylDecomposition::getCanonicalMatrix(0.5, 0.5, 0.5), + []() -> Eigen::Matrix4cd { + return TwoQubitWeylDecomposition::getCanonicalMatrix(0.5, 0.5, 0.5); + }, // partial swap equiv (flipped) - TwoQubitWeylDecomposition::getCanonicalMatrix(0.5, 0.5, -0.5), + []() -> Eigen::Matrix4cd { + return TwoQubitWeylDecomposition::getCanonicalMatrix(0.5, 0.5, -0.5); + }, // mirror controlled equiv - getTwoQubitMatrix({.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * - getTwoQubitMatrix( - {.type = qc::X, .parameter = {}, .qubitId = {1, 0}}), + []() -> Eigen::Matrix4cd { + return getTwoQubitMatrix( + {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}) * + getTwoQubitMatrix( + {.type = qc::X, .parameter = {}, .qubitId = {1, 0}}); + }, // sim aab equiv - TwoQubitWeylDecomposition::getCanonicalMatrix(0.5, 0.5, 0.1), + []() -> Eigen::Matrix4cd { + return TwoQubitWeylDecomposition::getCanonicalMatrix(0.5, 0.5, 0.1); + }, // sim abb equiv - TwoQubitWeylDecomposition::getCanonicalMatrix(0.5, 0.1, 0.1), + []() -> Eigen::Matrix4cd { + return TwoQubitWeylDecomposition::getCanonicalMatrix(0.5, 0.1, 0.1); + }, // sim ab-b equiv - TwoQubitWeylDecomposition::getCanonicalMatrix(0.5, 0.1, -0.1))); + []() -> Eigen::Matrix4cd { + return TwoQubitWeylDecomposition::getCanonicalMatrix(0.5, 0.1, -0.1); + })); From a908ba55d8a739876b8e4daf641de3d042d6c051 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 8 Feb 2026 11:49:14 +0100 Subject: [PATCH 215/237] switch to fully deterministic random matrix generation --- mlir/unittests/Passes/Decomposition/utils.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/mlir/unittests/Passes/Decomposition/utils.h b/mlir/unittests/Passes/Decomposition/utils.h index a2dcada254..858f8587a3 100644 --- a/mlir/unittests/Passes/Decomposition/utils.h +++ b/mlir/unittests/Passes/Decomposition/utils.h @@ -14,16 +14,15 @@ #include #include -#include +#include template [[nodiscard]] MatrixType randomUnitaryMatrix() { - [[maybe_unused]] static auto initializeRandom = []() { - // Eigen uses std::rand() internally, use fixed seed for deterministic - // testing behavior - std::srand(123456UL); - return true; - }(); - const MatrixType randomMatrix = MatrixType::Random(); + [[maybe_unused]] static auto rng = []() { return std::mt19937{123456UL}; }(); + std::uniform_real_distribution dist(-1.0, 1.0); + MatrixType randomMatrix; + for (auto& x : randomMatrix.reshaped()) { + x = std::complex(dist(rng), dist(rng)); + } Eigen::HouseholderQR qr{}; // NOLINT(misc-include-cleaner) qr.compute(randomMatrix); const MatrixType unitaryMatrix = qr.householderQ(); From c8110be1d2c96a380ee81c424c8f26f829676565 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 8 Feb 2026 11:51:08 +0100 Subject: [PATCH 216/237] fix potential alignment issues in SmallVector inline storage --- .../Passes/Decomposition/BasisDecomposer.h | 20 +++++++++++++++---- .../Decomposition/BasisDecomposer.cpp | 8 ++++---- .../QCO/IR/test_unitary_op_interface.cpp | 2 +- .../test_euler_decomposition.cpp | 1 + 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index d1500342b2..8bb44a1a6e 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -103,8 +103,11 @@ class TwoQubitBasisDecomposer { * 4\Big\vert (\cos(x)\cos(y)\cos(z)+ j \sin(x)\sin(y)\sin(z)\Big\vert * * which is optimal for all targets and bases + * + * @note The inline storage of llvm::SmallVector must be set to 0 to ensure + * correct Eigen alignment via heap allocation */ - [[nodiscard]] static llvm::SmallVector + [[nodiscard]] static llvm::SmallVector decomp0(const decomposition::TwoQubitWeylDecomposition& target); /** @@ -120,8 +123,11 @@ class TwoQubitBasisDecomposer { * \sin(x-a)\sin(y-b)\sin(z-c)\Big\vert * * which is optimal for all targets and bases with ``z==0`` or ``c==0``. + * + * @note The inline storage of llvm::SmallVector must be set to 0 to ensure + * correct Eigen alignment via heap allocation */ - [[nodiscard]] llvm::SmallVector + [[nodiscard]] llvm::SmallVector decomp1(const decomposition::TwoQubitWeylDecomposition& target) const; /** @@ -146,8 +152,11 @@ class TwoQubitBasisDecomposer { * decomposition). This is an exact decomposition for supercontrolled basis * and target :math:`\sim U_d(x, y, 0)`. No guarantees for * non-supercontrolled basis. + * + * @note The inline storage of llvm::SmallVector must be set to 0 to ensure + * correct Eigen alignment via heap allocation */ - [[nodiscard]] llvm::SmallVector decomp2Supercontrolled( + [[nodiscard]] llvm::SmallVector decomp2Supercontrolled( const decomposition::TwoQubitWeylDecomposition& target) const; /** @@ -158,8 +167,11 @@ class TwoQubitBasisDecomposer { * This is an exact decomposition for supercontrolled basis * :math:`\sim U_d(\pi/4, b, 0)`, all b, and any target. No guarantees for * non-supercontrolled basis. + * + * @note The inline storage of llvm::SmallVector must be set to 0 to ensure + * correct Eigen alignment via heap allocation */ - [[nodiscard]] llvm::SmallVector decomp3Supercontrolled( + [[nodiscard]] llvm::SmallVector decomp3Supercontrolled( const decomposition::TwoQubitWeylDecomposition& target) const; /** diff --git a/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp b/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp index 6e47f3e916..60b9e6ccc9 100644 --- a/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp +++ b/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp @@ -261,7 +261,7 @@ std::optional TwoQubitBasisDecomposer::twoQubitDecompose( return gates; } -llvm::SmallVector +llvm::SmallVector TwoQubitBasisDecomposer::decomp0(const TwoQubitWeylDecomposition& target) { return { target.k1r() * target.k2r(), @@ -269,7 +269,7 @@ TwoQubitBasisDecomposer::decomp0(const TwoQubitWeylDecomposition& target) { }; } -llvm::SmallVector TwoQubitBasisDecomposer::decomp1( +llvm::SmallVector TwoQubitBasisDecomposer::decomp1( const TwoQubitWeylDecomposition& target) const { // may not work for z != 0 and c != 0 (not always in Weyl chamber) return { @@ -280,7 +280,7 @@ llvm::SmallVector TwoQubitBasisDecomposer::decomp1( }; } -llvm::SmallVector +llvm::SmallVector TwoQubitBasisDecomposer::decomp2Supercontrolled( const TwoQubitWeylDecomposition& target) const { if (!isSuperControlled) { @@ -298,7 +298,7 @@ TwoQubitBasisDecomposer::decomp2Supercontrolled( }; } -llvm::SmallVector +llvm::SmallVector TwoQubitBasisDecomposer::decomp3Supercontrolled( const TwoQubitWeylDecomposition& target) const { if (!isSuperControlled) { diff --git a/mlir/unittests/Dialect/QCO/IR/test_unitary_op_interface.cpp b/mlir/unittests/Dialect/QCO/IR/test_unitary_op_interface.cpp index 2bfd5cc693..bd408e1e8c 100644 --- a/mlir/unittests/Dialect/QCO/IR/test_unitary_op_interface.cpp +++ b/mlir/unittests/Dialect/QCO/IR/test_unitary_op_interface.cpp @@ -144,7 +144,7 @@ TEST_F(QcoUnitaryOpInterfaceTest, getUnitaryMatrix2x2) { ASSERT_FALSE(moduleOps.empty()); auto funcOp = llvm::dyn_cast(*moduleOps.begin()); - llvm::SmallVector actualValues; + llvm::SmallVector actualValues; for (auto&& op : funcOp.getOps()) { auto unitaryOp = llvm::dyn_cast(op); if (unitaryOp) { diff --git a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp index 8a6b9facb9..2281643dd6 100644 --- a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp @@ -15,6 +15,7 @@ #include "mlir/Passes/Decomposition/UnitaryMatrices.h" #include "utils.h" +#include #include #include #include From fae71dd189a1616f6f172baee0d9b74e4bb67d06 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 8 Feb 2026 11:51:39 +0100 Subject: [PATCH 217/237] fix alignment issue for static-sized matrices on stack Stack alignment is only 16 bytes, but AVX instructions required 32 byte alignment. Can be fixed by making all stack-variables aligned, but lowering the alignment (and thus limiting the optimizations applied) seems like the more long-term solution - especially considering that this issue is not detected in the CI. --- mlir/lib/Dialect/QCO/IR/CMakeLists.txt | 5 +++++ mlir/lib/Passes/CMakeLists.txt | 5 +++++ mlir/unittests/Passes/Decomposition/CMakeLists.txt | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/mlir/lib/Dialect/QCO/IR/CMakeLists.txt b/mlir/lib/Dialect/QCO/IR/CMakeLists.txt index 7c62ed47dc..1409e10708 100644 --- a/mlir/lib/Dialect/QCO/IR/CMakeLists.txt +++ b/mlir/lib/Dialect/QCO/IR/CMakeLists.txt @@ -49,3 +49,8 @@ target_sources( ${MQT_MLIR_BUILD_INCLUDE_DIR} FILES ${IR_HEADERS_BUILD}) + +# fix alignment issues for stack-allocated fixed-size matrices +target_compile_definitions( + MLIRQCODialect + PUBLIC EIGEN_MAX_STATIC_ALIGN_BYTES=16) diff --git a/mlir/lib/Passes/CMakeLists.txt b/mlir/lib/Passes/CMakeLists.txt index cfa3558868..7cde8748f3 100644 --- a/mlir/lib/Passes/CMakeLists.txt +++ b/mlir/lib/Passes/CMakeLists.txt @@ -40,3 +40,8 @@ target_sources( ${MQT_MLIR_BUILD_INCLUDE_DIR} FILES ${PASSES_HEADERS_BUILD}) + +# fix alignment issues for stack-allocated fixed-size matrices +target_compile_definitions( + QcoPasses + PUBLIC EIGEN_MAX_STATIC_ALIGN_BYTES=16) diff --git a/mlir/unittests/Passes/Decomposition/CMakeLists.txt b/mlir/unittests/Passes/Decomposition/CMakeLists.txt index 878daea605..d445ea3ce4 100644 --- a/mlir/unittests/Passes/Decomposition/CMakeLists.txt +++ b/mlir/unittests/Passes/Decomposition/CMakeLists.txt @@ -24,6 +24,10 @@ if(NOT TARGET ${testname}) MQT::CoreIR Eigen3::Eigen QcoPasses) + # fix alignment issues for stack-allocated fixed-size matrices + target_compile_definitions( + ${testname} + PRIVATE EIGEN_MAX_STATIC_ALIGN_BYTES=16) # discover tests gtest_discover_tests(${testname} DISCOVERY_TIMEOUT 60) set_target_properties(${testname} PROPERTIES FOLDER unittests) From 926ab1b4acc509dde6393b38904082b1c7099344 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 8 Feb 2026 10:55:19 +0000 Subject: [PATCH 218/237] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/lib/Dialect/QCO/IR/CMakeLists.txt | 4 +--- mlir/lib/Passes/CMakeLists.txt | 4 +--- mlir/unittests/Passes/Decomposition/CMakeLists.txt | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/mlir/lib/Dialect/QCO/IR/CMakeLists.txt b/mlir/lib/Dialect/QCO/IR/CMakeLists.txt index 1409e10708..febf4445eb 100644 --- a/mlir/lib/Dialect/QCO/IR/CMakeLists.txt +++ b/mlir/lib/Dialect/QCO/IR/CMakeLists.txt @@ -51,6 +51,4 @@ target_sources( ${IR_HEADERS_BUILD}) # fix alignment issues for stack-allocated fixed-size matrices -target_compile_definitions( - MLIRQCODialect - PUBLIC EIGEN_MAX_STATIC_ALIGN_BYTES=16) +target_compile_definitions(MLIRQCODialect PUBLIC EIGEN_MAX_STATIC_ALIGN_BYTES=16) diff --git a/mlir/lib/Passes/CMakeLists.txt b/mlir/lib/Passes/CMakeLists.txt index 7cde8748f3..01de179e48 100644 --- a/mlir/lib/Passes/CMakeLists.txt +++ b/mlir/lib/Passes/CMakeLists.txt @@ -42,6 +42,4 @@ target_sources( ${PASSES_HEADERS_BUILD}) # fix alignment issues for stack-allocated fixed-size matrices -target_compile_definitions( - QcoPasses - PUBLIC EIGEN_MAX_STATIC_ALIGN_BYTES=16) +target_compile_definitions(QcoPasses PUBLIC EIGEN_MAX_STATIC_ALIGN_BYTES=16) diff --git a/mlir/unittests/Passes/Decomposition/CMakeLists.txt b/mlir/unittests/Passes/Decomposition/CMakeLists.txt index d445ea3ce4..8dda1061e0 100644 --- a/mlir/unittests/Passes/Decomposition/CMakeLists.txt +++ b/mlir/unittests/Passes/Decomposition/CMakeLists.txt @@ -25,9 +25,7 @@ if(NOT TARGET ${testname}) Eigen3::Eigen QcoPasses) # fix alignment issues for stack-allocated fixed-size matrices - target_compile_definitions( - ${testname} - PRIVATE EIGEN_MAX_STATIC_ALIGN_BYTES=16) + target_compile_definitions(${testname} PRIVATE EIGEN_MAX_STATIC_ALIGN_BYTES=16) # discover tests gtest_discover_tests(${testname} DISCOVERY_TIMEOUT 60) set_target_properties(${testname} PROPERTIES FOLDER unittests) From 0c3a4cdc4c4a4c40aeb05bf008b9178a6a04cb59 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 8 Feb 2026 13:25:05 +0100 Subject: [PATCH 219/237] linter/coderabbit fixes --- mlir/lib/Passes/CMakeLists.txt | 1 - .../Decomposition/BasisDecomposer.cpp | 15 +++-- .../Patterns/GateDecompositionPattern.cpp | 60 +++++++++---------- mlir/unittests/Passes/Decomposition/utils.h | 2 + 4 files changed, 36 insertions(+), 42 deletions(-) diff --git a/mlir/lib/Passes/CMakeLists.txt b/mlir/lib/Passes/CMakeLists.txt index 01de179e48..e200953c65 100644 --- a/mlir/lib/Passes/CMakeLists.txt +++ b/mlir/lib/Passes/CMakeLists.txt @@ -8,7 +8,6 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) set(LIBRARIES ${dialect_libs} MQT::CoreIR Eigen3::Eigen) -add_compile_options(-fexceptions) file(GLOB_RECURSE PASSES_SOURCES *.cpp) diff --git a/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp b/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp index 60b9e6ccc9..7e5e67c946 100644 --- a/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp +++ b/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp @@ -205,7 +205,7 @@ std::optional TwoQubitBasisDecomposer::twoQubitDecompose( llvm_unreachable(""); }; auto decomposition = chooseDecomposition(); - llvm::SmallVector, 8> eulerDecompositions; + llvm::SmallVector eulerDecompositions; for (auto&& decomp : decomposition) { assert(helpers::isUnitaryMatrix(decomp)); auto eulerDecomp = unitaryToGateSequence(decomp, target1qEulerBases, 0, @@ -230,14 +230,13 @@ std::optional TwoQubitBasisDecomposer::twoQubitDecompose( } auto addEulerDecomposition = [&](std::size_t index, QubitId qubitId) { - if (auto&& eulerDecomp = eulerDecompositions[index]) { - for (auto&& gate : eulerDecomp->gates) { - gates.gates.push_back({.type = gate.type, - .parameter = gate.parameter, - .qubitId = {qubitId}}); - } - gates.globalPhase += eulerDecomp->globalPhase; + auto&& eulerDecomp = eulerDecompositions[index]; + for (auto&& gate : eulerDecomp.gates) { + gates.gates.push_back({.type = gate.type, + .parameter = gate.parameter, + .qubitId = {qubitId}}); } + gates.globalPhase += eulerDecomp.globalPhase; }; for (std::size_t i = 0; i < bestNbasis; ++i) { diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 102b1c3463..0609296383 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -138,7 +138,7 @@ struct GateDecompositionPattern final auto newComplexity = sequence.complexity(); if (newComplexity < bestSequence.second) { - bestSequence = std::make_pair(sequence, newComplexity); + bestSequence = std::make_pair(std::move(sequence), newComplexity); } } } else { @@ -593,6 +593,29 @@ struct GateDecompositionPattern final #ifndef NDEBUG Eigen::Matrix4cd unitaryMatrix = Eigen::Matrix4cd::Identity(); #endif // NDEBUG + + auto addSingleQubitRotationGate = [&](auto&& gate) { + assert(gate.qubitId.size() == 1); + UnitaryOpInterface newGate; + if (gate.type == qc::RX) { + newGate = rewriter.create(location, inQubits[gate.qubitId[0]], + gate.parameter[0]); + } else if (gate.type == qc::RY) { + newGate = rewriter.create(location, inQubits[gate.qubitId[0]], + gate.parameter[0]); + } else if (gate.type == qc::RZ) { + newGate = rewriter.create(location, inQubits[gate.qubitId[0]], + gate.parameter[0]); + } +#ifndef NDEBUG + unitaryMatrix = decomposition::expandToTwoQubits( + newGate.getUnitaryMatrix().value(), + gate.qubitId[0]) * + unitaryMatrix; +#endif // NDEBUG + return newGate; + }; + for (auto&& gate : sequence.gates) { // TODO: need to add each basis gate we want to use if (gate.type == qc::X && gate.qubitId.size() > 1) { @@ -607,38 +630,9 @@ struct GateDecompositionPattern final unitaryMatrix; #endif // NDEBUG updateInQubits(gate.qubitId, newGate); - } else if (gate.type == qc::RX) { - assert(gate.qubitId.size() == 1); - auto newGate = rewriter.create( - location, inQubits[gate.qubitId[0]], gate.parameter[0]); -#ifndef NDEBUG - unitaryMatrix = - decomposition::expandToTwoQubits(newGate.getUnitaryMatrix().value(), - gate.qubitId[0]) * - unitaryMatrix; -#endif // NDEBUG - updateInQubits(gate.qubitId, newGate); - } else if (gate.type == qc::RY) { - assert(gate.qubitId.size() == 1); - auto newGate = rewriter.create( - location, inQubits[gate.qubitId[0]], gate.parameter[0]); -#ifndef NDEBUG - unitaryMatrix = - decomposition::expandToTwoQubits(newGate.getUnitaryMatrix().value(), - gate.qubitId[0]) * - unitaryMatrix; -#endif // NDEBUG - updateInQubits(gate.qubitId, newGate); - } else if (gate.type == qc::RZ) { - assert(gate.qubitId.size() == 1); - auto newGate = rewriter.create( - location, inQubits[gate.qubitId[0]], gate.parameter[0]); -#ifndef NDEBUG - unitaryMatrix = - decomposition::expandToTwoQubits(newGate.getUnitaryMatrix().value(), - gate.qubitId[0]) * - unitaryMatrix; -#endif // NDEBUG + } else if (gate.type == qc::RX || gate.type == qc::RY || + gate.type == qc::RZ) { + auto newGate = addSingleQubitRotationGate(gate); updateInQubits(gate.qubitId, newGate); } else { llvm::reportFatalInternalError("Unsupported gate type in decomposition " diff --git a/mlir/unittests/Passes/Decomposition/utils.h b/mlir/unittests/Passes/Decomposition/utils.h index 858f8587a3..c6db2eb76e 100644 --- a/mlir/unittests/Passes/Decomposition/utils.h +++ b/mlir/unittests/Passes/Decomposition/utils.h @@ -14,10 +14,12 @@ #include #include +#include #include template [[nodiscard]] MatrixType randomUnitaryMatrix() { [[maybe_unused]] static auto rng = []() { return std::mt19937{123456UL}; }(); + // NOLINTNEXTLINE(misc-const-correctness) std::uniform_real_distribution dist(-1.0, 1.0); MatrixType randomMatrix; for (auto& x : randomMatrix.reshaped()) { From e7287118d2d8e9aff5b0ecf8d9abf2f4c8dd74a8 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 8 Feb 2026 13:50:17 +0100 Subject: [PATCH 220/237] remove static random generator for better reproducibility --- mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp | 4 ++-- .../Passes/Decomposition/test_euler_decomposition.cpp | 4 +++- .../Passes/Decomposition/test_weyl_decomposition.cpp | 4 +++- mlir/unittests/Passes/Decomposition/utils.h | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp index 5dcc4dc2f7..727eeaa9d3 100644 --- a/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp +++ b/mlir/unittests/Passes/Decomposition/test_basis_decomposer.cpp @@ -95,13 +95,13 @@ TEST_P(BasisDecomposerTest, TestApproximation) { TEST(BasisDecomposerTest, Random) { constexpr auto maxIterations = 2000; + std::mt19937 rng{123456UL}; const llvm::SmallVector basisGates{ {.type = qc::X, .parameter = {}, .qubitId = {0, 1}}, {.type = qc::X, .parameter = {}, .qubitId = {1, 0}}}; const llvm::SmallVector eulerBases = { EulerBasis::XYX, EulerBasis::ZXZ, EulerBasis::ZYZ, EulerBasis::XZX}; - std::mt19937 rng{123456UL}; std::uniform_int_distribution distBasisGate{ 0, basisGates.size() - 1}; std::uniform_int_distribution distEulerBases{ @@ -116,7 +116,7 @@ TEST(BasisDecomposerTest, Random) { auto selectRandomBasisGate = [&]() { return basisGates[distBasisGate(rng)]; }; for (int i = 0; i < maxIterations; ++i) { - auto originalMatrix = randomUnitaryMatrix(); + auto originalMatrix = randomUnitaryMatrix(rng); auto targetDecomposition = TwoQubitWeylDecomposition::create(originalMatrix, 1.0); diff --git a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp index 2281643dd6..d7a86cfb6d 100644 --- a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp @@ -64,11 +64,13 @@ TEST_P(EulerDecompositionTest, TestExact) { TEST(EulerDecompositionTest, Random) { constexpr auto maxIterations = 10000; + std::mt19937 rng{12345678UL}; + auto eulerBases = std::array{EulerBasis::XYX, EulerBasis::XZX, EulerBasis::ZYZ, EulerBasis::ZXZ}; std::size_t currentEulerBase = 0; for (int i = 0; i < maxIterations; ++i) { - auto originalMatrix = randomUnitaryMatrix(); + auto originalMatrix = randomUnitaryMatrix(rng); auto eulerBasis = eulerBases[currentEulerBase++ % eulerBases.size()]; auto decomposition = EulerDecomposition::generateCircuit( eulerBasis, originalMatrix, true, std::nullopt); diff --git a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp index c26bdfd3de..d233dbab21 100644 --- a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp @@ -73,8 +73,10 @@ TEST_P(WeylDecompositionTest, TestApproximation) { TEST(WeylDecompositionTest, Random) { constexpr auto maxIterations = 5000; + std::mt19937 rng{1234567UL}; + for (int i = 0; i < maxIterations; ++i) { - auto originalMatrix = randomUnitaryMatrix(); + auto originalMatrix = randomUnitaryMatrix(rng); auto decomposition = TwoQubitWeylDecomposition::create(originalMatrix, 1.0 - 1e-12); auto restoredMatrix = WeylDecompositionTest::restore(decomposition); diff --git a/mlir/unittests/Passes/Decomposition/utils.h b/mlir/unittests/Passes/Decomposition/utils.h index c6db2eb76e..2b1b86fb78 100644 --- a/mlir/unittests/Passes/Decomposition/utils.h +++ b/mlir/unittests/Passes/Decomposition/utils.h @@ -17,8 +17,8 @@ #include #include -template [[nodiscard]] MatrixType randomUnitaryMatrix() { - [[maybe_unused]] static auto rng = []() { return std::mt19937{123456UL}; }(); +template +[[nodiscard]] MatrixType randomUnitaryMatrix(std::mt19937& rng) { // NOLINTNEXTLINE(misc-const-correctness) std::uniform_real_distribution dist(-1.0, 1.0); MatrixType randomMatrix; From 7d9aa330598608d608b344e62cbef9a2cb7eecbc Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 8 Feb 2026 13:58:07 +0100 Subject: [PATCH 221/237] minor fixes --- mlir/lib/Passes/CMakeLists.txt | 10 ++++++---- .../Passes/Patterns/Decomposition/BasisDecomposer.cpp | 5 +++++ mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp | 3 +++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/mlir/lib/Passes/CMakeLists.txt b/mlir/lib/Passes/CMakeLists.txt index e200953c65..7e5480bc84 100644 --- a/mlir/lib/Passes/CMakeLists.txt +++ b/mlir/lib/Passes/CMakeLists.txt @@ -6,17 +6,19 @@ # # Licensed under the MIT License -get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) -set(LIBRARIES ${dialect_libs} MQT::CoreIR Eigen3::Eigen) - file(GLOB_RECURSE PASSES_SOURCES *.cpp) +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) + add_mlir_library( QcoPasses ${PASSES_SOURCES} LINK_LIBS PUBLIC - ${LIBRARIES} + MQT::CoreIR Eigen3::Eigen + PRIVATE + ${dialect_libs} + PRIVATE DEPENDS QcoPassesIncGen) diff --git a/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp b/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp index 7e5e67c946..9121dac27d 100644 --- a/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp +++ b/mlir/lib/Passes/Patterns/Decomposition/BasisDecomposer.cpp @@ -186,6 +186,11 @@ std::optional TwoQubitBasisDecomposer::twoQubitDecompose( }; // number of basis gates that need to be used in the decomposition auto bestNbasis = numBasisGateUses.value_or(getDefaultNbasis()); + if (bestNbasis > 1 && !isSuperControlled) { + // cannot reliably decompose with more than one basis gate and a + // non-super-controlled basis gate + return std::nullopt; + } auto chooseDecomposition = [&]() { if (bestNbasis == 0) { return decomp0(targetDecomposition); diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 0609296383..6fbb16fca5 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -606,6 +606,9 @@ struct GateDecompositionPattern final } else if (gate.type == qc::RZ) { newGate = rewriter.create(location, inQubits[gate.qubitId[0]], gate.parameter[0]); + } else { + llvm::reportFatalInternalError( + "Unknown single-qubit rotation gate while applying decomposition!"); } #ifndef NDEBUG unitaryMatrix = decomposition::expandToTwoQubits( From b3b4a86ba440ef3a0dfc256add6b9ad630bd6dd7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 8 Feb 2026 12:59:15 +0000 Subject: [PATCH 222/237] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/lib/Passes/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mlir/lib/Passes/CMakeLists.txt b/mlir/lib/Passes/CMakeLists.txt index 7e5480bc84..7eaf9ec2d1 100644 --- a/mlir/lib/Passes/CMakeLists.txt +++ b/mlir/lib/Passes/CMakeLists.txt @@ -15,7 +15,8 @@ add_mlir_library( ${PASSES_SOURCES} LINK_LIBS PUBLIC - MQT::CoreIR Eigen3::Eigen + MQT::CoreIR + Eigen3::Eigen PRIVATE ${dialect_libs} PRIVATE From 4ea669417ad68a872bb20157e760aba31fb38858 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 8 Feb 2026 14:47:13 +0100 Subject: [PATCH 223/237] minor fixes --- mlir/lib/Passes/CMakeLists.txt | 1 - mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp | 4 +++- .../Passes/Decomposition/test_euler_decomposition.cpp | 1 + .../Passes/Decomposition/test_weyl_decomposition.cpp | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/mlir/lib/Passes/CMakeLists.txt b/mlir/lib/Passes/CMakeLists.txt index 7eaf9ec2d1..58962248bd 100644 --- a/mlir/lib/Passes/CMakeLists.txt +++ b/mlir/lib/Passes/CMakeLists.txt @@ -19,7 +19,6 @@ add_mlir_library( Eigen3::Eigen PRIVATE ${dialect_libs} - PRIVATE DEPENDS QcoPassesIncGen) diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 6fbb16fca5..dcc1b76b00 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -620,7 +620,9 @@ struct GateDecompositionPattern final }; for (auto&& gate : sequence.gates) { - // TODO: need to add each basis gate we want to use + // these if branches should handle all gates in availableSingleQubitGates + // and availableTwoQubitGates; additional gates will need to be added when + // using new euler bases or basis gates if (gate.type == qc::X && gate.qubitId.size() > 1) { // X gate involving more than one qubit is a CX gate: // qubit position 0 is target, 1 is control diff --git a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp index d7a86cfb6d..fcd942e0a0 100644 --- a/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_euler_decomposition.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include using namespace mlir::qco; diff --git a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp index d233dbab21..58849a344e 100644 --- a/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp +++ b/mlir/unittests/Passes/Decomposition/test_weyl_decomposition.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include using namespace mlir::qco; From 310d35ab13ca7851d6d3b119a24626b547b0f93f Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 13 Feb 2026 11:26:20 +0100 Subject: [PATCH 224/237] reapply thesis-evaluation (revert 8b7f0bc5640cd93093db534bf608652c02a694c0) --- mlir/include/mlir/Passes/Passes.h | 15 +- mlir/lib/Compiler/CompilerPipeline.cpp | 88 +++--- mlir/lib/Passes/GateDecompositionPass.cpp | 83 +++++- .../Patterns/GateDecompositionPattern.cpp | 117 +++++++- thesis-evaluation/__init__.py | 0 thesis-evaluation/evaluate.py | 34 +++ thesis-evaluation/evaluate_cache.json | 1 + .../figures/numberOfTwoQubitCreations.pdf | Bin 0 -> 16543 bytes .../figures/subCircuitComplexityChange.pdf | Bin 0 -> 21030 bytes .../successfulSingleQubitDecompositions.pdf | Bin 0 -> 18729 bytes .../successfulTwoQubitDecompositions.pdf | Bin 0 -> 19324 bytes .../figures/timeInCircuitCollection.pdf | Bin 0 -> 22681 bytes .../timeInCircuitCollectionStandalone.pdf | Bin 0 -> 18602 bytes .../timeInSingleQubitDecomposition.pdf | Bin 0 -> 18996 bytes .../figures/timeInTwoQubitDecomposition.pdf | Bin 0 -> 19323 bytes .../timePerSingleQubitDecomposition.pdf | Bin 0 -> 17857 bytes .../figures/timePerTwoQubitDecomposition.pdf | Bin 0 -> 18236 bytes .../figures/totalCircuitCollections.pdf | Bin 0 -> 17475 bytes .../totalSingleQubitDecompositions.pdf | Bin 0 -> 19814 bytes .../figures/totalTouchedGates.pdf | Bin 0 -> 18909 bytes .../figures/totalTwoQubitDecompositions.pdf | Bin 0 -> 19402 bytes .../figures/twoQubitCreationTime.pdf | Bin 0 -> 22947 bytes thesis-evaluation/qiskit_run.py | 167 +++++++++++ thesis-evaluation/run.sh | 33 +++ thesis-evaluation/total_evaluate.py | 274 ++++++++++++++++++ 25 files changed, 756 insertions(+), 56 deletions(-) create mode 100644 thesis-evaluation/__init__.py create mode 100644 thesis-evaluation/evaluate.py create mode 100644 thesis-evaluation/evaluate_cache.json create mode 100644 thesis-evaluation/figures/numberOfTwoQubitCreations.pdf create mode 100644 thesis-evaluation/figures/subCircuitComplexityChange.pdf create mode 100644 thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf create mode 100644 thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf create mode 100644 thesis-evaluation/figures/timeInCircuitCollection.pdf create mode 100644 thesis-evaluation/figures/timeInCircuitCollectionStandalone.pdf create mode 100644 thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf create mode 100644 thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf create mode 100644 thesis-evaluation/figures/timePerSingleQubitDecomposition.pdf create mode 100644 thesis-evaluation/figures/timePerTwoQubitDecomposition.pdf create mode 100644 thesis-evaluation/figures/totalCircuitCollections.pdf create mode 100644 thesis-evaluation/figures/totalSingleQubitDecompositions.pdf create mode 100644 thesis-evaluation/figures/totalTouchedGates.pdf create mode 100644 thesis-evaluation/figures/totalTwoQubitDecompositions.pdf create mode 100644 thesis-evaluation/figures/twoQubitCreationTime.pdf create mode 100644 thesis-evaluation/qiskit_run.py create mode 100755 thesis-evaluation/run.sh create mode 100644 thesis-evaluation/total_evaluate.py diff --git a/mlir/include/mlir/Passes/Passes.h b/mlir/include/mlir/Passes/Passes.h index 34e5a770ba..ec21ae3523 100644 --- a/mlir/include/mlir/Passes/Passes.h +++ b/mlir/include/mlir/Passes/Passes.h @@ -27,7 +27,20 @@ namespace mlir::qco { #define GEN_PASS_DECL #include "mlir/Passes/Passes.h.inc" // IWYU pragma: export -void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns); +void populateGateDecompositionPatterns( + mlir::RewritePatternSet& patterns, + llvm::Statistic& twoQubitCreationTime, + llvm::Statistic& numberOfTwoQubitCreations, + llvm::Statistic& successfulSingleQubitDecompositions, + llvm::Statistic& totalSingleQubitDecompositions, + llvm::Statistic& successfulTwoQubitDecompositions, + llvm::Statistic& totalTwoQubitDecompositions, + llvm::Statistic& totalCircuitCollections, + llvm::Statistic& totalTouchedGates, + llvm::Statistic& subCircuitComplexityChange, + llvm::Statistic& timeInCircuitCollection, + llvm::Statistic& timeInSingleQubitDecomposition, + llvm::Statistic& timeInTwoQubitDecomposition); //===----------------------------------------------------------------------===// // Registration diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index 8a09fe56d3..0ee6d530a2 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -124,17 +124,17 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, } // Stage 2: QC canonicalization - addCleanupPasses(pm); - if (pm.run(module).failed()) { - return failure(); - } - if (record != nullptr && config_.recordIntermediates) { - record->afterInitialCanon = captureIR(module); - if (config_.printIRAfterAllStages) { - prettyPrintStage(module, "Initial QC Canonicalization", ++currentStage, - totalStages); - } - } + // addCleanupPasses(pm); + // if (pm.run(module).failed()) { + // return failure(); + // } + // if (record != nullptr && config_.recordIntermediates) { + // record->afterInitialCanon = captureIR(module); + // if (config_.printIRAfterAllStages) { + // prettyPrintStage(module, "Initial QC Canonicalization", ++currentStage, + // totalStages); + // } + // } pm.clear(); // Stage 3: QC-to-QCO conversion @@ -152,17 +152,17 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, pm.clear(); // Stage 4: QCO canonicalization - addCleanupPasses(pm); - if (failed(pm.run(module))) { - return failure(); - } - if (record != nullptr && config_.recordIntermediates) { - record->afterQCOCanon = captureIR(module); - if (config_.printIRAfterAllStages) { - prettyPrintStage(module, "Initial QCO Canonicalization", ++currentStage, - totalStages); - } - } + // addCleanupPasses(pm); + // if (failed(pm.run(module))) { + // return failure(); + // } + // if (record != nullptr && config_.recordIntermediates) { + // record->afterQCOCanon = captureIR(module); + // if (config_.printIRAfterAllStages) { + // prettyPrintStage(module, "Initial QCO Canonicalization", ++currentStage, + // totalStages); + // } + // } pm.clear(); // Stage 5: Optimization passes @@ -180,17 +180,17 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, pm.clear(); // Stage 6: QCO canonicalization - addCleanupPasses(pm); - if (failed(pm.run(module))) { - return failure(); - } - if (record != nullptr && config_.recordIntermediates) { - record->afterOptimizationCanon = captureIR(module); - if (config_.printIRAfterAllStages) { - prettyPrintStage(module, "Final QCO Canonicalization", ++currentStage, - totalStages); - } - } + // addCleanupPasses(pm); + // if (failed(pm.run(module))) { + // return failure(); + // } + // if (record != nullptr && config_.recordIntermediates) { + // record->afterOptimizationCanon = captureIR(module); + // if (config_.printIRAfterAllStages) { + // prettyPrintStage(module, "Final QCO Canonicalization", ++currentStage, + // totalStages); + // } + // } pm.clear(); // Stage 7: QCO-to-QC conversion @@ -208,17 +208,17 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, pm.clear(); // Stage 8: QC canonicalization - addCleanupPasses(pm); - if (failed(pm.run(module))) { - return failure(); - } - if (record != nullptr && config_.recordIntermediates) { - record->afterQCCanon = captureIR(module); - if (config_.printIRAfterAllStages) { - prettyPrintStage(module, "Final QC Canonicalization", ++currentStage, - totalStages); - } - } + // addCleanupPasses(pm); + // if (failed(pm.run(module))) { + // return failure(); + // } + // if (record != nullptr && config_.recordIntermediates) { + // record->afterQCCanon = captureIR(module); + // if (config_.printIRAfterAllStages) { + // prettyPrintStage(module, "Final QC Canonicalization", ++currentStage, + // totalStages); + // } + // } pm.clear(); // Stage 9: QC-to-QIR conversion (optional) diff --git a/mlir/lib/Passes/GateDecompositionPass.cpp b/mlir/lib/Passes/GateDecompositionPass.cpp index ee90801549..5ac5ded2d4 100644 --- a/mlir/lib/Passes/GateDecompositionPass.cpp +++ b/mlir/lib/Passes/GateDecompositionPass.cpp @@ -28,6 +28,46 @@ namespace mlir::qco { struct GateDecompositionPass final : impl::GateDecompositionPassBase { + GateDecompositionPass() = default; + GateDecompositionPass(const GateDecompositionPass& other) + : impl::GateDecompositionPassBase{other}, + twoQubitCreationTime{this, "twoQubitCreationTime", + "Creation time of basis decomposers"}, + numberOfTwoQubitCreations{ + this, "numberOfTwoQubitCreations", + "Number of times basis decomposers are created"}, + successfulSingleQubitDecompositions{ + this, "successfulSingleQubitDecompositions", + "Number of times a single-qubit decomposition was applied"}, + totalSingleQubitDecompositions{this, "totalSingleQubitDecompositions", + "Number of times (only) a single-qubit " + "decomposition was calculated"}, + successfulTwoQubitDecompositions{ + this, "successfulTwoQubitDecompositions", + "Number of times a two-qubit decomposition was applied"}, + totalTwoQubitDecompositions{ + this, "totalTwoQubitDecompositions", + "Number of times a two-qubit decomposition was calculated"}, + totalCircuitCollections{this, "totalCircuitCollections", + "Number of times a sub-circuit was collected"}, + totalTouchedGates{ + this, "totalTouchedGates", + "Number of gates that were looked at (in sub-circuit collection)"}, + subCircuitComplexityChange{ + this, "subCircuitComplexityChange", + "Increase or decrease of complexity in sub-circuit"}, + timeInCircuitCollection{this, "timeInCircuitCollection", + "Time spent in circuit collection (µs)"}, + timeInSingleQubitDecomposition{ + this, "timeInSingleQubitDecomposition", + "Time spent in single-qubit decomposition (µs)"}, + timeInTwoQubitDecomposition{ + this, "timeInTwoQubitDecomposition", + "Time spent in single-qubit decomposition (µs)"} {} + GateDecompositionPass(GateDecompositionPass&& other) = delete; + GateDecompositionPass& operator=(const GateDecompositionPass& other) = delete; + GateDecompositionPass& operator=(GateDecompositionPass&& other) = delete; + void runOnOperation() override { // Get the current operation being operated on. auto op = getOperation(); @@ -35,7 +75,13 @@ struct GateDecompositionPass final // Define the set of patterns to use. mlir::RewritePatternSet patterns(ctx); - populateGateDecompositionPatterns(patterns); + populateGateDecompositionPatterns( + patterns, twoQubitCreationTime, numberOfTwoQubitCreations, + successfulSingleQubitDecompositions, totalSingleQubitDecompositions, + successfulTwoQubitDecompositions, totalTwoQubitDecompositions, + totalCircuitCollections, totalTouchedGates, subCircuitComplexityChange, + timeInCircuitCollection, timeInSingleQubitDecomposition, + timeInTwoQubitDecomposition); // Configure greedy driver mlir::GreedyRewriteConfig config; @@ -51,6 +97,41 @@ struct GateDecompositionPass final signalPassFailure(); } } + + Statistic twoQubitCreationTime{this, "twoQubitCreationTime", + "Creation time of basis decomposers"}; + Statistic numberOfTwoQubitCreations{ + this, "numberOfTwoQubitCreations", + "Number of times basis decomposers are created"}; + Statistic successfulSingleQubitDecompositions{ + this, "successfulSingleQubitDecompositions", + "Number of times a single-qubit decomposition was applied"}; + Statistic totalSingleQubitDecompositions{ + this, "totalSingleQubitDecompositions", + "Number of times (only) a single-qubit decomposition was calculated"}; + Statistic successfulTwoQubitDecompositions{ + this, "successfulTwoQubitDecompositions", + "Number of times a two-qubit decomposition was applied"}; + Statistic totalTwoQubitDecompositions{ + this, "totalTwoQubitDecompositions", + "Number of times a two-qubit decomposition was calculated"}; + Statistic totalCircuitCollections{ + this, "totalCircuitCollections", + "Number of times a sub-circuit was collected"}; + Statistic totalTouchedGates{ + this, "totalTouchedGates", + "Number of gates that were looked at (in sub-circuit collection)"}; + Statistic subCircuitComplexityChange{ + this, "subCircuitComplexityChange", + "Increase or decrease of complexity in sub-circuit"}; + Statistic timeInCircuitCollection{this, "timeInCircuitCollection", + "Time spent in circuit collection (µs)"}; + Statistic timeInSingleQubitDecomposition{ + this, "timeInSingleQubitDecomposition", + "Time spent in single-qubit decomposition (µs)"}; + Statistic timeInTwoQubitDecomposition{ + this, "timeInTwoQubitDecomposition", + "Time spent in single-qubit decomposition (µs)"}; }; } // namespace mlir::qco diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index dcc1b76b00..eea288b437 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -73,18 +73,49 @@ struct GateDecompositionPattern final * a decomposition if the (sub)circuit only contains * gates available as basis gates or euler bases */ - explicit GateDecompositionPattern(mlir::MLIRContext* context, - llvm::SmallVector basisGates, - llvm::SmallVector eulerBasis, - bool singleQubitOnly, bool forceApplication) + explicit GateDecompositionPattern( + mlir::MLIRContext* context, llvm::SmallVector basisGates, + llvm::SmallVector eulerBasis, bool singleQubitOnly, + bool forceApplication, llvm::Statistic& twoQubitCreationTime, + llvm::Statistic& numberOfTwoQubitCreations, + llvm::Statistic& successfulSingleQubitDecompositions, + llvm::Statistic& totalSingleQubitDecompositions, + llvm::Statistic& successfulTwoQubitDecompositions, + llvm::Statistic& totalTwoQubitDecompositions, + llvm::Statistic& totalCircuitCollections, + llvm::Statistic& totalTouchedGates, + llvm::Statistic& subCircuitComplexityChange, + llvm::Statistic& timeInCircuitCollection, + llvm::Statistic& timeInSingleQubitDecomposition, + llvm::Statistic& timeInTwoQubitDecomposition) : OpInterfaceRewritePattern(context), decomposerBasisGates{std::move(basisGates)}, decomposerEulerBases{std::move(eulerBasis)}, - singleQubitOnly{singleQubitOnly}, forceApplication{forceApplication} { + singleQubitOnly{singleQubitOnly}, forceApplication{forceApplication}, + twoQubitCreationTime{twoQubitCreationTime}, + numberOfTwoQubitCreations{numberOfTwoQubitCreations}, + successfulSingleQubitDecompositions{ + successfulSingleQubitDecompositions}, + totalSingleQubitDecompositions{totalSingleQubitDecompositions}, + successfulTwoQubitDecompositions{successfulTwoQubitDecompositions}, + totalTwoQubitDecompositions{totalTwoQubitDecompositions}, + totalCircuitCollections{totalCircuitCollections}, + totalTouchedGates{totalTouchedGates}, + subCircuitComplexityChange{subCircuitComplexityChange}, + timeInCircuitCollection{timeInCircuitCollection}, + timeInSingleQubitDecomposition{timeInSingleQubitDecomposition}, + timeInTwoQubitDecomposition{timeInTwoQubitDecomposition} { + ++numberOfTwoQubitCreations; + auto startTime = std::chrono::steady_clock::now(); for (auto&& basisGate : decomposerBasisGates) { basisDecomposers.push_back(decomposition::TwoQubitBasisDecomposer::create( basisGate, DEFAULT_FIDELITY)); } + auto endTime = std::chrono::steady_clock::now(); + twoQubitCreationTime += + std::chrono::duration_cast(endTime - + startTime) + .count(); auto&& [singleQubitGates, twoQubitGates] = getDecompositionGates(); availableSingleQubitGates = std::move(singleQubitGates); availableTwoQubitGates = std::move(twoQubitGates); @@ -103,13 +134,23 @@ struct GateDecompositionPattern final return mlir::failure(); } - auto collectSeries = [](UnitaryOpInterface op, bool singleQubitOnly) { + auto collectSeries = [this](UnitaryOpInterface op, bool singleQubitOnly) { + ++totalCircuitCollections; if (singleQubitOnly) { return TwoQubitSeries::getSingleQubitSeries(op); } return TwoQubitSeries::getTwoQubitSeries(op); }; + auto startTime = std::chrono::steady_clock::now(); auto series = collectSeries(op, singleQubitOnly); + auto endTime = std::chrono::steady_clock::now(); + timeInCircuitCollection += + std::chrono::duration_cast(endTime - + startTime) + .count(); + // not really accurate since it neglects the "past the series" gates that + // terminated the series + totalTouchedGates += series.gates.size(); auto containsForeignGates = !series.containsOnlyGates( availableSingleQubitGates, availableTwoQubitGates); @@ -132,6 +173,10 @@ struct GateDecompositionPattern final // cannot process decomposition without the matrix of the series return mlir::failure(); } + // only count the multiple decompositions as "one" since the number of + // euler bases is constant + ++totalSingleQubitDecompositions; + startTime = std::chrono::steady_clock::now(); for (auto&& eulerBasis : decomposerEulerBases) { auto sequence = decomposition::EulerDecomposition::generateCircuit( eulerBasis, *unitaryMatrix, true, std::nullopt); @@ -141,6 +186,11 @@ struct GateDecompositionPattern final bestSequence = std::make_pair(std::move(sequence), newComplexity); } } + endTime = std::chrono::steady_clock::now(); + timeInSingleQubitDecomposition += + std::chrono::duration_cast(endTime - + startTime) + .count(); } else { // two-qubit series; perform two-qubit basis decomposition const auto unitaryMatrix = series.getUnitaryMatrix(); @@ -152,9 +202,13 @@ struct GateDecompositionPattern final decomposition::TwoQubitWeylDecomposition::create(*unitaryMatrix, DEFAULT_FIDELITY); + // only count the multiple decompositions as "one" since the number of + // euler bases is constant + ++totalTwoQubitDecompositions; + startTime = std::chrono::steady_clock::now(); for (const auto& decomposer : basisDecomposers) { auto sequence = decomposer.twoQubitDecompose( - targetDecomposition, decomposerEulerBases, DEFAULT_FIDELITY, false, + targetDecomposition, decomposerEulerBases, DEFAULT_FIDELITY, true, std::nullopt); if (sequence) { // decomposition successful @@ -166,6 +220,11 @@ struct GateDecompositionPattern final } } } + endTime = std::chrono::steady_clock::now(); + timeInTwoQubitDecomposition += + std::chrono::duration_cast(endTime - + startTime) + .count(); } if (bestSequence.second == std::numeric_limits::max()) { @@ -181,6 +240,13 @@ struct GateDecompositionPattern final return mlir::failure(); } + if (series.isSingleQubitSeries()) { + ++successfulSingleQubitDecompositions; + } else { + ++successfulTwoQubitDecompositions; + } + subCircuitComplexityChange += series.complexity - bestSequence.second; + applySeries(rewriter, series, bestSequence.first); return mlir::success(); @@ -697,13 +763,38 @@ struct GateDecompositionPattern final // configuration of pattern bool singleQubitOnly; bool forceApplication; + + llvm::Statistic& twoQubitCreationTime; + llvm::Statistic& numberOfTwoQubitCreations; + llvm::Statistic& successfulSingleQubitDecompositions; + llvm::Statistic& totalSingleQubitDecompositions; + llvm::Statistic& successfulTwoQubitDecompositions; + llvm::Statistic& totalTwoQubitDecompositions; + llvm::Statistic& totalCircuitCollections; + llvm::Statistic& totalTouchedGates; + llvm::Statistic& subCircuitComplexityChange; + llvm::Statistic& timeInCircuitCollection; + llvm::Statistic& timeInSingleQubitDecomposition; + llvm::Statistic& timeInTwoQubitDecomposition; }; /** * @brief Populates the given pattern set with patterns for gate * decomposition. */ -void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns) { +void populateGateDecompositionPatterns( + mlir::RewritePatternSet& patterns, llvm::Statistic& twoQubitCreationTime, + llvm::Statistic& numberOfTwoQubitCreations, + llvm::Statistic& successfulSingleQubitDecompositions, + llvm::Statistic& totalSingleQubitDecompositions, + llvm::Statistic& successfulTwoQubitDecompositions, + llvm::Statistic& totalTwoQubitDecompositions, + llvm::Statistic& totalCircuitCollections, + llvm::Statistic& totalTouchedGates, + llvm::Statistic& subCircuitComplexityChange, + llvm::Statistic& timeInCircuitCollection, + llvm::Statistic& timeInSingleQubitDecomposition, + llvm::Statistic& timeInTwoQubitDecomposition) { llvm::SmallVector basisGates; llvm::SmallVector eulerBases; basisGates.push_back({.type = qc::X, .parameter = {}, .qubitId = {0, 1}}); @@ -711,8 +802,14 @@ void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns) { eulerBases.push_back(GateDecompositionPattern::EulerBasis::ZYZ); eulerBases.push_back(GateDecompositionPattern::EulerBasis::XYX); eulerBases.push_back(GateDecompositionPattern::EulerBasis::ZXZ); - patterns.add(patterns.getContext(), basisGates, - eulerBases, false, false); + patterns.add( + patterns.getContext(), basisGates, eulerBases, false, true, + twoQubitCreationTime, numberOfTwoQubitCreations, + successfulSingleQubitDecompositions, totalSingleQubitDecompositions, + successfulTwoQubitDecompositions, totalTwoQubitDecompositions, + totalCircuitCollections, totalTouchedGates, subCircuitComplexityChange, + timeInCircuitCollection, timeInSingleQubitDecomposition, + timeInTwoQubitDecomposition); } } // namespace mlir::qco diff --git a/thesis-evaluation/__init__.py b/thesis-evaluation/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/thesis-evaluation/evaluate.py b/thesis-evaluation/evaluate.py new file mode 100644 index 0000000000..dda4b4fc5b --- /dev/null +++ b/thesis-evaluation/evaluate.py @@ -0,0 +1,34 @@ +import matplotlib.pyplot as plt +import glob +import os + +INPUT_DIR = "./tmp-output" + +def read_stats(file_name) -> dict[str, int]: + result = {} + + with open(file_name, "r") as f: + for line in f.readlines(): + line = line.lstrip() + line.removeprefix("(S)") + line = line.lstrip() + elements = line.split(" ") + elements = list(filter(lambda x: x != " " and len(x) > 0, elements)) + + metric = elements[2] + value = int(elements[1]) + result[metric] = value if value < 2**31 else value - 2**32 + + return result + +def evaluate(): + all_stats = {} + for file in glob.glob(f"{INPUT_DIR}/*.statistic"): + print(f"Processing {file}...") + name = os.path.basename(file).removesuffix(".statistic") + name = name.removesuffix(".qasm") + name = name.removesuffix(".mlir") + all_stats[name] = read_stats(file) + + print(all_stats) + return all_stats diff --git a/thesis-evaluation/evaluate_cache.json b/thesis-evaluation/evaluate_cache.json new file mode 100644 index 0000000000..92f5381da9 --- /dev/null +++ b/thesis-evaluation/evaluate_cache.json @@ -0,0 +1 @@ +{"mqt": {"modular_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 14.95, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 815.4999999999998}, "bmw_quark_cardinality_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 39.49999999999999, "timeInSingleQubitDecomposition": 8.250000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 13.999999999999995, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 11.999999999999996, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 839.1}, "draper_qft_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 22.699999999999992, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 755.4999999999999, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 822.0499999999998}, "vqe_two_local_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 291.70000000000005, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12806.850000000002, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 803.05}, "wstate_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -8.000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 29.499999999999996, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1395.1999999999998, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 782.05}, "bmw_quark_copula_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 149.25, "timeInSingleQubitDecomposition": 48.80000000000001, "timeInTwoQubitDecomposition": 829.85, "totalCircuitCollections": 32.999999999999986, "totalSingleQubitDecompositions": 8.000000000000002, "totalTouchedGates": 50.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 815.4499999999999}, "qaoa_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 11.600000000000001, "timeInSingleQubitDecomposition": 7.299999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 807.8500000000001}, "qftentangled_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -13.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 33.99999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1859.0000000000007, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 815.0}, "wstate_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.750000000000001, "timeInSingleQubitDecomposition": 6.049999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 795.2000000000002}, "ghz_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 22.699999999999992, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 831.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 800.55}, "qpeinexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -16.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 23.249999999999993, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1359.8000000000002, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 815.7}, "vqe_real_amp_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 14.799999999999999, "timeInSingleQubitDecomposition": 6.1999999999999975, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 807.55}, "vqe_su2_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 5.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 21.3, "timeInSingleQubitDecomposition": 7.95, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 823.3499999999999}, "qft_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 9.399999999999999, "timeInSingleQubitDecomposition": 6.749999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 811.0999999999999}, "bmw_quark_cardinality_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -55.99999999999998, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 2.999999999999999, "timeInCircuitCollection": 153.75, "timeInSingleQubitDecomposition": 27.250000000000007, "timeInTwoQubitDecomposition": 4109.2, "totalCircuitCollections": 47.999999999999986, "totalSingleQubitDecompositions": 4.000000000000001, "totalTouchedGates": 50.999999999999986, "totalTwoQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 796.4499999999999}, "bv_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 5.749999999999999, "timeInSingleQubitDecomposition": 6.049999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 806.4499999999999}, "ghz_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.5, "timeInSingleQubitDecomposition": 6.399999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 806.6500000000002}, "vqe_su2_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 626.4999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 22723.0, "totalCircuitCollections": 20.0, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 198.00000000000006, "totalTwoQubitDecompositions": 15.0, "twoQubitCreationTime": 825.6999999999999}, "qnn_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 39.550000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1033.7, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 13.000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 802.3499999999999}, "qpeexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 23.449999999999992, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 809.9, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 808.5000000000002}, "qnn_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 17.249999999999996, "timeInSingleQubitDecomposition": 7.800000000000001, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 786.6}, "vqe_real_amp_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 285.54999999999995, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12790.199999999999, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 806.9499999999999}, "qftentangled_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 10.099999999999998, "timeInSingleQubitDecomposition": 3.099999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 812.8000000000002}, "bv_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 2.0000000000000004, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 12.65, "timeInSingleQubitDecomposition": 8.500000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 2.0000000000000004, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 793.25}, "randomcircuit_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 10.4, "timeInSingleQubitDecomposition": 7.6499999999999995, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 794.25}, "qft_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -23.999999999999993, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 24.449999999999996, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1888.9499999999998, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 793.1999999999999}, "qaoa_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -6.999999999999997, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 27.500000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1481.3, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 801.7500000000001}}, "qiskit": {"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 220.62315, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 530.834}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1449.0686999999998, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 664.06875}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1400.6623, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 753.1374999999999}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1403.9076999999997, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 302.8408}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 7828.1339, "subCircuitComplexityChange": -75.0, "totalTwoQubitDecompositions": 5.999999999999998, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 2283.7117499999995}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 3831.9234500000002, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1648.71505}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1465.3955999999998, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1092.5579}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 187.1023, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 393.38485}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1550.9868500000002, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1698.9834500000002}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1430.4808500000001, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 552.24995}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1362.3799499999998, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 570.4323499999999}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1306.5448999999999, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 385.82995000000005}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1374.2908000000002, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 516.0969000000001}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1427.7414, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1202.9632000000001}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 180.8468, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 312.29975}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1457.9090999999999, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1094.43475}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 180.33175, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 282.276}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 169.9849, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 817.0377500000002}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1306.34635, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 336.2191000000001}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1333.5626999999997, "subCircuitComplexityChange": 15.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 712.1571000000001}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1426.60265, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 601.4511999999999}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1457.3285500000004, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 622.4298}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 175.84595, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 653.8033499999999}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 504.0352500000001, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1364.66555}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 165.24959999999996, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 163.43635000000003}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 165.68475, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 284.16045}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 154.48034999999996, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 365.45809999999994}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1490.2478999999998, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 576.6129999999999}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1428.98125, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 606.5438499999999}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 173.56869999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 284.78909999999996}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 169.89490000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 372.2458499999999}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1383.5119000000002, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 552.30705}}} \ No newline at end of file diff --git a/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf b/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a470700fd83bb9600297142eafed75bee885af0d GIT binary patch literal 16543 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 B|0Dna literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf b/thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ca1dcd1de4fa0332407cd9526368cf3928dac78c GIT binary patch literal 18729 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 literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf b/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf new file mode 100644 index 0000000000000000000000000000000000000000..fafaf6e95d502d4b1612672a0ad27a2a69772c77 GIT binary patch literal 19324 zcmb`v2Rzo__W*A1kxkZPZ=U7hv9kAG8IiqLJS0(wkiADzgp906cJ|KB2q_~G4H6>$ z???6d^ew;tr`P}Wf4r`H-{*brJ?EZt-shb69d9mOMP(s~Fr0|%;T)*6fd~QugFJ0q zh@_-IB8FGI>_H+5)+lQ?Pe+i5uC=4R4+siW&hM0|kPeR1h)I zv2n4tMS;Y=Un=>dR18qoD0>k6J3!YOg|hed0Ewb6i9`&XtnHjV96^X5cfCDr4eU`M za{#TPGC+#GKMEwG<_?e`|07rUk*k3$ejx|)=K#=jqpA0{2dMi_zKFiPkEgGEfnF`wf;m zgu>?E&bGF3VA{P@+!^qEd9*n{KI?fgsQFvR<%smJ^WzT`=B{-TP8&z>xweGE05k66tH|XUbIwj zsdq2fl``(R`5ikWx!%XTJTsC!NU)AfT)v!xs}rXKve43jnI zy*9!Xr<&4PG%~gZi0yyjCf@n9 zGYF%T&#Xx8E1#UHiia9XmN?bcg&HmtLA&%C6JA7CRFFrU{i44e=^njjt@DkVsCE8; z2?=Fzi8y;zkyqZTLiD{(0%Oo_ag6+DkmmD^Y3D|2ptkjiXW zyb@IwXyOz%qVD*xORr?_E8Vbhr(u~o#xypLar5I8{+zGj6X(KJ6oS)q$LA877PB9` zb<%BlY(}JKuJk!R>E=j)3p?q4@n>4ImtW`11$eB=K5(&n9U!nmWp0t(lOABW#3~*A zNO8cCuV+EVV8GIVxd(rk&5jBX6D za|~ZD=qK~g^+>#~fI5mk5@?FVKO6FJs=@I5+Ioh{zK}qPmF)-IM5$`SwYaf6DKcGh zD)7RbTSD~A>qcP+I+8;VRZ;mz+*~8vE$g*b7)vC%io*0GD}s((ig|kLhU@)R{kjHK zx8CHJfkc~2p=Nl7?!xrV8<5VFI;+n$1Myx^tWV{*lpRBxPmGr{{u)pj+Nl{xq7vO_8B74os8Ge^a%QO(+qA|ZvAq*Eo3QlJT-_AW7Qd}l<&^O z+5AZ;W_lFzvS7Dyy`~i%WTYPg{|9+6YH4JgL&RzX(#klf1mm_M!J64RRa zO_`RyXK3vMO_#>e;-R1nIF0+_FH|qwsYF!zF!XS%*goo%)^TfhYg5SJXs5Uy7^bCUF??dDow94a08@ z3uDcq#I^{Kd$f21g5;!!3|-Q*vC11I&d;>+tKo0KyyYlp-#_4iS*d{vSy^eC;Ljn< z#%M$MS?#PuQ{Hfwk+5|8Do!Ix&|kC|?qlrg{m{=XT>PmhZ?GS41|{}lHMl=ZahXJ8 zIW;D#*ufpw3fdG};BwDJZ(f%wgK~WNBdXQqkas=619_X$U}b~4em$>q0`md{ZeUY# z-)M(aSK5}sK(rLAQrN_5#=^0z!4cy@r`6@#1{wC#Bx5VfHP0N>HPe|##QhY`CinOp z8h0y2VH&#K9Xap)P@%XQY(b;AOp|YC;ntsGTz~}@FlifeIscefq=-rF_cd;?oh8}r&7q?&L$3QbBehC{39t? z>igiqEV`A;LPn)mx$A~NgjZj|<3B8m-^dU4z9XL#NRWmF9!|+2Kh}XnMJ$8fPZR4M z7V3U^86b2)$e>7L+KgH=A6Iet@nmGNusT-jyY$v|GoHu%#g8?upuH(elZgVmGOO((Dd=YAc2m8TRq#B8?Thnj%pHR#yyVYTRDSkMBW&6BAdGZ$kYh^*k61r$BcC~sDQN@; z$L*XusU{&QHc3}*TvDB#GZ7p6gEL&NM5a=FH;Vj|<*51X&luf;MxG6ZsjBl%;WBH( zhPlNt*7zC9yg_M=%K_6k<^iUMU#UoC!&>OdMn zp`8Mn6&%svK33BqCzZ@`Df3+u^XO;bR4TK4{>E4}1;U5@0sy}MmJSq;|D zigW|9i$@D9iHI1U@%DKU8q2ZFYT5pts+S|i#%`_G3C}}&n-UdtxG2JMW zW*D_f=_2Dj!1CtQyifCQRHieu9M&iXj2~eF_bl6t^a~7UC6AoJ{)3DOI|W0P*FiCJ zBFP7W^DX@C?I z3%SlPAypNNc(p7xei!zpoBp2==Ljc8RiIsz>UZp&O}W_9t+-qt8y6Z*(gZtWQVTvN z)(}`1n7}B+U-MR$G|PEonz9oaYbdI@5=L9hZ;2t>7)->YMG~@8vqd^0ZOdl>{b_tO z$`&(Q*W@axNBUPB;`_%E6imZ}SW|N+?$2?2m59#P)82?{KGSjAIlg(q%@7kX`l;OP zjH-Pu8useS=P|nFh|;o)T@q)X<8Uh`kXLR?=A?~R^KR%?X>}NBJH6}eVwaUy^>&P} zBvUDV{qCHU`5n`(67%-hZ#2C7>PA33HuXA$B&=+NfFtW;FJ^&6&KFPszG$yQk@j}O zxd=ggzs*G^@_M>@GU5!+)|(A>YeuH*TV-Zk@4n4;C6}+2`1{;HpF6g4(dd>3VJW7j zm*z&S8POZxrx!RYsOtMStQ}^}Q){kMsx{rWFGy+BL@>&>sju-Y ztopvuN76G=0QW5~69@aYVGX=DLz^8y^!Q9wN1;MsWM86NMU;V__9KQYF0YUf$1jMh zb^i15lUGz$ryKORBDi8aW(5*S^X(eipJr?s9pko&tF|SrdGLTs}Fi$(JepQh#nQdCma`r+k#2 z9kf=VnRC1S9Ni4rV<*c!>{zC0=BsQs&yNUOiP^(3Hw86(x!Gq``ds?Ek~Pa0W3N*4 z*XyUUKLEHMY?tybCF#oRyFOhAg8hEY_GNZkdH2uJZ8UCbBht6&j;2wTuHT>d{uH{G zSXNT^toqDLj6Z3F>E3m9F1ty}SZf-%k2(7NSq`Gf7`bNY%HnLY_~%c``pd$L?&Gh|w85h%wy z_Cw`nAv2A$D5_8)|i6(RynX88ir^IFzzaxF_EqYJ;npPPAd# zJ#KIeElp0Cnmx82g0iggIS)~WDNMHnJ-)V;Q5`sFYB_AmeOTGO%9)QHr8qgNgoGNcy z9i6+qd=ZJg5}m3y?nr!ax+&`mPr_674NFqi$W*m0QnaKh?KD&|C&Y+vqcma8oY!zGjUR|UE|4$E z;DK4OGt#j$=ANw&=IQ&^COR}sQs-wLnQbS*d1|>yoU=5Yr7jmvh~6tC@r`qjLTq~w z=lk6*iK#6pL0qnMbxLHpP#xRpr6u>s=}x*~!jf6~Ykp~_?K2(r$~4dL&L^yA6=+;e z@p}(BcY-P&bFc^FjV*@HRT8TsW<0P~@Y{TL$XKRwQqgui-kl-!ByI^qH3_Q3O@(E9 zb(<0dVCv2hr3C_}ZVE7UvMA%kyA}qqgT7j_XK`Fjl3E>9N;eomSJ7T&Bh7yaGhF?p zegP2I@yN1ZXZ*yr4GG?-Zw(}|`aa(O09ON~mJ$Ba+~)wGHtY7LM5*fTUEh*K5A}CA z`SnK4H@ViZYRQbojNI9`9(%A?Fuv+H@Vr&(?^AjqcZ?KebnATy@Q{X#swwe}CGUKW;ifQR_t%zbm3$X5^-O&{8n-f}_E@O<_ow;L}uR5No z+z*mCc7jF@sp}Js=Blf+DK1mXFQ>{!7Qgv7jM5|1qso|}zhk)UIHk$qW3e4i+m@AT zSCeDbf}7+hWK-g2gfWRvEO_8r{9Kw!9H|)bK{N90LxA_i%9O=a33xBhrg3Vaijq)( zv5Dv%Na;7w`pwt?k~-Jua5nQ@)^#`m$~bA{C-2z{Yd+FqEMekmywnGtEUf?K{jpf^ z{#I$=*X`5?QT>(iOAF;G=gjx9N%dcxtEn$a+Q~T{;@<1p-0c{~&+G_fyS_!aRzW_~ zu$wcYeE(9?c+}=v$Ln`u5$VU&Pv~pK7Fg|Ewx3;RI&wc?53K)o@a2t8#PvWQ{~&Sp z*XL4H2_gfiFlqI%g5g*JNmtKwy5hiD6;FV3Q;GYxm=YgcJ(KT>BazmQ2FmI1*70@} z^tG)s6KsiMF-KbAC^ko7l;#eAjqiGkEs^1@oW-1f>PVp35{1bqNqv69J$bNyJ|tIy z{`0LTZL-U^>6hb>lYF&0-mXhzB+uIJJ&7d6V_)bFeh=4XNN@97DOZ|dJ`-T@JQ%d) zex{eiz14#vr_t4d3_tAUO*kkrkTGoVOl_PDcC2a>ekV%yZLB1AXzvp_!^{Q2%Cod% z7Nsx=wWM-i25QBY*t|YzaziuIg3DziZ#mT)B<*z1>?3ZmCA)Bt9=f!+1}XBGj_pt@ zw=K`|7HX6ae+fB`Lnv{rrL7Bq-g)QhTC--~# z*UK6Kv92fG33A}l2q;YU02O1@J`kN$)LY(>V^XE`_6j5?;;|ni=DPvVsApKuBwd0E z-f!VrU)BtGXMNI~Tg*!U<1zzl6MlFye9deA9x!fdg`wHJKv}su*CH*YmRU+3*Mmu-9$xr+>(~MVD|cE0s@N&a4>glPNMY=~(L;bzd>QgHM^<5036WQ8KD;=yI%A|7Kfn zA=AZv{bhGb6UTO=(8YR{zUzXfej=oIx*a!F)oQLeMNpJ2Gv=*qUl<9vK53i+%x7T^ zzPo3*MLKFBoDukj#ZYO0>$PD8dra?jK{K;9eb26pDfMEfZ`8~jW_@Cc0~QZa0)rmh zAxpEnmuMUmTiDEG671%PVB_$DO`W~Aeev}bG0)>K@7Fe;RJ6Rzk@+TP>}`!Td^sYJ ziLv1;F8jr!n<8y1KA)N(qojndi|$^F%w($lRGgU28z#uWNV9o(+pbZumTKwSfG6wG z*DvXtOS`j|pGr$NfBU?4u<+8;vqeh#>x%Ju@7CST=!J9F>YL9@^;JGSeBgPpX>aSJ zyoa^SInMazqs@vB*XCQU9DM%NasB$?=l4^WS+9S^*<~7fe`VhY8ALlvKA^F+@47YW z>dnzK>{FAAcZ<>{y3Q^4^X@$k$vENdHbuJifU|r^c>R5kqHl*A;~{Hr?b7d%HV;G? zaz$E4HQ&AK5U3sm{mT38+b1Qkgu|8Nz6ruz{+x;-IYwnW!Wc8BCP7HmIt4>d}YVT zxBUw!8N(!&whvzM?Fw$KnQ@RLz4)~9iISkJC&t=rU3`FwMg4;fHj>1#+sh&fnQsHk z_uY4P-TACy|<{ev%U0y*yWBN5AD8ysxAU2JYzGXcZ}X*4gI<`r?tz$|eoUi6y3{`z(P!EwN@ zfXc?Zj$@1$vFzsP<(3~S1V0vDPL>nUOAH7ILhR>i%*i?g8fXr(_y>kwg86+d0s#jW zApT<^LOP+1LV=Ay@q=*{S8%Ybbf2B~-qT}O+2jj$%vQU_k|7Ct5wH%&#l9QkRd6p) zniyAt3XabNT3E@goo!kEB*d5xtP^d87mU)39;B9M#pX;k+sZw6P8TJ0e-W}(7Jsf? z^yU4JXE#{n@vC@I9`}u1Eb?ctL{yVytlkq^)2Ag%6r-1g{OS4h)UR`;Mk=1_rc| zoA=fc(vq;=e-WOF?HO9d<7;JMiA^I<%V4)WD@hlv?m+uAi@TU=jPue<#au}(^*iJ> zD6R#dQsgn%~^jVog;-OYpm2}XH|)Op0zKBp5#xWpy>6h-*f~j zD*6{x>1lWPe&JbUq#)rGZ(7^az(3d3s*JBs0gJo#~as-}5;}U`f&lR?1_IVc}=`N3oUY zmG5!sfGv4GUT&6(z1!26VD`~4pS2F*zVywcnwuz1SulL2r-hYk+27@m5lfoRxjjZx znT!+r($mS+;ecemh=pLdyEZGA8hIXWqj^W12r*POK))mD&S7Oat}$IMp^vQ=C&vTh z!Z-WVunXN(wwy&-xSVTLyTzdt&fT`F54p&pOgcM@T@_D0nZ1GRJSvgxMzk@x@xWeF zE8#5CrErQ}qSIpT8XYw8X&lkjUv6oPNKUrgO;0Xr6=2KmGHZt@=$O8-dmu+>SeA_e zdpOks)*e?TyzJ7@EbwJ#dyq3`mdVdpy|4{Q_TD}j8ERJ{$?ev@bY}adbZBCR*wbjC zP=R#hl-<`VRwc%@8;f7FOZlp4xDFLkrK%Rs9^YL!*xx=p!jIv-#&L>t&?~UNX*mM% z-@<{x;D%t`5m78khnw6)ZLICFyKtXOh7EV2RQ_c5J zNuM=w3Ydkz!5+t=Qu#=!uo0C?#R?4+c?%7E0+swIF_Y<~Q|ED^!@dJ%ZT?D6Q3hRra?CtIbt-HGy`%n+=gvL& zTZ;aVms4B=eW`RRs+J9(v6($9qdI%5(#>CPqK}i}F=;eUnh%@aehFi#gs@dI*W%~k zxqGP&fdTqz6yO_y5Br=3u@EE4jgEBPSsE-#ap&O~m6B?rAA@%nYg={^m*ThS_QEeO z91D;IO<~Dg$Iv*kz&nL`=q&`lGYq^EcvXQ()hKqdU+~V+JQ7#p7_^?opoyDiCd94=4IYJSlLIO#t zSmG+XS03^-TK<#A;=3E|pi%{W&`L)q7v!t)14;LypXuah=)BJtnUw7hl+Er}m&& z2KTaShHfBf8g`eW5cpcLFWzqKQaSUB=r=N(-MQvVnL1CuN>7>Yc%5Ov?sy;nD8h{W z6b_>IrToT081gUtqqM$;N0;o0f4n#L@%Hc)eWUrN15Z~4{bK&6C`dOrs6U~8MZ<#f zQh47J%W`w6!a!kjv&<`zjnT1OGPQH*@7ZcCP{LK>%qm;N&^!E!MbAn`bNA%a!toQd zKjsp*2O*D^M1tFn7G=+@2Jq#Ew9;U=h1fp>Hi{V=-TO&r*Bg7_AS17+A4^{V8C*9a?N_b3e zMXj*Hx}cOpRvJjF-1H@tSF@(R6y?nIbwj8S*3?Na&6;=DdUte=P_t&we4D%p@?=-E zoXLSyB)pQXTNzjoq<1aWv?+|zx~)oG4__5 zk90H6q~?b%3%XfLpHa_@Lii_X_Cx1(gA*~KNq03Z41!d{U44!rG(|1);-4jWb zhJ}dbr`(eEp&|Hz^cgC?OU?y7^IzO5z8qobpDY}SLs*fNoR(oiOb!WS6_nmfWb+aG#&|7nrqMJYsc+^uW0l=N zZ%@BS*hbzWl5sef;-hq~E2U{w^CzU>{Y;-0eAe(f99S-J0|`Y!(}pxGyUEt$p^gFl4Kub zAN{9yL-$PT~ z2fULYs--xpUS60^m87#DvzjEhhATTX(lIy#+R9#A^*5lX zJ{T-L@6M(qEb>>^N^2kvn5b!)XmNVg`0kCy^Qd#rhUxB*H!?HKpF-LxQ6Bs+UiD68 zRoWS2VLDAg{6VbIG%q6doVHXvjCw~>o>S1C79WX<|2IpN5;eSWKm>}rIiVcEhs#pQ z>h_TOV+84t9l|fY=v$aNMyF!(Q4KH|&Nq3gjOA$AcO)U*&kQ~UZbD@#5ru^YNvdlEaS~%9%baUe1{G>$zx?MB{@QJ;ztH z>MCqD#USxA8P>C0cyM#5-YUKEhUSO;bCbhgkFZ(z9KN2y%3mX=@B3kYZfq4X@U=mm z?2uLV^mYe{$ba8GDg*&TAs`_*0tSMJ0k#7Ig~8ApM}NKyB8mQU-en022OEG9qDH+d zP%#8nwRfu^hYtscm^0c+&JG4Vm^9l&fv3=VN{j*l$oNluN(qvb*lZ*USG99F_BgO* zh8>`Mh+R+HX*d1WaTCyuMyk+>7okyaLnCUr=5ari;W*kJt|2@`DVK44gN^f-oKp0I z>bsm%QJ|OYNXkWo==i<&&n|b8zsF1#XW`bwy z26Oue$-IV!zN7q}xrbBN1U8JUolP|c2%p?(bdV*SGeEv5l|IKxB=O~xv7X|)VbH&@ zLZW(&?pbT&n_6!bDEQtg49syT zJS5vmSju3TVQVm$OuI@?n-+CNow+JYaMz0%(Sngh=|lW!m*As7uwJX;J&#Yj+4DgznkJ~Bf-(Zx1Vuh${C24%G6mO zxHP_5&E(|~M^0AcL0U+)-)QK+JYU~4d{6G>9!$#da-1$%mM+2Pc`Q)Ge5-Pp%;Vu! zVeDG9#>=+q``LDUEjS3GFGU8-{U2>M28}(Z4}0GBJLtJ3r0$Hak@e5rqq1S~w97Sr zRC^#sULACdd6D6&-6^6tE$V>$g|(5^t&hclkPKLapEYR>*erIWjn*hU{;azHg?*G( zNuOUdGl@^cH-*d8;H5-hS|{0+n=2l62rhmlk>pCQ-4UWsK~Zn0cjup{RV%>Losidv zVf-bG%;Oq}EH8elhI?}zu&<=L|qH-xZFGb zu4Oc%Ovfh$>ck|nH=@TT-?n^6D-7a!5=j$*?JylCqjOEKI6|DJh&q<+O%d z&4hv*pI9C-)^IeC$bO0Hyp*LMm;2P!t6*4Ug~wp}6c(KlR{V{NQi^FrPx&ccEwsY9 z#{ES`FQxtEQ_?eB9(6qP(ifNsGxeeQtt4jPF>!L4tZ8d>n8r5=5| zuFy}Il*Ki9;;Uh%_x(REO^@@1tVu)L9IBg<;8N)!vIOnA?uzws$y844sD^}X z`2wPMEE8OL@dC3M`ggR2R_N33zP%b4hauhdGInmXXMiq7`LR&Zs7tCiIhe{|H1$$x zVn_-Vo`R#wc{p)FZ-0QiXnQO&H?z6J%L;p-IxN4SOuvcT9`h}h#S4igxO!+DM*9M9 z%+6Q5l0(ar>Gl7eeFx_B{&*pTApT;aSt$WY3A{Yu_%w&|s9p%&eK_#A?dx$d{-jFY z8WxqbQq)-Pv-%8Kxf!J%XbO6Af3ovQ?4WG;>x$CB@L|>2UWv$ogy^~? zq42AUg3FLjsm{<3rJq<>_8$)RRpWPy%v>fH_%ab@&$iUX@WPs=$C_r@q|%qIZTNbtqwyA^S-_LFFxcQf3_%L6JkB}?)vncQ@IEV(b5Io3NZtVe%NrgwvKkodDuHPBn@G)joJA&XX;EF!dc3T26_fr0Sre6 zTkk(fUOU1R^a=DiMJ%U;mSTUQelQrq0EoiDSd@i@H&2<$6dVi@)SMD=!hkh}{|r4uf$0%kVEJH}uTHk$qt4KFNlA1I zFg-%mOrr_5TH+Hi9n)x;;F>MyST<+f<*Jf@8NZn|sNc0qTTMMIRG~6V@kEFSM~_O{ zBvXWjePa0YTZtks^}8YG37$|hHr-IexZ$#g&r$#b>8{FlH`+k;u|qkheVi{5M0s;Z zUC(dIS>P$l&;2ZInmMqLtWhw+tfl=Hv#Y;L?Vg3&{=Li$b;YFgcR^M1pKrD%Ny(@{ z`hqtQQSN!T+^Z}FT!Wp|5|uqzO%7SljowVsYCtr%KIdE?Z)l0lypo z8`$K&uEd>qV`Crseq~9tqCIj`FWI8Q>wVg@%LJoNM~t-nuWV_Of>j<=QIa}~w??++ zSc-R9Zc`!2S|e@mWC@bkI&nV;h>L0+$*C@|?(ShR+u0^JTnmgiMG(Knt3N`{zwyN| zM2#*uxPw}u9FD>K23!A>o==M|fzb9pMXVIBL52gYBJO7A#kKaZu3mz!h5HZm+-2-fM_XIzNs`1}s(~ZtJ znV1 z2_ZaXG^fRnfG_;d(TM6JJpi}&?i*1MDA;56!we8V9zG>4o}tFRgwjuuG&A%YhzK`) zyn))UNqs>nwtU$>ypyo6lyX!5rfN}>G28PGnb3+SqK-Jd`UUOF`OBC3LeF&+JRt7b zudS+B>1B>*F`?neRcwWJb-q2DHuS#I!Teg9b8EbRShm2VP>%_&KI6Rj=B?e_D?-5L z&F0LsTSUypQMA|OKHXD^4f;ZFXaXADWK#2riPB9B_6vETN`ky9=%>f}8O95tdi#P= zX-+PLD=7on)qwiEus2e5?`S$k?5PC*nz@;&?U)SXW=gjpbS=Q-GirOX=LRwSL*C)) za}r9peQZ)Ue%TA;&Ad!KWB(@#$)wL9J#5=#IUNjy>R+X(pT=`hF{kt{kdfwIzsPbw zCL8e*>rLJ{cN%=M#p({r`Vbm*I@hFK%wo;AA#2AKpB}eNO|dd>26vw#oYNvo(Z5(k z6Vg-j#+D;!92WQ-=I~&_HeDcM`FM(;A0Ll2S{7xtcrWHINbr2qN@A%~#{vuzx_}#T zO<>C1WLee_q-Lutn(Ox|;4{ngr3}Ksw9>ilZ%Q@^SL#LBiXr3sBn#sv>0er*(BXM!Nd40j4}=$uF4i(ayx$rM~*V4Tsaou zO?^-x5|6rR6k7z@@DXyqIIJb^Ip(%3dAf{qV)?^z>QGs4+|1 zg_|`gY?)j7$wh1{1~LJe!r2XTQ-dz>)wCKRpWwRMCXuqF)+mR_8*R=Rb(mRFm0%`C!|thm)egjd`|Vw3_b>>oam-_=z`rO z`3FFsN3n<#VSR4BqSbO-Ot+uxhO2Nt68)z(|Y5k)NY+wK;TYv+*iuOLX-p*bqPj3+9 z$MNHTCU4L<057I(?G79gmjAu$@9qC%Y6p!%%NphFd~$kN7z{?s82J0AWI-f?CQcVP z)C?RIJ~`q{B%;q6G;$!b;??EJD@9gM= z0*QDy0}T*Bb43t(_Gw!HjA8hwnI4q1OI+9{NJPF zC$jjtsP?a{f05RS^!^Xg08)3ecJu+5^H)NW^RY#r|3*Rq!q6Dd7eY|l)4S{b11-Hp)&Eo=Xi5di~&Lw`Mb{xc*u2O+@V|34DZ z|JQzCK+FdRBnJoHNX22|AOs9SgamA(m^c^&1rD;qfGttzBnr@pzK^aCLqNXgFfnxf z|F=Z+^F9oyi%#f1ekM^E1W*z*1H|D#0y+YeNep;5K>%tj3LqClCs8PHuwIl1NFanL z0wgL5bOOv(iUC?JCJqn)2FmCheIEuS;9S2L&>r0<5-{ip@$WEneli%C7!f*06M-HS zIw1gsL$?(L2L8Pdpe}&-qzt^lLdC>SlBgK+Bu4`75dpvv5GXLf?|9(=UQu8^7luT) zgM&nYTpVB)IzLHJbUnlg6VN#t|B2Ah3A{R8e?}U#ko@Ou|fV8lQ(Q8ZNmaYVK@a9^K1I46F+k_QvkUGdn|$cw{#-w@AuJl{z+)PKuZ;!|GNqE*ViXSU}*rbB>HIE z3o!AdqzwX0=1EBpbg~-{xN86cBzpog1Ob!VCnXyYAY`CK1Q=)N4`*f%0t9|?_q!c~ zp12sa(T4uObK(o2KTw^x2fz-V^zy@-oUADVD4hYN`QDW)+U=Zl>ISIy_mVpZ80QJh z1F#q9647^W0@#2PuqOx*%J&lBV}N0}Ki^BC{09oO zzXCQwowWPM=bc=k0CV;o{@u+1D{0^1fae2LFiP%~LlOt$9CkOq%s|f@C5vT|T z@{_8t|FbIKZ~yHP{(n{j3j;3tq(^a}$6p5aO91unXnxVYe}QQJ{T!k)MCjjuO32;W z!x!+Zzw-nwu%8XS`|I!Dgb~qjwnN7$z=MZo^2ujXd;$M@!j>P~l>sC8Z({}EQzz(X zj28%-yxcrdZq7C!KbSBC7?dE$35D|Vkq{Aa|L2~tr?(>?kq8h!+43)lR;?Hz~! ziwGkET-T2u;F&}L9yoyhfuUnJ^bh3m0|qSdApkD@215Ye><<{=wSI?*10LrO7-0Q= zhavyK3)J`nFBl9%KRD>$KlFhS2q0ej4F)g*cwBy~CklA#|Aj#i0K0d6#a1i27{wb=x;D_v|0XN7!-oG|G&YY;(y=;9D0*9T?o VjXE(g5FmO4KDNcBq@zsq{{UwFIxYYJ literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/timeInCircuitCollection.pdf b/thesis-evaluation/figures/timeInCircuitCollection.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9ad9b266999fa29d152b6d505432d17b33f6541e GIT binary patch literal 22681 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 BR3rcZ literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf b/thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4af6951c81c581568bc033e08aface67b850f5cd GIT binary patch literal 18996 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�s&|@=K7^A6#P4VV1U!#VGy|Z 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 QgkeHR5Eqx+6?xGA1MFV@e*gdg literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/totalCircuitCollections.pdf b/thesis-evaluation/figures/totalCircuitCollections.pdf new file mode 100644 index 0000000000000000000000000000000000000000..99d4f23350852482f5dfa2eedf1d3669ecea0018 GIT binary patch literal 17475 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 literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/totalSingleQubitDecompositions.pdf b/thesis-evaluation/figures/totalSingleQubitDecompositions.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a7a1a4ea33fe7242b4069b2efc32c74ab2dfc90c GIT binary patch literal 19814 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 literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/totalTouchedGates.pdf b/thesis-evaluation/figures/totalTouchedGates.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a3077d54667ab418ff29745b0a654c47033ce1ed GIT binary patch literal 18909 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 bfUflLv~>48Q6~@)5m7NhPEG|aMZ*6F9i7gS literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/totalTwoQubitDecompositions.pdf b/thesis-evaluation/figures/totalTwoQubitDecompositions.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5a16eb0a1509748e8e00fe5385738481931be925 GIT binary patch literal 19402 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 literal 0 HcmV?d00001 diff --git a/thesis-evaluation/figures/twoQubitCreationTime.pdf b/thesis-evaluation/figures/twoQubitCreationTime.pdf new file mode 100644 index 0000000000000000000000000000000000000000..342a4231413ee5a4ec0b721677dabc624b4947c3 GIT binary patch literal 22947 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 list[QuantumCircuit]: + subcircuits = [] + + # Start with an empty circuit that has the same registers + current = QuantumCircuit(*qc.qregs, *qc.cregs) + + for instr in qc.data: + if instr.name == "barrier" or instr.name == "measure": + # Finish current subcircuit if it has content + if current.data: + subcircuits.append(current) + # Start a fresh one + current = QuantumCircuit(*qc.qregs, *qc.cregs) + else: + current.append(instr.operation, instr.qubits, instr.clbits) + + # Append the last chunk if non-empty + if current.data: + subcircuits.append(current) + + return subcircuits + + +def circuit_complexity(qc: QuantumCircuit) -> int: + num_one_qubit_gates = qc.size( + lambda instr: len(instr.qubits) == 1 and instr.name != "barrier" + ) + num_two_qubit_gates = qc.size( + lambda instr: len(instr.qubits) == 2 and instr.name != "barrier" + ) + num_other_gates = qc.size( + lambda instr: len(instr.qubits) != 1 and len(instr.qubits) != 2 + ) + + return num_one_qubit_gates + 10 * num_two_qubit_gates + + +def evaluate(): + stats = {} + + otherCX = QuantumCircuit(2) + otherCX.cx(1, 0) + otherCXGate = otherCX.to_gate() + oneQubitDec = OneQubitEulerDecomposer("ZYZ") + start_time = time.perf_counter_ns() + dec = TwoQubitBasisDecomposer(CXGate(), euler_basis="ZYZ") + dec2 = TwoQubitBasisDecomposer(otherCXGate, euler_basis="ZYZ") + end_time = time.perf_counter_ns() + creation_time_us = (end_time - start_time) / 1000 + print(f"Python Basis Decomposition Creation: {creation_time_us}µs") + + print(f"Processing '{MQT_BENCH_DIR}/{MQT_BENCH_PATTERN}'...") + for file in glob.glob(f"{MQT_BENCH_DIR}/{MQT_BENCH_PATTERN}"): + name = os.path.basename(file).removesuffix(".qasm") + print(f"Decomposing {name} ({file})") + with open(file, "r") as f: + content = f.read() + try: + if is_qasm3: + content = "\n".join( + filter(lambda line: not "measure " in line, content.splitlines()) + ) + qc: QuantumCircuit = qasm3.loads(content) + else: + qc: QuantumCircuit = qasm2.loads(content) + except Exception as e: + print(f"FAILED: {name} ({e})") + continue + + # respect barriers + start_time = time.perf_counter_ns() + subcircuits = split_by_barriers(qc) + end_time = time.perf_counter_ns() + collection_time_us = (end_time - start_time) / 1000 + + decomposition_times_1q = [] + decomposition_times_2q = [] + complexity_changes = [] + num_two_qubit_decompositions = 0 + num_single_qubit_decompositions = 0 + for subcircuit in subcircuits: + m = Operator(subcircuit) + if len(qc.qubits) == 1: + start_time = time.perf_counter_ns() + decomposed_circuit = oneQubitDec(m) + decomposed_circuit2 = None + end_time = time.perf_counter_ns() + decomposition_time = (end_time - start_time) / 1000 + decomposition_times_1q.append(decomposition_time) + + num_single_qubit_decompositions += 1 + elif len(qc.qubits) == 2: + start_time = time.perf_counter_ns() + decomposed_circuit = dec(m) + decomposed_circuit2 = dec2(m) + end_time = time.perf_counter_ns() + decomposition_time = (end_time - start_time) / 1000 + decomposition_times_2q.append(decomposition_time) + + num_two_qubit_decompositions += 1 + else: + raise RuntimeError("Invalid circuit size!") + + before_complexity = circuit_complexity(subcircuit) + after_complexity = circuit_complexity(decomposed_circuit) + + if decomposed_circuit2: + after_complexity2 = circuit_complexity(decomposed_circuit2) + if after_complexity2 < after_complexity: + print( + f"Choose alternative decomposition ({after_complexity2} vs {after_complexity})!" + ) + decomposed_circuit = decomposed_circuit2 + after_complexity = after_complexity2 + + print(subcircuit) + print(f"{before_complexity} -> {after_complexity}") + print(decomposed_circuit) + + print(Operator(subcircuit).data) + print("vs") + print(Operator(decomposed_circuit).data) + + complexity_changes.append(before_complexity - after_complexity) + + stats[name] = { + "timeInSingleQubitDecomposition": sum(decomposition_times_1q), + "timeInTwoQubitDecomposition": sum(decomposition_times_2q), + "subCircuitComplexityChange": sum(complexity_changes), + "totalTwoQubitDecompositions": num_two_qubit_decompositions, + "totalSingleQubitDecompositions": num_single_qubit_decompositions, + "twoQubitCreationTime": creation_time_us, + "timeInCircuitCollection": collection_time_us, + } + + print() + print(stats) + print(f"Total benchmarks: {len(stats)}") + return stats + + +if __name__ == "__main__": + evaluate() diff --git a/thesis-evaluation/run.sh b/thesis-evaluation/run.sh new file mode 100755 index 0000000000..43f070b123 --- /dev/null +++ b/thesis-evaluation/run.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +OUTPUT_DIR="tmp-output" + +MQT_CORE_ROOT_DIR=.. +MQT_BENCH_BENCHMARK_DIR="../../mqt-bench/generated_benchmarks/v3_qasm3" +MQT_BENCH_PATTERN="*.qasm" + +mkdir -p "${OUTPUT_DIR}/broken" + +benchmark_count="$(eza -1 ${MQT_BENCH_BENCHMARK_DIR}/${MQT_BENCH_PATTERN} | wc -l)" +i=0 +success=0 + +for benchmark_path in ${MQT_BENCH_BENCHMARK_DIR}/${MQT_BENCH_PATTERN}; do + i=$((i + 1)) + benchmark="$(basename "${benchmark_path}")" + echo "${i}/${benchmark_count}: ${benchmark}" + result="$("${MQT_CORE_ROOT_DIR}/build/mlir/tools/mqt-cc/mqt-cc" --mlir-timing --mlir-statistics "${benchmark_path}" 2>&1)" + if [ "${?}" -eq 0 ]; then + echo "${result}" | rg 'Total Execution Time' -A 8 >"${OUTPUT_DIR}/${benchmark}.timing" + echo "${result}" | rg '\(S\) ' >"${OUTPUT_DIR}/${benchmark}.statistic" + echo "${result}" | rg 'module \{' -A 99999999999999 >"${OUTPUT_DIR}/${benchmark}.mlir" + echo "${result}" >"${OUTPUT_DIR}/${benchmark}.all" + echo "SUCCESS" + success=$((success + 1)) + else + echo "${result}" >"${OUTPUT_DIR}/broken/${benchmark}.error" + echo "FAILED" + fi +done + +echo "COMPLETED: ${success}/${i}" diff --git a/thesis-evaluation/total_evaluate.py b/thesis-evaluation/total_evaluate.py new file mode 100644 index 0000000000..4b9de3dbcf --- /dev/null +++ b/thesis-evaluation/total_evaluate.py @@ -0,0 +1,274 @@ +import evaluate +import qiskit_run + +import matplotlib.pyplot as plt +from matplotlib.ticker import MaxNLocator +import math +import json +import os +import numpy as np +import subprocess + +OUT_DIR = "./figures" +if not os.path.exists(OUT_DIR): + os.makedirs(OUT_DIR, exist_ok=True) + +CACHE_FILE = "./evaluate_cache.json" +if os.path.exists(CACHE_FILE): + with open(CACHE_FILE, "r") as f: + cache = json.load(f) + mqt_results = cache["mqt"] + qiskit_results = cache["qiskit"] +else: + ITERATIONS = 20 + all_mqt_results = [] + all_qiskit_results = [] + for _ in range(ITERATIONS): + # evaluate.evalue() will only process statistics files; need to re-run mqt-cc using run.sh + subprocess.run(["./run.sh"]).check_returncode() + all_mqt_results.append(evaluate.evaluate()) + all_qiskit_results.append(qiskit_run.evaluate()) + + def average_results( + all_results: list[dict[str, dict[str, int | float]]], + ) -> dict[str, dict[str, int | float]]: + result = {} + for r in all_results: + for benchmark_name, measurements in r.items(): + if not benchmark_name in result: + result[benchmark_name] = {} + for metric_name, value in measurements.items(): + if not metric_name in result[benchmark_name]: + result[benchmark_name][metric_name] = 0.0 + result[benchmark_name][metric_name] += value / ITERATIONS + return result + + # [benchmark_name -> [metric_name -> value]] + mqt_results = average_results(all_mqt_results) + qiskit_results = average_results(all_qiskit_results) + + with open(CACHE_FILE, "w") as f: + cache = { + "mqt": mqt_results, + "qiskit": qiskit_results, + } + json.dump(cache, f) + +print("In MQT, but not Qiskit: ", mqt_results.keys() - qiskit_results.keys()) +print("In Qiskit, but not MQT: ", qiskit_results.keys() - mqt_results.keys()) + + +x: dict[str, list[str]] = {} +y1: dict[str, list[int | float]] = {} +y2: dict[str, list[int | float]] = {} + + +def define_division_metric( + new_metric: str, old_metric: str, divisor_metric: str, benchmark_name: str +): + y1[new_metric] = y1.get(new_metric, []) + [ + m[old_metric] / m[divisor_metric] if m[divisor_metric] > 0 else float("nan") + ] + y2[new_metric] = y2.get(new_metric, []) + [ + q[old_metric] / q[divisor_metric] if q[divisor_metric] > 0 else float("nan") + ] + x[new_metric] = x.get(new_metric, []) + [benchmark_name] + + +aliases_qiskit = { + "totalSingleQubitDecompositions": "successfulSingleQubitDecompositions", + "totalTwoQubitDecompositions": "successfulTwoQubitDecompositions", +} +aliases_mqt = { + "timeInCircuitCollection": "timeInCircuitCollectionStandalone", +} +names = sorted(mqt_results.keys() & qiskit_results.keys()) +for name in names: + m = mqt_results[name] + q = qiskit_results[name] + + for metric in m.keys() | q.keys(): + if metric in m: + y1[metric] = y1.get(metric, []) + [m[metric]] + if metric in aliases_mqt: + y1[aliases_mqt[metric]] = y1.get(aliases_mqt[metric], []) + [m[metric]] + if metric in q: + y2[metric] = y2.get(metric, []) + [q[metric]] + if metric in aliases_qiskit: + y2[aliases_qiskit[metric]] = y2.get(aliases_qiskit[metric], []) + [q[metric]] + + define_division_metric( + "timePerSingleQubitDecomposition", + "timeInSingleQubitDecomposition", + "totalSingleQubitDecompositions", + name, + ) + define_division_metric( + "timePerTwoQubitDecomposition", + "timeInTwoQubitDecomposition", + "totalTwoQubitDecompositions", + name, + ) + + for metric in y1.keys() | y2.keys(): + x[metric] = names + +titles = { + "subCircuitComplexityChange": "Complexity Improvement after Decomposition", + "successfulSingleQubitDecompositions": "Number of Successful Single-Qubit Decompositions", + "successfulTwoQubitDecompositions": "Number of Successful Two-Qubit Decompositions", + "timeInCircuitCollection": "Sub-Circuit Collection Time [µs]", + "timeInCircuitCollectionStandalone": "Sub-Circuit Collection Time [µs]", + "timeInSingleQubitDecomposition": "Total Time for Single-Qubit Decompositions [µs]", + "timeInTwoQubitDecomposition": "Total Time for Two-Qubit Decompositions [µs]", + "totalCircuitCollections": "Number of Sub-Circuit Collections", + "totalSingleQubitDecompositions": "Total Number of Single-Qubit Decompositions", + "totalTouchedGates": "Total Number of Gates in Collected Sub-Circuits", + "totalTwoQubitDecompositions": "Total Number of Two-Qubit Decompositions", + "timePerTwoQubitDecomposition": "Time / Two-Qubit Decomposition [µs]", + "timePerSingleQubitDecomposition": "Time / Single-Qubit Decomposition [µs]", + "twoQubitCreationTime": "Time for Creation of Two-Qubit Basis Decomposers [µs]", +} +legend_positions = { + "totalTouchedGates": "upper left", + "timeInCircuitCollection": "upper right", + "timeInCircuitCollectionStandalone": "upper left", + "timePerSingleQubitDecomposition": "upper left", + "timePerTwoQubitDecomposition": "lower right", + "totalTouchedGates": "upper left", + "twoQubitCreationTime": "lower right", +} +pruneFunctions = { + "timeInCircuitCollection": lambda value: np.isclose(value, 0), + "timeInCircuitCollectionStandalone": lambda value: np.isclose(value, 0), + "timeInSingleQubitDecomposition": lambda value: np.isclose(value, 0), + "timeInTwoQubitDecomposition": lambda value: np.isclose(value, 0), + "timePerTwoQubitDecomposition": lambda value: np.isclose(value, 0), + "timePerSingleQubitDecomposition": lambda value: np.isclose(value, 0), + "twoQubitCreationTime": lambda value: np.isclose(value, 0), +} +# modifications = { +# "subCircuitComplexityChange": lambda value: -1.0 * value, +# } +for metric in x.keys(): + plt.title(titles.get(metric, metric)) + # x_values = x[metric] # use for benchmark names on x axis + x_values = [str(i) for i in range(len(x[metric]))] + + # if metric in modifications: + # for y in y1[metric]: + # y = modifications[metric](y) + # for y in y2[metric]: + # y = modifications[metric](y) + + DEFAULT_POINT_SIZE = 100 + scale1 = [] + scale2 = [] + num_erased_y = 0 + for i in range(len(y1[metric])): + if metric in y1: + y1i = y1[metric][i - num_erased_y] + else: + y1i = None + if metric in y2: + y2i = y2[metric][i - num_erased_y] + else: + y2i = None + if ( + (y1i == None and y2i == None) + or (y1i != None and y2i != None and math.isnan(y1i) and math.isnan(y2i)) + or ( + y1i != None + and y2i != None + and pruneFunctions.get(metric, lambda _: False)(y1i) + and pruneFunctions.get(metric, lambda _: False)(y2i) + ) + ): + x_values.pop(i - num_erased_y) + if metric in y1: + y1[metric].pop(i - num_erased_y) + if metric in y2: + y2[metric].pop(i - num_erased_y) + num_erased_y += 1 + elif ( + y1i == None + or math.isnan(y1i) + or pruneFunctions.get(metric, lambda _: False)(y1i) + ): + scale1.append(0) + scale2.append(DEFAULT_POINT_SIZE) + elif ( + y2i == None + or math.isnan(y2i) + or pruneFunctions.get(metric, lambda _: False)(y2i) + ): + scale1.append(DEFAULT_POINT_SIZE) + scale2.append(0) + else: + scale1.append(DEFAULT_POINT_SIZE) + scale2.append(DEFAULT_POINT_SIZE) + + ymin = float("inf") + if metric in y1: + mqt_scatter = plt.scatter( + x_values, y1[metric], color="blue", s=scale1, alpha=0.4 + ) + mqt_scatter.set_label("MQT") + ymin = min(ymin, min(y1[metric])) + if metric in y2: + qiskit_scatter = plt.scatter( + x_values, y2[metric], color="red", s=scale2, alpha=0.4 + ) + qiskit_scatter.set_label("Qiskit") + ymin = min(ymin, min(y2[metric])) + + # plt.xticks(x_values) # does not work for strings + if ymin > 0: + # let matplotlib handle non-positive values automatically + plt.ylim(bottom=0) + + yint = [] + locs, labels = plt.yticks() + for each in locs: + yint.append(int(each)) + plt.yticks(yint) + + leg = plt.legend(loc=legend_positions.get(metric, "best")) + for handle in leg.legend_handles: + handle.set_sizes([DEFAULT_POINT_SIZE]) + plt.savefig( + f"{OUT_DIR}/{metric}.pdf", format="pdf", bbox_inches="tight", pad_inches=0 + ) + # plt.show() + plt.clf() + +for i, name in enumerate(names): + if "_indep_1_none_O0" in name: + print( + i, " & \\code{", name.removesuffix("_indep_1_none_O0"), "} & 1\\\\", sep="" + ) + elif "_indep_2_none_O0" in name: + print( + i, " & \\code{", name.removesuffix("_indep_2_none_O0"), "} & 2\\\\", sep="" + ) + else: + print("UNKNOWN") + +for metric in x.keys(): + print() + print( + f"Average MQT {metric}:", + np.average(np.ma.masked_invalid(y1[metric])) if metric in y1 else "-", + ) + print( + f"Average Qiskit {metric}:", + np.average(np.ma.masked_invalid(y2[metric])) if metric in y2 else "-", + ) + print( + f"Median MQT {metric}:", + np.median(np.ma.masked_invalid(y1[metric])) if metric in y1 else "-", + ) + print( + f"Median Qiskit {metric}:", + np.median(np.ma.masked_invalid(y2[metric])) if metric in y2 else "-", + ) From 8f58cb401706e3f457de7e3f148c8c33b7bf3063 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Fri, 13 Feb 2026 20:36:33 +0100 Subject: [PATCH 225/237] evaluation script update: automated Qiskit Rust eval --- thesis-evaluation/evaluate_cache.json | 1 - thesis-evaluation/evaluate_cache_mqt.json | 1 + .../evaluate_cache_qiskit-rust.json | 1 + thesis-evaluation/evaluate_cache_qiskit.json | 1 + .../figures/numberOfTwoQubitCreations.pdf | Bin 16543 -> 16590 bytes .../figures/subCircuitComplexityChange.pdf | Bin 21030 -> 20621 bytes .../successfulSingleQubitDecompositions.pdf | Bin 18729 -> 18978 bytes .../successfulTwoQubitDecompositions.pdf | Bin 19324 -> 18650 bytes .../figures/timeInCircuitCollection.pdf | Bin 22681 -> 22554 bytes .../timeInCircuitCollectionStandalone.pdf | Bin 18602 -> 18503 bytes .../timeInSingleQubitDecomposition.pdf | Bin 18996 -> 19513 bytes .../figures/timeInTwoQubitDecomposition.pdf | Bin 19323 -> 18709 bytes .../timePerSingleQubitDecomposition.pdf | Bin 17857 -> 18055 bytes .../figures/timePerTwoQubitDecomposition.pdf | Bin 18236 -> 17836 bytes .../figures/totalCircuitCollections.pdf | Bin 17475 -> 17485 bytes .../totalSingleQubitDecompositions.pdf | Bin 19814 -> 19783 bytes .../figures/totalTouchedGates.pdf | Bin 18909 -> 18954 bytes .../figures/totalTwoQubitDecompositions.pdf | Bin 19402 -> 19059 bytes .../figures/twoQubitCreationTime.pdf | Bin 22947 -> 23367 bytes thesis-evaluation/qiskit_run.py | 169 +++++++++++++----- thesis-evaluation/run.sh | 4 +- thesis-evaluation/total_evaluate.py | 100 +++++++---- 22 files changed, 199 insertions(+), 78 deletions(-) delete mode 100644 thesis-evaluation/evaluate_cache.json create mode 100644 thesis-evaluation/evaluate_cache_mqt.json create mode 100644 thesis-evaluation/evaluate_cache_qiskit-rust.json create mode 100644 thesis-evaluation/evaluate_cache_qiskit.json diff --git a/thesis-evaluation/evaluate_cache.json b/thesis-evaluation/evaluate_cache.json deleted file mode 100644 index 92f5381da9..0000000000 --- a/thesis-evaluation/evaluate_cache.json +++ /dev/null @@ -1 +0,0 @@ -{"mqt": {"modular_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 14.95, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 815.4999999999998}, "bmw_quark_cardinality_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 39.49999999999999, "timeInSingleQubitDecomposition": 8.250000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 13.999999999999995, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 11.999999999999996, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 839.1}, "draper_qft_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 22.699999999999992, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 755.4999999999999, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 822.0499999999998}, "vqe_two_local_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 291.70000000000005, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12806.850000000002, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 803.05}, "wstate_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -8.000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 29.499999999999996, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1395.1999999999998, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 782.05}, "bmw_quark_copula_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 149.25, "timeInSingleQubitDecomposition": 48.80000000000001, "timeInTwoQubitDecomposition": 829.85, "totalCircuitCollections": 32.999999999999986, "totalSingleQubitDecompositions": 8.000000000000002, "totalTouchedGates": 50.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 815.4499999999999}, "qaoa_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 11.600000000000001, "timeInSingleQubitDecomposition": 7.299999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 807.8500000000001}, "qftentangled_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -13.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 33.99999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1859.0000000000007, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 815.0}, "wstate_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.750000000000001, "timeInSingleQubitDecomposition": 6.049999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 795.2000000000002}, "ghz_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 22.699999999999992, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 831.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 800.55}, "qpeinexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -16.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 23.249999999999993, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1359.8000000000002, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 815.7}, "vqe_real_amp_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 14.799999999999999, "timeInSingleQubitDecomposition": 6.1999999999999975, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 807.55}, "vqe_su2_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 5.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 21.3, "timeInSingleQubitDecomposition": 7.95, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 823.3499999999999}, "qft_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 9.399999999999999, "timeInSingleQubitDecomposition": 6.749999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 811.0999999999999}, "bmw_quark_cardinality_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -55.99999999999998, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 2.999999999999999, "timeInCircuitCollection": 153.75, "timeInSingleQubitDecomposition": 27.250000000000007, "timeInTwoQubitDecomposition": 4109.2, "totalCircuitCollections": 47.999999999999986, "totalSingleQubitDecompositions": 4.000000000000001, "totalTouchedGates": 50.999999999999986, "totalTwoQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 796.4499999999999}, "bv_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 5.749999999999999, "timeInSingleQubitDecomposition": 6.049999999999998, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 806.4499999999999}, "ghz_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.5, "timeInSingleQubitDecomposition": 6.399999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 806.6500000000002}, "vqe_su2_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 626.4999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 22723.0, "totalCircuitCollections": 20.0, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 198.00000000000006, "totalTwoQubitDecompositions": 15.0, "twoQubitCreationTime": 825.6999999999999}, "qnn_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 39.550000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1033.7, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 13.000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 802.3499999999999}, "qpeexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 23.449999999999992, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 809.9, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 808.5000000000002}, "qnn_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 17.249999999999996, "timeInSingleQubitDecomposition": 7.800000000000001, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 786.6}, "vqe_real_amp_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 285.54999999999995, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12790.199999999999, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 806.9499999999999}, "qftentangled_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 10.099999999999998, "timeInSingleQubitDecomposition": 3.099999999999999, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 812.8000000000002}, "bv_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 2.0000000000000004, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 12.65, "timeInSingleQubitDecomposition": 8.500000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 2.0000000000000004, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 793.25}, "randomcircuit_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 10.4, "timeInSingleQubitDecomposition": 7.6499999999999995, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 794.25}, "qft_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -23.999999999999993, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 24.449999999999996, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1888.9499999999998, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 793.1999999999999}, "qaoa_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -6.999999999999997, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 27.500000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1481.3, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 801.7500000000001}}, "qiskit": {"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 220.62315, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 530.834}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1449.0686999999998, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 664.06875}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1400.6623, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 753.1374999999999}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1403.9076999999997, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 302.8408}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 7828.1339, "subCircuitComplexityChange": -75.0, "totalTwoQubitDecompositions": 5.999999999999998, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 2283.7117499999995}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 3831.9234500000002, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1648.71505}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1465.3955999999998, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1092.5579}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 187.1023, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 393.38485}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1550.9868500000002, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1698.9834500000002}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1430.4808500000001, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 552.24995}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1362.3799499999998, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 570.4323499999999}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1306.5448999999999, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 385.82995000000005}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1374.2908000000002, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 516.0969000000001}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1427.7414, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1202.9632000000001}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 180.8468, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 312.29975}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1457.9090999999999, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1094.43475}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 180.33175, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 282.276}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 169.9849, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 817.0377500000002}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1306.34635, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 336.2191000000001}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1333.5626999999997, "subCircuitComplexityChange": 15.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 712.1571000000001}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1426.60265, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 601.4511999999999}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1457.3285500000004, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 622.4298}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 175.84595, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 653.8033499999999}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 504.0352500000001, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 1364.66555}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 165.24959999999996, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 163.43635000000003}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 165.68475, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 284.16045}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 154.48034999999996, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 365.45809999999994}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1490.2478999999998, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 576.6129999999999}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1428.98125, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 606.5438499999999}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 173.56869999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 284.78909999999996}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 169.89490000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 372.2458499999999}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 1383.5119000000002, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 1897.7755000000002, "timeInCircuitCollection": 552.30705}}} \ No newline at end of file diff --git a/thesis-evaluation/evaluate_cache_mqt.json b/thesis-evaluation/evaluate_cache_mqt.json new file mode 100644 index 0000000000..adf782602c --- /dev/null +++ b/thesis-evaluation/evaluate_cache_mqt.json @@ -0,0 +1 @@ +{"modular_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.3000000000000003, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 44.7}, "bmw_quark_cardinality_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.4000000000000004, "timeInSingleQubitDecomposition": 1.3000000000000005, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 13.999999999999995, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 11.999999999999996, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 44.7}, "draper_qft_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.0500000000000003, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12.35, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 43.05}, "vqe_two_local_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.299999999999997, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 194.34999999999997, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 38.2}, "wstate_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -8.000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.3000000000000003, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 23.300000000000004, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 39.95}, "bmw_quark_copula_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.2, "timeInSingleQubitDecomposition": 2.3500000000000005, "timeInTwoQubitDecomposition": 15.149999999999999, "totalCircuitCollections": 32.999999999999986, "totalSingleQubitDecompositions": 8.000000000000002, "totalTouchedGates": 50.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 39.599999999999994}, "qaoa_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.4000000000000006, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 39.3}, "qftentangled_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -13.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.55, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 31.150000000000002, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 38.3}, "wstate_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.3, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 34.900000000000006}, "ghz_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 1.2000000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 14.549999999999995, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 35.15}, "qpeinexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -16.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.0500000000000007, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 23.099999999999998, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.900000000000006}, "vqe_real_amp_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.1500000000000006, "timeInSingleQubitDecomposition": 1.1000000000000003, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 43.300000000000004}, "vqe_su2_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 5.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.4000000000000006, "timeInSingleQubitDecomposition": 1.3000000000000005, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 35.6}, "qft_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.39999999999999997, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 36.300000000000004}, "bmw_quark_cardinality_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -55.99999999999998, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 2.999999999999999, "timeInCircuitCollection": 1.0000000000000002, "timeInSingleQubitDecomposition": 2.45, "timeInTwoQubitDecomposition": 65.5, "totalCircuitCollections": 47.999999999999986, "totalSingleQubitDecompositions": 4.000000000000001, "totalTouchedGates": 50.999999999999986, "totalTwoQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 37.5}, "bv_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.3, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 35.7}, "ghz_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.2, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 36.45}, "vqe_su2_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 17.500000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 325.15000000000003, "totalCircuitCollections": 20.0, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 198.00000000000006, "totalTwoQubitDecompositions": 15.0, "twoQubitCreationTime": 37.050000000000004}, "qnn_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 3.049999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 19.449999999999992, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 13.000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 39.6}, "qpeexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.0000000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 13.850000000000001, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.10000000000001}, "qnn_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.7000000000000004, "timeInSingleQubitDecomposition": 1.3000000000000005, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 39.0}, "vqe_real_amp_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 8.049999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 201.09999999999994, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 39.65}, "qftentangled_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.9500000000000003, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 36.099999999999994}, "bv_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 2.0000000000000004, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.6, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 2.0000000000000004, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 37.79999999999999}, "randomcircuit_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.0500000000000003, "timeInSingleQubitDecomposition": 1.1500000000000004, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 36.55}, "qft_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -23.999999999999993, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.0500000000000003, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 31.650000000000002, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.050000000000004}, "qaoa_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -6.999999999999997, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.2000000000000006, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 26.5, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.10000000000001}} \ No newline at end of file diff --git a/thesis-evaluation/evaluate_cache_qiskit-rust.json b/thesis-evaluation/evaluate_cache_qiskit-rust.json new file mode 100644 index 0000000000..b4092f9bcc --- /dev/null +++ b/thesis-evaluation/evaluate_cache_qiskit-rust.json @@ -0,0 +1 @@ +{"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.6693000000000002, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 68.24999999999999, "timeInCircuitCollection": 59.871500000000005}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 47.89999999999999, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.25000000000001, "timeInCircuitCollection": 80.76885}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 46.05, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.2, "timeInCircuitCollection": 100.9747}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.95, "timeInCircuitCollection": 39.55315}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 8.898950000000001, "timeInTwoQubitDecomposition": 140.64999999999998, "subCircuitComplexityChange": -65.99999999999997, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 36.65, "timeInCircuitCollection": 242.69385000000003}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 5.838399999999999, "timeInTwoQubitDecomposition": 35.8, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 2.0000000000000004, "twoQubitCreationTime": 36.949999999999996, "timeInCircuitCollection": 167.0617}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 57.40000000000001, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.650000000000006, "timeInCircuitCollection": 116.8651}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.4743500000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.8, "timeInCircuitCollection": 53.9262}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 59.50000000000001, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.949999999999996, "timeInCircuitCollection": 167.32000000000002}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 56.3, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.400000000000006, "timeInCircuitCollection": 67.90270000000001}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 36.5, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.199999999999996, "timeInCircuitCollection": 66.17474999999999}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 36.25000000000001, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.3, "timeInCircuitCollection": 48.367650000000005}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 37.6, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.849999999999994, "timeInCircuitCollection": 61.388749999999995}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 39.5, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.74999999999999, "timeInCircuitCollection": 123.67175}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.9454000000000002, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.35, "timeInCircuitCollection": 51.8517}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 57.55, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.29999999999999, "timeInCircuitCollection": 115.60185}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.1652500000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.14999999999999, "timeInCircuitCollection": 35.40555}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.8032999999999997, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.1, "timeInCircuitCollection": 83.2311}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 2.1692500000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.0, "timeInCircuitCollection": 48.4669}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 45.699999999999996, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.949999999999996, "timeInCircuitCollection": 82.72365}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 47.4, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.49999999999999, "timeInCircuitCollection": 70.39055}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 47.3, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.300000000000004, "timeInCircuitCollection": 74.90429999999998}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 3.2137000000000002, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.29999999999999, "timeInCircuitCollection": 71.526}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.9353, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.849999999999994, "timeInCircuitCollection": 114.38800000000002}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.1753500000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.3, "timeInCircuitCollection": 33.42935}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.56235, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.6, "timeInCircuitCollection": 34.2841}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 1.8582999999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.14999999999999, "timeInCircuitCollection": 42.88195}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 47.15, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.449999999999996, "timeInCircuitCollection": 65.00000000000001}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 36.55000000000001, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.7, "timeInCircuitCollection": 71.21195}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.5497000000000005, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.1, "timeInCircuitCollection": 34.8426}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 3.0153500000000006, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.550000000000004, "timeInCircuitCollection": 48.33565}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 36.4, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.75000000000001, "timeInCircuitCollection": 57.89245000000001}} \ No newline at end of file diff --git a/thesis-evaluation/evaluate_cache_qiskit.json b/thesis-evaluation/evaluate_cache_qiskit.json new file mode 100644 index 0000000000..67e67ca2c5 --- /dev/null +++ b/thesis-evaluation/evaluate_cache_qiskit.json @@ -0,0 +1 @@ +{"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 296.69720000000007, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 220.44915000000003, "timeInCircuitCollection": 58.09570000000001}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 157.23375000000001, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 180.81419999999997, "timeInCircuitCollection": 78.45695000000002}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 154.60445, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 179.16495, "timeInCircuitCollection": 95.56074999999998}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 175.47254999999998, "timeInCircuitCollection": 37.9996}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 872.1498999999999, "timeInTwoQubitDecomposition": 461.6757, "subCircuitComplexityChange": -65.99999999999997, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 177.9274, "timeInCircuitCollection": 236.82060000000004}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 575.1606499999999, "timeInTwoQubitDecomposition": 131.47050000000002, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 2.0000000000000004, "twoQubitCreationTime": 175.05319999999998, "timeInCircuitCollection": 161.2176}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 176.98079999999996, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.42295000000001, "timeInCircuitCollection": 114.41595}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 289.4362499999999, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 177.24789999999996, "timeInCircuitCollection": 51.5821}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 180.56085, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 177.94589999999994, "timeInCircuitCollection": 164.5999}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 175.01370000000003, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 176.63645000000002, "timeInCircuitCollection": 65.38274999999999}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 131.6936, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 175.73405, "timeInCircuitCollection": 63.1769}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 131.3157, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 177.59835, "timeInCircuitCollection": 47.40789999999999}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 135.70365, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 175.7378, "timeInCircuitCollection": 59.752750000000006}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 135.4567, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.08245, "timeInCircuitCollection": 122.72814999999999}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 291.7512, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 176.22564999999997, "timeInCircuitCollection": 51.3624}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 179.04050000000004, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 185.18149999999997, "timeInCircuitCollection": 113.37980000000003}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 286.04645000000005, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 175.8176, "timeInCircuitCollection": 33.39065}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 292.03305000000006, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 178.59234999999998, "timeInCircuitCollection": 80.36815}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 288.15489999999994, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 176.277, "timeInCircuitCollection": 46.53595}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 152.7687, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.27049999999997, "timeInCircuitCollection": 81.1797}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 157.21085, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 176.39600000000002, "timeInCircuitCollection": 66.92224999999999}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 155.94840000000002, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 177.81455, "timeInCircuitCollection": 72.6108}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 294.88100000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 176.763, "timeInCircuitCollection": 69.81880000000002}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 292.5032999999999, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 179.21890000000002, "timeInCircuitCollection": 111.38385}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 285.76345, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 179.039, "timeInCircuitCollection": 32.29745}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 285.62745, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 177.08870000000002, "timeInCircuitCollection": 32.9989}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 281.29725, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 178.22199999999998, "timeInCircuitCollection": 41.21045}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 154.96949999999998, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.22134999999997, "timeInCircuitCollection": 63.23895000000001}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 133.15385, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 177.6715, "timeInCircuitCollection": 69.82260000000001}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 289.8093, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 180.41359999999997, "timeInCircuitCollection": 34.006150000000005}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 291.50255000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 177.928, "timeInCircuitCollection": 46.6455}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 133.66164999999998, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.29955, "timeInCircuitCollection": 56.619299999999996}} \ No newline at end of file diff --git a/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf b/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf index a470700fd83bb9600297142eafed75bee885af0d..e385ab3f2e233d6917f64b5bde451a5aae588cf8 100644 GIT binary patch delta 5756 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 delta 5709 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 JoVq;u{{!_b%dG$a delta 9017 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 C#5ack delta 6586 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 delta 7782 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 diff --git a/thesis-evaluation/figures/timeInCircuitCollection.pdf b/thesis-evaluation/figures/timeInCircuitCollection.pdf index 9ad9b266999fa29d152b6d505432d17b33f6541e..aa8274211de262e2fb83446ab51246166f980ff6 100644 GIT binary patch delta 10796 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 RN}zJ`fKgu#sfrS){|5*A{=)zO diff --git a/thesis-evaluation/figures/timeInCircuitCollectionStandalone.pdf b/thesis-evaluation/figures/timeInCircuitCollectionStandalone.pdf index 50a75e96473b123f7f52412aade41cad4f7fd549..0ac73d450b31e2674ff06435be0939230c92b7c3 100644 GIT binary patch delta 6964 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 HW$OO}1}m}I delta 6959 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 F{|BfI!QcP@ diff --git a/thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf b/thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf index 0911ef9558fe9b9aba314c8da551535dbd0ec2e4..7f336193dfdc049361d2ecc3151fce57b60541a6 100644 GIT binary patch delta 7129 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%0A(uRq`(8J=AAY$X zVS*6nLP`7|dkA)#P_Y+F@pWWdW(|fgU+M<7H_{=J@=n{onTUyAIItlW1 z3RRCIM6-kfRRS2h1;#A_CXXx)chVVDnmV}d7=0VgJ2Ugt{$1ahnXevvM)u3#;4tdJ zZJaN6u1l)n@=j;pVE@fK3x3}&QKS!DNN9TbcFys0U;FdeMjvW-4Uzrc0jLI@i-7%? zWOPZ*V?W2umY{M@D?XRojCxD?$ z-x>Ti4=Y}uo0=SfB${)7VkY)VA^`XnNme|U^eFy}G%Jx3#0bD|NweWUi3#KNWSsGA zGOWZV84&=!RE8CQ03O0GgIS65kZ1tD7s84Ufra8-VXQ=Icpw133uDEnAhhw}2sZp0 z;w^pz!Ah(_CIIjSNLHe`92kIClVihYq89LRDAGvzWPG?hD-o)&M~y`S!N4m(8E+s= z_HWAv2$TEA^2PvRsK4zSKsflfMJwY6ghT%J`2*q5e=JNO#v2HS{nK*`2EyV0*f$)U zFhDrs?_K~9{udstOu%(N49PDm{m&mkK0x?Cus4D5e?WtPh`&IAG9eg0A0Prk9Ms?g z;LmAk1E6?wtw?GJ3<`!K5pwt$EnWZ&f2ak!=M>L5vL1cUL#+KlX{Bfv-!8oozc1PhiUY4@K<2mPinHLKk@;VgUbG;-@ggUAy9vSp#IVM-{VkF**}0$2-In{z$jTP z@(*cHa&o8B1p$L$r^i9SkUvy_fMMX%AP|z)r%6M=D8%0w*njOx2n2PSH3ULR@+k=+ zkUw)w3Pt=u5DNJ-7K%XqXWVc7Ay8S^AHXn@_cSmJ3i;Cqhmwl?cl-bHg2}=DPyr4> zp5B-cI2?Ls;PNDCqzSV~H?{s9ht W0l|J47g~rc0*Rs(5z#c#qWwRJc}C9w delta 7676 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 X2nq|pGPnR>Xc!6sxNt#TUjy)e5GNBJ diff --git a/thesis-evaluation/figures/timePerSingleQubitDecomposition.pdf b/thesis-evaluation/figures/timePerSingleQubitDecomposition.pdf index a7a9a3d2b124dc9873fddc335babc14ccfdf543a..d1eb956b32612a482b65911cfc2eda4c3d3409db 100644 GIT binary patch delta 6365 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 diff --git a/thesis-evaluation/figures/timePerTwoQubitDecomposition.pdf b/thesis-evaluation/figures/timePerTwoQubitDecomposition.pdf index 63a2523c780c33b727c4117847759b82c4fb6438..d22f0f738fba8e24fcac27ed44e5b94eddf0cb70 100644 GIT binary patch delta 6705 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 delta 7148 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 diff --git a/thesis-evaluation/figures/totalCircuitCollections.pdf b/thesis-evaluation/figures/totalCircuitCollections.pdf index 99d4f23350852482f5dfa2eedf1d3669ecea0018..9a42dd9966bc87b910ce916e65477b65b14c8d10 100644 GIT binary patch delta 6180 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 delta 6176 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 B(7ONt diff --git a/thesis-evaluation/figures/totalSingleQubitDecompositions.pdf b/thesis-evaluation/figures/totalSingleQubitDecompositions.pdf index a7a1a4ea33fe7242b4069b2efc32c74ab2dfc90c..b12c35be1f723ee2778f39add5482ef204710c3b 100644 GIT binary patch delta 7051 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 aB>b2{IXDuI=hD}vhr$>{MKukz82$_4VQUZo delta 7055 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 BU623( diff --git a/thesis-evaluation/figures/totalTouchedGates.pdf b/thesis-evaluation/figures/totalTouchedGates.pdf index a3077d54667ab418ff29745b0a654c47033ce1ed..40abe74a65a4296a81454a38736c5765ce0e17ee 100644 GIT binary patch delta 6628 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 delta 6554 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 U*br^C3kW0_Nh2Vjrms%(Ut9}9ApigX delta 7356 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 HMS}kUZZe{s delta 10035 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 diff --git a/thesis-evaluation/qiskit_run.py b/thesis-evaluation/qiskit_run.py index 29f23fe4b8..69302ddd03 100644 --- a/thesis-evaluation/qiskit_run.py +++ b/thesis-evaluation/qiskit_run.py @@ -1,4 +1,6 @@ import numpy as np +import sys +import re import os import glob import time @@ -25,26 +27,56 @@ def split_by_barriers(qc: QuantumCircuit) -> list[QuantumCircuit]: subcircuits = [] - # Start with an empty circuit that has the same registers - current = QuantumCircuit(*qc.qregs, *qc.cregs) - + # collect subcircuit instructions + current = [] + num_qubits = 0 for instr in qc.data: if instr.name == "barrier" or instr.name == "measure": # Finish current subcircuit if it has content - if current.data: - subcircuits.append(current) + if len(current) > 0: + circuit = QuantumCircuit(num_qubits) + for x in current: + remapped_qubits = [] + for y in x.qubits: + if num_qubits > 1: + prev_qubit_pos = qc.qubits.index(y) + else: + prev_qubit_pos = 0 + remapped_qubits.append(circuit.qubits[prev_qubit_pos]) + circuit.append(x.operation, remapped_qubits, x.clbits) + subcircuits.append(circuit) # Start a fresh one - current = QuantumCircuit(*qc.qregs, *qc.cregs) + current = [] + num_qubits = 0 else: - current.append(instr.operation, instr.qubits, instr.clbits) + current.append(instr) + num_qubits = max(num_qubits, len(instr.qubits)) # Append the last chunk if non-empty - if current.data: - subcircuits.append(current) + if len(current) > 0: + circuit = QuantumCircuit(num_qubits) + for x in current: + remapped_qubits = [] + for y in x.qubits: + if num_qubits > 1: + prev_qubit_pos = qc.qubits.index(y) + else: + prev_qubit_pos = 0 + remapped_qubits.append(circuit.qubits[prev_qubit_pos]) + circuit.append(x.operation, remapped_qubits, x.clbits) + subcircuits.append(circuit) return subcircuits +def circuit_length(qc: QuantumCircuit) -> int: + return qc.size(lambda instr: instr.name != "barrier" and instr.name != "measure") + + +def contains_foreign_gates(qc: QuantumCircuit) -> bool: + return qc.size(lambda instr: instr.name not in ["rz", "rx", "ry", "cx"]) > 0 + + def circuit_complexity(qc: QuantumCircuit) -> int: num_one_qubit_gates = qc.size( lambda instr: len(instr.qubits) == 1 and instr.name != "barrier" @@ -59,24 +91,63 @@ def circuit_complexity(qc: QuantumCircuit) -> int: return num_one_qubit_gates + 10 * num_two_qubit_gates -def evaluate(): +def read_rust_timings_file( + file_name="/tmp/qiskit_rust.timing", remove_file=False +) -> dict[str, list[int]]: + patterns = { + "timePerTwoQubitDecomposition": re.compile( + r"TwoQubitBasisDecomposer::generate_sequence\(\):\s+(\d+)us" + ), + "twoQubitCreationTime": re.compile( + r"TwoQubitBasisDecomposer::new_inner\(\):\s+(\d+)us" + ), + "timePerSingleQubitDecomposition": re.compile( + r"unitary_to_gate_sequence_inner\(\):\s+(\d+)ns" + ), + } + + timings: dict[str, list[int]] = {} + with open(file_name) as f: + for line in f.readlines(): + print(line) + for metric, pattern in patterns.items(): + match = pattern.search(line) + if match: + timings[metric] = timings.get(metric, []) + [int(match.group(1))] + if remove_file: + os.remove(file_name) + return timings + + +def evaluate(evaluate_rust_timings=False): stats = {} otherCX = QuantumCircuit(2) otherCX.cx(1, 0) otherCXGate = otherCX.to_gate() - oneQubitDec = OneQubitEulerDecomposer("ZYZ") - start_time = time.perf_counter_ns() - dec = TwoQubitBasisDecomposer(CXGate(), euler_basis="ZYZ") - dec2 = TwoQubitBasisDecomposer(otherCXGate, euler_basis="ZYZ") - end_time = time.perf_counter_ns() - creation_time_us = (end_time - start_time) / 1000 - print(f"Python Basis Decomposition Creation: {creation_time_us}µs") + oneQubitDecs = [ + OneQubitEulerDecomposer("ZYZ"), + OneQubitEulerDecomposer("ZXZ"), + OneQubitEulerDecomposer("XYX"), + OneQubitEulerDecomposer("XZX"), + ] print(f"Processing '{MQT_BENCH_DIR}/{MQT_BENCH_PATTERN}'...") for file in glob.glob(f"{MQT_BENCH_DIR}/{MQT_BENCH_PATTERN}"): name = os.path.basename(file).removesuffix(".qasm") print(f"Decomposing {name} ({file})") + + start_time = time.perf_counter_ns() + dec = TwoQubitBasisDecomposer(CXGate(), euler_basis="ZYZ") + dec2 = TwoQubitBasisDecomposer(otherCXGate, euler_basis="ZYZ") + end_time = time.perf_counter_ns() + creation_time_us = (end_time - start_time) / 1000 + print(f"Python Basis Decomposition Creation: {creation_time_us}µs") + if evaluate_rust_timings: + rust_stats = read_rust_timings_file(remove_file=True) + creation_time_us = np.sum(rust_stats["twoQubitCreationTime"]) + twoQubitDecs = [dec, dec2] + with open(file, "r") as f: content = f.read() try: @@ -103,49 +174,65 @@ def evaluate(): num_two_qubit_decompositions = 0 num_single_qubit_decompositions = 0 for subcircuit in subcircuits: + if circuit_length(subcircuit) < 3 and not contains_foreign_gates( + subcircuit + ): + print(f"SKIPPED (length: {circuit_length(subcircuit)})") + continue m = Operator(subcircuit) - if len(qc.qubits) == 1: + decomposed_circuits = [] + if len(subcircuit.qubits) == 1: start_time = time.perf_counter_ns() - decomposed_circuit = oneQubitDec(m) - decomposed_circuit2 = None + for dec in oneQubitDecs: + decomposed_circuits.append(dec(m)) end_time = time.perf_counter_ns() decomposition_time = (end_time - start_time) / 1000 - decomposition_times_1q.append(decomposition_time) + + if evaluate_rust_timings: + rust_stats = read_rust_timings_file(remove_file=True) + decomposition_times_1q.append( + # measured in nanoseconds, convert to microseconds + np.sum(rust_stats["timePerSingleQubitDecomposition"]) / 1000 + ) + else: + decomposition_times_1q.append(decomposition_time) num_single_qubit_decompositions += 1 - elif len(qc.qubits) == 2: + elif len(subcircuit.qubits) == 2: start_time = time.perf_counter_ns() - decomposed_circuit = dec(m) - decomposed_circuit2 = dec2(m) + for dec in twoQubitDecs: + decomposed_circuits.append(dec(m)) end_time = time.perf_counter_ns() decomposition_time = (end_time - start_time) / 1000 - decomposition_times_2q.append(decomposition_time) + + if evaluate_rust_timings: + rust_stats = read_rust_timings_file(remove_file=True) + decomposition_times_2q.append( + np.sum(rust_stats["timePerTwoQubitDecomposition"][1:]) + ) + else: + decomposition_times_2q.append(decomposition_time) num_two_qubit_decompositions += 1 else: raise RuntimeError("Invalid circuit size!") before_complexity = circuit_complexity(subcircuit) - after_complexity = circuit_complexity(decomposed_circuit) - - if decomposed_circuit2: - after_complexity2 = circuit_complexity(decomposed_circuit2) - if after_complexity2 < after_complexity: - print( - f"Choose alternative decomposition ({after_complexity2} vs {after_complexity})!" - ) - decomposed_circuit = decomposed_circuit2 - after_complexity = after_complexity2 + after_complexities = [circuit_complexity(x) for x in decomposed_circuits] + best_decomposed_circuit = decomposed_circuits[ + after_complexities.index(min(after_complexities)) + ] + best_after_complexity = min(after_complexities) print(subcircuit) - print(f"{before_complexity} -> {after_complexity}") - print(decomposed_circuit) + print(f"{before_complexity} -> {best_after_complexity}") + print(best_decomposed_circuit) print(Operator(subcircuit).data) print("vs") - print(Operator(decomposed_circuit).data) + print(Operator(best_decomposed_circuit).data) - complexity_changes.append(before_complexity - after_complexity) + complexity_changes.append(before_complexity - best_after_complexity) stats[name] = { "timeInSingleQubitDecomposition": sum(decomposition_times_1q), @@ -164,4 +251,6 @@ def evaluate(): if __name__ == "__main__": - evaluate() + evaluate_rust_timings = sys.argv[-1] == "--rust-timings" + print(f"Rust evaluation: {evaluate_rust_timings} ({sys.argv[-1]})") + evaluate(evaluate_rust_timings) diff --git a/thesis-evaluation/run.sh b/thesis-evaluation/run.sh index 43f070b123..fce20ceb76 100755 --- a/thesis-evaluation/run.sh +++ b/thesis-evaluation/run.sh @@ -2,7 +2,7 @@ OUTPUT_DIR="tmp-output" -MQT_CORE_ROOT_DIR=.. +MQT_CORE_BUILD_DIR="../build.release" MQT_BENCH_BENCHMARK_DIR="../../mqt-bench/generated_benchmarks/v3_qasm3" MQT_BENCH_PATTERN="*.qasm" @@ -16,7 +16,7 @@ for benchmark_path in ${MQT_BENCH_BENCHMARK_DIR}/${MQT_BENCH_PATTERN}; do i=$((i + 1)) benchmark="$(basename "${benchmark_path}")" echo "${i}/${benchmark_count}: ${benchmark}" - result="$("${MQT_CORE_ROOT_DIR}/build/mlir/tools/mqt-cc/mqt-cc" --mlir-timing --mlir-statistics "${benchmark_path}" 2>&1)" + result="$("${MQT_CORE_BUILD_DIR}/mlir/tools/mqt-cc/mqt-cc" --mlir-timing --mlir-statistics "${benchmark_path}" 2>&1)" if [ "${?}" -eq 0 ]; then echo "${result}" | rg 'Total Execution Time' -A 8 >"${OUTPUT_DIR}/${benchmark}.timing" echo "${result}" | rg '\(S\) ' >"${OUTPUT_DIR}/${benchmark}.statistic" diff --git a/thesis-evaluation/total_evaluate.py b/thesis-evaluation/total_evaluate.py index 4b9de3dbcf..26109a4fbb 100644 --- a/thesis-evaluation/total_evaluate.py +++ b/thesis-evaluation/total_evaluate.py @@ -13,46 +13,72 @@ if not os.path.exists(OUT_DIR): os.makedirs(OUT_DIR, exist_ok=True) -CACHE_FILE = "./evaluate_cache.json" -if os.path.exists(CACHE_FILE): - with open(CACHE_FILE, "r") as f: - cache = json.load(f) - mqt_results = cache["mqt"] - qiskit_results = cache["qiskit"] +QISKIT_CACHE_FILE = "./evaluate_cache_qiskit.json" +QISKIT_RUST_CACHE_FILE = "./evaluate_cache_qiskit-rust.json" +MQT_CACHE_FILE = "./evaluate_cache_mqt.json" + + +def average_results( + all_results: list[dict[str, dict[str, int | float]]], +) -> dict[str, dict[str, int | float]]: + result = {} + for r in all_results: + for benchmark_name, measurements in r.items(): + if not benchmark_name in result: + result[benchmark_name] = {} + for metric_name, value in measurements.items(): + if not metric_name in result[benchmark_name]: + result[benchmark_name][metric_name] = 0.0 + result[benchmark_name][metric_name] += value / ITERATIONS + return result + + +if os.path.exists(QISKIT_CACHE_FILE): + with open(QISKIT_CACHE_FILE, "r") as f: + qiskit_results = json.load(f) +else: + ITERATIONS = 20 + all_results = [] + for _ in range(ITERATIONS): + all_results.append(qiskit_run.evaluate()) + + # [benchmark_name -> [metric_name -> value]] + qiskit_results = average_results(all_results) + + with open(QISKIT_CACHE_FILE, "w") as f: + json.dump(qiskit_results, f) + +if os.path.exists(MQT_CACHE_FILE): + with open(MQT_CACHE_FILE, "r") as f: + mqt_results = json.load(f) else: ITERATIONS = 20 - all_mqt_results = [] - all_qiskit_results = [] + all_results = [] for _ in range(ITERATIONS): # evaluate.evalue() will only process statistics files; need to re-run mqt-cc using run.sh subprocess.run(["./run.sh"]).check_returncode() - all_mqt_results.append(evaluate.evaluate()) - all_qiskit_results.append(qiskit_run.evaluate()) - - def average_results( - all_results: list[dict[str, dict[str, int | float]]], - ) -> dict[str, dict[str, int | float]]: - result = {} - for r in all_results: - for benchmark_name, measurements in r.items(): - if not benchmark_name in result: - result[benchmark_name] = {} - for metric_name, value in measurements.items(): - if not metric_name in result[benchmark_name]: - result[benchmark_name][metric_name] = 0.0 - result[benchmark_name][metric_name] += value / ITERATIONS - return result + all_results.append(evaluate.evaluate()) + + # [benchmark_name -> [metric_name -> value]] + mqt_results = average_results(all_results) + + with open(MQT_CACHE_FILE, "w") as f: + json.dump(mqt_results, f) + +if os.path.exists(QISKIT_RUST_CACHE_FILE): + with open(QISKIT_RUST_CACHE_FILE, "r") as f: + qiskit_rust_results = json.load(f) +else: + ITERATIONS = 20 + all_results = [] + for _ in range(ITERATIONS): + all_results.append(qiskit_run.evaluate(evaluate_rust_timings=True)) # [benchmark_name -> [metric_name -> value]] - mqt_results = average_results(all_mqt_results) - qiskit_results = average_results(all_qiskit_results) + qiskit_rust_results = average_results(all_results) - with open(CACHE_FILE, "w") as f: - cache = { - "mqt": mqt_results, - "qiskit": qiskit_results, - } - json.dump(cache, f) + with open(QISKIT_RUST_CACHE_FILE, "w") as f: + json.dump(qiskit_rust_results, f) print("In MQT, but not Qiskit: ", mqt_results.keys() - qiskit_results.keys()) print("In Qiskit, but not MQT: ", qiskit_results.keys() - mqt_results.keys()) @@ -85,7 +111,8 @@ def define_division_metric( names = sorted(mqt_results.keys() & qiskit_results.keys()) for name in names: m = mqt_results[name] - q = qiskit_results[name] + # q = qiskit_results[name] + q = qiskit_rust_results[name] for metric in m.keys() | q.keys(): if metric in m: @@ -95,7 +122,9 @@ def define_division_metric( if metric in q: y2[metric] = y2.get(metric, []) + [q[metric]] if metric in aliases_qiskit: - y2[aliases_qiskit[metric]] = y2.get(aliases_qiskit[metric], []) + [q[metric]] + y2[aliases_qiskit[metric]] = y2.get(aliases_qiskit[metric], []) + [ + q[metric] + ] define_division_metric( "timePerSingleQubitDecomposition", @@ -223,6 +252,7 @@ def define_division_metric( ymin = min(ymin, min(y2[metric])) # plt.xticks(x_values) # does not work for strings + plt.xticks(rotation=45) if ymin > 0: # let matplotlib handle non-positive values automatically plt.ylim(bottom=0) @@ -239,7 +269,7 @@ def define_division_metric( plt.savefig( f"{OUT_DIR}/{metric}.pdf", format="pdf", bbox_inches="tight", pad_inches=0 ) - # plt.show() + #plt.show() plt.clf() for i, name in enumerate(names): From 97755cf753f9ecea468ccbb54923f982346b1e5a Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 14 Feb 2026 12:36:40 +0100 Subject: [PATCH 226/237] evaluation results --- thesis-evaluation/evaluate_cache_mqt.json | 2 +- .../evaluate_cache_qiskit-rust.json | 2 +- thesis-evaluation/evaluate_cache_qiskit.json | 2 +- .../figures/numberOfTwoQubitCreations.pdf | Bin 16590 -> 16590 bytes .../figures/subCircuitComplexityChange.pdf | Bin 20621 -> 20621 bytes .../successfulSingleQubitDecompositions.pdf | Bin 18978 -> 18978 bytes .../successfulTwoQubitDecompositions.pdf | Bin 18650 -> 18650 bytes .../figures/timeInCircuitCollection.pdf | Bin 22554 -> 22764 bytes .../timeInCircuitCollectionStandalone.pdf | Bin 18503 -> 18614 bytes .../timeInSingleQubitDecomposition.pdf | Bin 19513 -> 19518 bytes .../figures/timeInTwoQubitDecomposition.pdf | Bin 18709 -> 18716 bytes .../timePerSingleQubitDecomposition.pdf | Bin 18055 -> 18063 bytes .../figures/timePerTwoQubitDecomposition.pdf | Bin 17836 -> 17800 bytes .../figures/totalCircuitCollections.pdf | Bin 17485 -> 17485 bytes .../totalSingleQubitDecompositions.pdf | Bin 19783 -> 19783 bytes .../figures/totalTouchedGates.pdf | Bin 18954 -> 18954 bytes .../figures/totalTwoQubitDecompositions.pdf | Bin 19059 -> 19059 bytes .../figures/twoQubitCreationTime.pdf | Bin 23367 -> 23498 bytes 18 files changed, 3 insertions(+), 3 deletions(-) diff --git a/thesis-evaluation/evaluate_cache_mqt.json b/thesis-evaluation/evaluate_cache_mqt.json index adf782602c..51525d60e1 100644 --- a/thesis-evaluation/evaluate_cache_mqt.json +++ b/thesis-evaluation/evaluate_cache_mqt.json @@ -1 +1 @@ -{"modular_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.3000000000000003, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 44.7}, "bmw_quark_cardinality_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.4000000000000004, "timeInSingleQubitDecomposition": 1.3000000000000005, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 13.999999999999995, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 11.999999999999996, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 44.7}, "draper_qft_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.0500000000000003, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12.35, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 43.05}, "vqe_two_local_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.299999999999997, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 194.34999999999997, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 38.2}, "wstate_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -8.000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.3000000000000003, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 23.300000000000004, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 39.95}, "bmw_quark_copula_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.2, "timeInSingleQubitDecomposition": 2.3500000000000005, "timeInTwoQubitDecomposition": 15.149999999999999, "totalCircuitCollections": 32.999999999999986, "totalSingleQubitDecompositions": 8.000000000000002, "totalTouchedGates": 50.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 39.599999999999994}, "qaoa_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.4000000000000006, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 39.3}, "qftentangled_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -13.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.55, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 31.150000000000002, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 38.3}, "wstate_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.3, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 34.900000000000006}, "ghz_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 1.2000000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 14.549999999999995, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 35.15}, "qpeinexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -16.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.0500000000000007, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 23.099999999999998, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.900000000000006}, "vqe_real_amp_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.1500000000000006, "timeInSingleQubitDecomposition": 1.1000000000000003, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 43.300000000000004}, "vqe_su2_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 5.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.4000000000000006, "timeInSingleQubitDecomposition": 1.3000000000000005, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 35.6}, "qft_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.39999999999999997, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 36.300000000000004}, "bmw_quark_cardinality_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -55.99999999999998, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 2.999999999999999, "timeInCircuitCollection": 1.0000000000000002, "timeInSingleQubitDecomposition": 2.45, "timeInTwoQubitDecomposition": 65.5, "totalCircuitCollections": 47.999999999999986, "totalSingleQubitDecompositions": 4.000000000000001, "totalTouchedGates": 50.999999999999986, "totalTwoQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 37.5}, "bv_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.3, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 35.7}, "ghz_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.2, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 36.45}, "vqe_su2_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 17.500000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 325.15000000000003, "totalCircuitCollections": 20.0, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 198.00000000000006, "totalTwoQubitDecompositions": 15.0, "twoQubitCreationTime": 37.050000000000004}, "qnn_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 3.049999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 19.449999999999992, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 13.000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 39.6}, "qpeexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.0000000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 13.850000000000001, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.10000000000001}, "qnn_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.7000000000000004, "timeInSingleQubitDecomposition": 1.3000000000000005, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 39.0}, "vqe_real_amp_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 8.049999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 201.09999999999994, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 39.65}, "qftentangled_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.9500000000000003, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 36.099999999999994}, "bv_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 2.0000000000000004, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.6, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 2.0000000000000004, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 37.79999999999999}, "randomcircuit_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.0500000000000003, "timeInSingleQubitDecomposition": 1.1500000000000004, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 36.55}, "qft_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -23.999999999999993, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.0500000000000003, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 31.650000000000002, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.050000000000004}, "qaoa_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -6.999999999999997, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.2000000000000006, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 26.5, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.10000000000001}} \ No newline at end of file +{"modular_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.3000000000000005, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 50.2}, "bmw_quark_cardinality_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.4000000000000004, "timeInSingleQubitDecomposition": 1.8500000000000003, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 13.999999999999995, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 11.999999999999996, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 46.5}, "draper_qft_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.0000000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12.249999999999996, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 43.5}, "vqe_two_local_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.299999999999998, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 185.75000000000003, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 37.14999999999999}, "wstate_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -8.000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.2000000000000006, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 22.750000000000007, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.9}, "bmw_quark_copula_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.2000000000000006, "timeInSingleQubitDecomposition": 2.150000000000001, "timeInTwoQubitDecomposition": 14.449999999999996, "totalCircuitCollections": 32.999999999999986, "totalSingleQubitDecompositions": 8.000000000000002, "totalTouchedGates": 50.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 38.50000000000001}, "qaoa_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.2500000000000004, "timeInSingleQubitDecomposition": 1.2000000000000006, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 37.3}, "qftentangled_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -13.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.4, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 30.85, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.75}, "wstate_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.1, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 40.80000000000001}, "ghz_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 1.1500000000000001, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 14.349999999999996, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 34.3}, "qpeinexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -16.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 1.8000000000000007, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 22.650000000000002, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 35.65}, "vqe_real_amp_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.2000000000000004, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 44.650000000000006}, "vqe_su2_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 5.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.2000000000000002, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 35.1}, "qft_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.0, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 35.7}, "bmw_quark_cardinality_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -55.99999999999998, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 2.999999999999999, "timeInCircuitCollection": 1.0000000000000002, "timeInSingleQubitDecomposition": 2.5, "timeInTwoQubitDecomposition": 65.55000000000001, "totalCircuitCollections": 47.999999999999986, "totalSingleQubitDecompositions": 4.000000000000001, "totalTouchedGates": 50.999999999999986, "totalTwoQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 40.8}, "bv_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.05, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 36.95}, "ghz_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.2, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 35.95}, "vqe_su2_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 19.349999999999998, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 351.25, "totalCircuitCollections": 20.0, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 198.00000000000006, "totalTwoQubitDecompositions": 15.0, "twoQubitCreationTime": 37.55}, "qnn_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 3.099999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 19.249999999999996, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 13.000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 38.650000000000006}, "qpeexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 1.6500000000000006, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 13.600000000000005, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 34.45}, "qnn_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.3000000000000005, "timeInSingleQubitDecomposition": 1.4000000000000004, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 34.55}, "vqe_real_amp_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.449999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 187.54999999999998, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 38.7}, "qftentangled_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.65, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 36.45}, "bv_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 2.0000000000000004, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.15000000000000002, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 2.0000000000000004, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 35.25}, "randomcircuit_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.0500000000000003, "timeInSingleQubitDecomposition": 1.2000000000000004, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 35.55}, "qft_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -23.999999999999993, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.0000000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 32.00000000000001, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 35.900000000000006}, "qaoa_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -6.999999999999997, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.2000000000000006, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 33.95000000000001, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.05}} \ No newline at end of file diff --git a/thesis-evaluation/evaluate_cache_qiskit-rust.json b/thesis-evaluation/evaluate_cache_qiskit-rust.json index b4092f9bcc..4227982bc2 100644 --- a/thesis-evaluation/evaluate_cache_qiskit-rust.json +++ b/thesis-evaluation/evaluate_cache_qiskit-rust.json @@ -1 +1 @@ -{"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.6693000000000002, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 68.24999999999999, "timeInCircuitCollection": 59.871500000000005}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 47.89999999999999, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.25000000000001, "timeInCircuitCollection": 80.76885}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 46.05, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.2, "timeInCircuitCollection": 100.9747}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.95, "timeInCircuitCollection": 39.55315}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 8.898950000000001, "timeInTwoQubitDecomposition": 140.64999999999998, "subCircuitComplexityChange": -65.99999999999997, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 36.65, "timeInCircuitCollection": 242.69385000000003}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 5.838399999999999, "timeInTwoQubitDecomposition": 35.8, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 2.0000000000000004, "twoQubitCreationTime": 36.949999999999996, "timeInCircuitCollection": 167.0617}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 57.40000000000001, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.650000000000006, "timeInCircuitCollection": 116.8651}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.4743500000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.8, "timeInCircuitCollection": 53.9262}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 59.50000000000001, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.949999999999996, "timeInCircuitCollection": 167.32000000000002}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 56.3, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.400000000000006, "timeInCircuitCollection": 67.90270000000001}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 36.5, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.199999999999996, "timeInCircuitCollection": 66.17474999999999}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 36.25000000000001, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.3, "timeInCircuitCollection": 48.367650000000005}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 37.6, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.849999999999994, "timeInCircuitCollection": 61.388749999999995}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 39.5, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.74999999999999, "timeInCircuitCollection": 123.67175}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.9454000000000002, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.35, "timeInCircuitCollection": 51.8517}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 57.55, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.29999999999999, "timeInCircuitCollection": 115.60185}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.1652500000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.14999999999999, "timeInCircuitCollection": 35.40555}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.8032999999999997, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.1, "timeInCircuitCollection": 83.2311}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 2.1692500000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.0, "timeInCircuitCollection": 48.4669}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 45.699999999999996, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.949999999999996, "timeInCircuitCollection": 82.72365}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 47.4, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.49999999999999, "timeInCircuitCollection": 70.39055}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 47.3, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.300000000000004, "timeInCircuitCollection": 74.90429999999998}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 3.2137000000000002, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.29999999999999, "timeInCircuitCollection": 71.526}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.9353, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.849999999999994, "timeInCircuitCollection": 114.38800000000002}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.1753500000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.3, "timeInCircuitCollection": 33.42935}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.56235, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.6, "timeInCircuitCollection": 34.2841}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 1.8582999999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.14999999999999, "timeInCircuitCollection": 42.88195}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 47.15, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.449999999999996, "timeInCircuitCollection": 65.00000000000001}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 36.55000000000001, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.7, "timeInCircuitCollection": 71.21195}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.5497000000000005, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.1, "timeInCircuitCollection": 34.8426}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 3.0153500000000006, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.550000000000004, "timeInCircuitCollection": 48.33565}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 36.4, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.75000000000001, "timeInCircuitCollection": 57.89245000000001}} \ No newline at end of file +{"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.8548499999999994, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 75.10000000000002, "timeInCircuitCollection": 58.458099999999995}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 47.050000000000004, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.699999999999996, "timeInCircuitCollection": 80.51124999999999}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 46.650000000000006, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.050000000000004, "timeInCircuitCollection": 96.09929999999997}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.099999999999994, "timeInCircuitCollection": 39.7703}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 9.42315, "timeInTwoQubitDecomposition": 138.75, "subCircuitComplexityChange": -65.99999999999997, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 42.599999999999994, "timeInCircuitCollection": 240.12545}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 6.2312, "timeInTwoQubitDecomposition": 36.25, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 2.0000000000000004, "twoQubitCreationTime": 36.349999999999994, "timeInCircuitCollection": 161.75055}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 57.84999999999999, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.5, "timeInCircuitCollection": 111.99449999999999}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.7302500000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 35.7, "timeInCircuitCollection": 53.66680000000001}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 60.5, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 35.7, "timeInCircuitCollection": 170.19494999999998}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 55.849999999999994, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.65, "timeInCircuitCollection": 65.83065000000002}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 37.20000000000001, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.6, "timeInCircuitCollection": 77.40250000000002}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 35.949999999999996, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.75, "timeInCircuitCollection": 48.24609999999999}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 38.20000000000001, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 35.95, "timeInCircuitCollection": 61.87939999999998}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 37.400000000000006, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.2, "timeInCircuitCollection": 119.5399}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 3.05645, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.8, "timeInCircuitCollection": 52.255399999999995}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 58.499999999999986, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.1, "timeInCircuitCollection": 111.47349999999999}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.3413, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 35.65, "timeInCircuitCollection": 34.41895}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.9997000000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 35.55, "timeInCircuitCollection": 80.91534999999999}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 2.40145, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 42.05, "timeInCircuitCollection": 50.45795}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 46.05, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.699999999999996, "timeInCircuitCollection": 85.81044999999999}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 47.34999999999999, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.24999999999999, "timeInCircuitCollection": 69.3054}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 54.2, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.849999999999994, "timeInCircuitCollection": 72.90065}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 3.4193000000000007, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.699999999999996, "timeInCircuitCollection": 70.28235}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 3.20185, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.45, "timeInCircuitCollection": 112.36549999999997}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.3529500000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.699999999999996, "timeInCircuitCollection": 33.20055000000001}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.7712499999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.25, "timeInCircuitCollection": 35.09595}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.0303, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 42.15, "timeInCircuitCollection": 43.899950000000004}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 47.900000000000006, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.7, "timeInCircuitCollection": 66.68315}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 36.300000000000004, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 35.94999999999999, "timeInCircuitCollection": 71.29590000000002}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.7689000000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.050000000000004, "timeInCircuitCollection": 34.67245}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 3.1816999999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.9, "timeInCircuitCollection": 47.785199999999996}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 36.0, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.550000000000004, "timeInCircuitCollection": 57.038399999999996}} \ No newline at end of file diff --git a/thesis-evaluation/evaluate_cache_qiskit.json b/thesis-evaluation/evaluate_cache_qiskit.json index 67e67ca2c5..ce68f809d0 100644 --- a/thesis-evaluation/evaluate_cache_qiskit.json +++ b/thesis-evaluation/evaluate_cache_qiskit.json @@ -1 +1 @@ -{"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 296.69720000000007, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 220.44915000000003, "timeInCircuitCollection": 58.09570000000001}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 157.23375000000001, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 180.81419999999997, "timeInCircuitCollection": 78.45695000000002}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 154.60445, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 179.16495, "timeInCircuitCollection": 95.56074999999998}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 175.47254999999998, "timeInCircuitCollection": 37.9996}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 872.1498999999999, "timeInTwoQubitDecomposition": 461.6757, "subCircuitComplexityChange": -65.99999999999997, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 177.9274, "timeInCircuitCollection": 236.82060000000004}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 575.1606499999999, "timeInTwoQubitDecomposition": 131.47050000000002, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 2.0000000000000004, "twoQubitCreationTime": 175.05319999999998, "timeInCircuitCollection": 161.2176}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 176.98079999999996, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.42295000000001, "timeInCircuitCollection": 114.41595}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 289.4362499999999, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 177.24789999999996, "timeInCircuitCollection": 51.5821}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 180.56085, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 177.94589999999994, "timeInCircuitCollection": 164.5999}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 175.01370000000003, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 176.63645000000002, "timeInCircuitCollection": 65.38274999999999}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 131.6936, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 175.73405, "timeInCircuitCollection": 63.1769}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 131.3157, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 177.59835, "timeInCircuitCollection": 47.40789999999999}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 135.70365, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 175.7378, "timeInCircuitCollection": 59.752750000000006}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 135.4567, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.08245, "timeInCircuitCollection": 122.72814999999999}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 291.7512, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 176.22564999999997, "timeInCircuitCollection": 51.3624}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 179.04050000000004, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 185.18149999999997, "timeInCircuitCollection": 113.37980000000003}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 286.04645000000005, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 175.8176, "timeInCircuitCollection": 33.39065}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 292.03305000000006, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 178.59234999999998, "timeInCircuitCollection": 80.36815}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 288.15489999999994, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 176.277, "timeInCircuitCollection": 46.53595}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 152.7687, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.27049999999997, "timeInCircuitCollection": 81.1797}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 157.21085, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 176.39600000000002, "timeInCircuitCollection": 66.92224999999999}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 155.94840000000002, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 177.81455, "timeInCircuitCollection": 72.6108}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 294.88100000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 176.763, "timeInCircuitCollection": 69.81880000000002}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 292.5032999999999, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 179.21890000000002, "timeInCircuitCollection": 111.38385}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 285.76345, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 179.039, "timeInCircuitCollection": 32.29745}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 285.62745, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 177.08870000000002, "timeInCircuitCollection": 32.9989}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 281.29725, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 178.22199999999998, "timeInCircuitCollection": 41.21045}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 154.96949999999998, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.22134999999997, "timeInCircuitCollection": 63.23895000000001}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 133.15385, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 177.6715, "timeInCircuitCollection": 69.82260000000001}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 289.8093, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 180.41359999999997, "timeInCircuitCollection": 34.006150000000005}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 291.50255000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 177.928, "timeInCircuitCollection": 46.6455}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 133.66164999999998, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.29955, "timeInCircuitCollection": 56.619299999999996}} \ No newline at end of file +{"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 343.54330000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 295.21914999999996, "timeInCircuitCollection": 58.103300000000004}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 159.76119999999997, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 182.90365000000003, "timeInCircuitCollection": 79.48235}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 153.97265000000002, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 182.4087, "timeInCircuitCollection": 98.20005}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 185.03105000000002, "timeInCircuitCollection": 39.53504999999999}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 870.63575, "timeInTwoQubitDecomposition": 480.2716500000001, "subCircuitComplexityChange": -65.99999999999997, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 184.22195, "timeInCircuitCollection": 244.47424999999998}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 577.7537000000001, "timeInTwoQubitDecomposition": 132.86515, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 2.0000000000000004, "twoQubitCreationTime": 178.18085000000002, "timeInCircuitCollection": 165.95174999999998}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 177.3191, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 179.31825000000006, "timeInCircuitCollection": 115.12845}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 294.06924999999995, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 178.501, "timeInCircuitCollection": 52.96925}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 186.69230000000002, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 179.4108, "timeInCircuitCollection": 166.7919}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 175.7082, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 181.0885, "timeInCircuitCollection": 66.4998}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 132.24415000000002, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.81040000000002, "timeInCircuitCollection": 65.3237}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 130.79319999999998, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 179.72724999999997, "timeInCircuitCollection": 54.5683}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 134.99695, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 191.7011, "timeInCircuitCollection": 67.07975}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 135.29895, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 179.7218, "timeInCircuitCollection": 130.57875}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 295.01525000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 178.13099999999997, "timeInCircuitCollection": 51.236349999999995}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 179.29979999999998, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 179.60395000000003, "timeInCircuitCollection": 123.0643}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 293.50190000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 179.5504, "timeInCircuitCollection": 34.0895}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 291.05845000000005, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 179.12944999999993, "timeInCircuitCollection": 81.93225}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 288.86820000000006, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 184.63150000000002, "timeInCircuitCollection": 48.12319999999999}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 151.21535, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 179.45475000000002, "timeInCircuitCollection": 83.52945000000001}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 164.7773, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 177.9284, "timeInCircuitCollection": 67.98870000000001}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 155.93705, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.49224999999998, "timeInCircuitCollection": 78.1205}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 308.2328, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 177.05255, "timeInCircuitCollection": 71.12490000000001}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 293.10975, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 177.5605, "timeInCircuitCollection": 122.1058}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 285.43385, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 188.28925000000004, "timeInCircuitCollection": 32.772200000000005}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 293.1708, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 184.8516, "timeInCircuitCollection": 33.564150000000005}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 288.0396, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 181.51809999999995, "timeInCircuitCollection": 42.056}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 155.25050000000002, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 179.67045000000005, "timeInCircuitCollection": 66.23519999999999}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 132.66865, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.0279, "timeInCircuitCollection": 72.4269}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 289.3162, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 180.0993, "timeInCircuitCollection": 34.069}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 299.75894999999997, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 182.3752, "timeInCircuitCollection": 49.293499999999995}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 132.8356, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 180.22440000000003, "timeInCircuitCollection": 57.563050000000004}} \ No newline at end of file diff --git a/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf b/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf index e385ab3f2e233d6917f64b5bde451a5aae588cf8..62f7520e7a7ae1c94a54d5c587227a7ffce27c72 100644 GIT binary patch delta 22 ecmX@t$at=ial;uKb`wJ*V-sV;&6jO{GXnr+WC(%) delta 22 ecmX@t$at=ial;uKc4H$0BQsNr&6jO{GXnr+jtGhX diff --git a/thesis-evaluation/figures/subCircuitComplexityChange.pdf b/thesis-evaluation/figures/subCircuitComplexityChange.pdf index 5b26baae3b8c90a508d1942a7fe55dc3b9615d75..f2d66e6837c19ed04bd19db9f602709509a3389a 100644 GIT binary patch delta 22 dcmeBO$k@A(al?Oab`wJ*V-sV;&1^pHEC64>2T}k4 delta 22 dcmeBO$k@A(al?Oac4H$0BQsO;&1^pHEC65Q2U!3B diff --git a/thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf b/thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf index feb698347b58febef94f88eb4c894b00ab700368..2114f553680bb1284363c5b8a7259069b00d2df5 100644 GIT binary patch delta 22 ecmZ29g>lgo#tkc6*-Z?Mj7^LUHm`RrW&r?R6$g?4 delta 22 ecmZ29g>lgo#tkc6*^P}1jLb|eH?MasW&r?RO9z|) diff --git a/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf b/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf index 578a5ed40edc5bc2827d89573f85268b416a1eec..5df8037ca26bc339b9175909464d2d8163792028 100644 GIT binary patch delta 22 dcmcaLk@40<#tjKB>?Vdr#wNxFo6}v2SpZ=#2WkKS delta 22 dcmcaLk@40<#tjKB?8Zh0MrNiKo6}v2SpZ>Q2Xg=b diff --git a/thesis-evaluation/figures/timeInCircuitCollection.pdf b/thesis-evaluation/figures/timeInCircuitCollection.pdf index aa8274211de262e2fb83446ab51246166f980ff6..cb26ceb1232dbaa01ef0ff1fcc86c49ead9b494c 100644 GIT binary patch delta 11218 zcmZX3bzGEP^FATnu`J!4yDSaT9n#$(A<|v4w7`-s-QC^Y9ij**(v5&pBA|f3eV*t2 ze1Grzc>i_I+;h)$oilUJ%+6*vBA+%PH&P&@3-a=VA;NHaWIyOv^Oaf zCnXDh@2vj5zujJK7`R`#xU%dEc7^^r--StqBtZwn*DiwYe|3z|S?+9}Sx`Uz7*TV+ zBMYvjfc~<0Br}J0cP)6uf_knSem`Db_GHWtgu>}AmNWSHiq_2zO&QiqaD!E!=IEyC98pzGGRViVJ)K_$-()_C1CpQ;P_8?JP zQ=}2T&y<61dyw^kBxO|BXRR(-Pr0p2VjoWT(b5k}Fr4nII+rO;L8=!`7>1+R0#iZ5 zvb%CP-(w4HodlVOJq5=Xx5<&C4QyzUMS8wSj+y znHW~xPH$Ht6s$T0e}hH5bYS8h(E2`zd(0+FI|mQSS7Ks%edKz{jQw3SSJQ%4yQVGO z>|-%1k|;1dbT-H8Qs}goVN!xJ#h_ismA%fBJG(blTy4!kjs1Zv@QnAp622tT5CPxP zy6WV*Uz35=*S4t zoVOc8ck5VMuiZ11Jd>1FQ<{yHx&$VExUjF(ft$yJjRfozZ9Q`E<{CEw6X=x-xI2!+ zci|tHv=+mQ95^WP3b&)KCj64^NYq!N&4r$1DKUAH)x}dO)=HOozXabwQCrM#9pg(l6mes0Q=RwGH@4FnK_3A9 zX7=ERblC2!gOGPb5X0`*8OnF>wmE1Hh=R-0iKAt>sDfGUr{Kh>SmU4=tM3xrnKnbS zjdRf*>pTEMGs2f&g*V@IWc$~~;Jo*&g*S7FAn_cPMmM__SFLuQL^E7Ds++7#cNMil zr?ZCkG(H&*OPb0m>B{#|>TjsAGie;%rHiAtFfJ#CmHh-MlE-^uL9{3I5{Sy<$DwS5#ib4}SFy0vWG0fR zpKYjoGMQ47NPXyBq#xL~BCg7aZuFBm6nxU#H7vaqvm4L&6cUc_MK0oQCpo)S4+Q1pqWx+{*my}@%qAL6#>7foVf4V@0f>F+X{9J;)y4+=eCga>TEyKsHS3Jnv08^|8tQZx7aWH`P)v~u4VzVs&k>rW`w(Z3j0R{3d_6riI!U9 z?oBrw70ObV*4dCqwJiSEc2g)MEEJF?`)nW3@SOIs9a{xU^qnmmR*G?AJm<&Dq|RZ_`auRnm?9GD=SivXVFwn;bMbK2)?g4 z4mN1?02-|668d>(8M6F9XTV zdzK7~xU~2vSI=|4guG$G#_-IpB>d-&+8N{#Pk+UY=`+_)g;9J#+Q>Zmxo@m~Tz`GF z@&$mIf}MIBN)Ixxp0&bt*%(W^q28mhJkZY+e4@rfZc+t?LAlOECWnXmQmlARz|wbi zyTgV;rmU?t=$O4)%tNe;1ttDms(SGnlaDAq^t?Scs0N--g1~8)? zOh0x)8!p|pj*d0m&cYqCK6oBwT9)<_{S|48|Gb$%%!lE+g##yJXQPox*S4 zmTCDoEWDUmOMKF&G$AldCYJ{*61ZeA6}p}_??}P>LR`aSD;@qdeaYy|(7M@`q@@00 z)cHi_ijzx8uCbOMuZ~-phu6595@UwH!*==^`w-v!PgAJkwEsc>2d;(Cmbq9{V&H^El?*I3V>gFL4?Ya%=^vA^xN2$g*_C zgDtU}T~)EGj|*MfWW92p@6ND@qI*EMShOWf>A;ZF&ImT&&Ph1)ngG;HwMt}z zV0*sb{M9SgJY0q{Fp$DHbX{c{yEW8ZsrpvWpVLNZalK;LOjcDeRu3QvNk2d1K~=M4 z9;%8@Q`xro>_{1wQ^&HzdqCJp>14?2{cVpy-TLx zD!G0box~QRnXt_nhd+z!Ip6o}I&OdO6A1uGovJHHjB{eJ`0AHef&MTjS-Q5BV*9h? zIn?HM30UmYD6=<#*UBhn$3+d8!Lm5gzNu)nn+p3kxVJQ>JB$8;NNP0XMpb+`TxaZ) zvO|5)So!+sM%CIGS>aLl6Hyxwu~&>4;eHB%IadWCaCES$m9^kV<_FP7*I;zBMJ7mxy%!jNo9RWO$NP4+3lmx_=6UMxKebcFQO{xQab2j+SK^U@nlcF&>= zQ5zS-!qR>=|Fx&wq-!~tWP|nAu2c}kt!Fct=GD7RZt*g+LmvAn+VCin7!2 z<2bu+HlKr&!GwpM6BFU0GI>Kq4SRh_HyxxIL6ndtlcF9p(|nU0Hbu>n-=t=BmQsx`E+`TWg*~@lHqbJkv%q!WdES`cO(u zs+B>?Xz|F^6P_FXHwEK*pgd#a33{OI4HLd)=lcYQU;5-7FxwrKWXcD9QkqQBOZ9%p zSD}Wr#&Iap)aWLmd(=BMCtF9MRx{1fy{ubPGliXO>dX{;kmiJd@5H`wQX2uKg-Nf? zkBQ2&pK*mgtg3esTLbUf&COiqWmYfjEYenDt?Hs;4PH8rr}}EFiDvPW>(Pmd9P(Yz!9)Q`%`rDCre#U@H=EkUi zbQVKwSpwruzZ%9^QeMXW{CBNF#sh;YOcCZvP8kgpM9md;61sMO z$$j805M}{v*u7o9h^u0ZnFO2z%R%Tu--hFYuy+!9FE~dj!^?Hf;3Zv7sxVhuh~VM8$Op)d6^&D%QTaVU59Q}6&|ba zG!y=RmEp!*&lU7?rx%%+%g>tY<;~8o1v1 z^Ud-KRce2et_-lqAZ+k$kR?2#!IgP=Bgo9#v%)}q#FISvbRqYBN4i7R3)rquZvahk zd#;Hkkvn{GDIH$S)6+SfAJM!!(~I2_ZB>OP$0cZwC~2S=n!+;uVvl}Ks#1FJ;_~lu zl0UCAuGp%}_v(b*iGbjJfo$~@*3qF=<8sHn>&|TKd2@Y@FS>gV-OR%(8(Ak=YCU5{ zZ*BC{r+&qb3DO2tO++saUN9D?Z@4A=()^+&!;((WyA@NaNSfAr=cD*M#gcVN_p_I7 zRpn!Or3VLe^?On{mZK2E7A`cAjnCWuNH-~r$hFma5_JGF0Me*-rGXJ@+qNhtpnho|T5`!(r2$XoGT^dhfJyDL#!eEyUon zW~))2LG7^6?Ddw)B*j@0c6{RKfJm%xfm%DD-U^A9@AIk;DmAHmg7jK-?aY~u?}x}`B2nbvfERDYZCmkjGW5jgG0Ynz5)Mmymb{6uhms=Ocl^~b(nT&e*7)p5tMR1DF2`&^ zYx&UXSm?NWTZ#G^ZJDF>n)F{CqR*_StkzPPpgW8t#eQtioY-{&$eN)bY%DU_zleV6 zq6P_Wx|eC=1!`ll99p1}o?C2Sln?zv0I{`ni3@Ids`xU4CoTF#k%#TiMHqHfidX!F=Bk3V9iP=G z(^4hHCRYZfDcOuhP^s5VFurq^m{-K^bp%t5?Zp~WEgGG%@cf(|_v-Ts2UI{$%@Tkz zPmqFu3+EEI;KoynuesJd)siL6w-qkh4%2YmZ|uAplwG85{HkHcelmA1_aRDYB7!=2 zgF49Q2Ang+PC2(ZW4|kBpJ0gwx^i+)t3z_OnyxA`o4cKu#LsN(pEbbvNl=XsO*Ql~ z^Q7j~yT%L}JoU0-r_f%4GfJSG5K*0othakJ(+)`^^IlJv(trj(Fzz$CNMqxcDVE1V z$$_uRC7qj|2wE>S*$kK2z+EB!lDCs+8Qo>2dSxWp)(MK1(2AyjAsbb`nZ@&h4cA0>$St zS5&`jWkPk}wi;8K(k|B42GFW5#~;00%sI#Fp@Bc!St)S6QWdP%kfH2}Y!#@AOyy@i z_7u$n(Rb0e;X`^!qh4!b38`+kEyP0AmssD1W!cw%C=l2a`dxkVQbfKu>Urh-aC0JOkP&Z!;ysQw$)W%OHQR zgzN57dgl~x_=0ndx&>!^x`+tSv5T%q5%>%_tc)3OW$Q#X*%X%h`Sr8KVHjDyhxNEZ zf0u-WLdx+c(o`;7xGCpRGpymXi(PzUu}-^|zE$=~vtB_Y(AVtA_KwQ*l)mpMwiE#e zo3MkLyp5GTt}fI_xh-fqQbo!ugpST?mM3XmstD_s&`-cPWPar9U@naA1!{nRMO4CQ z32%8Iz`H*rOSIq$|A0YKoKLEUUwZCRG`fWMSvc-X2i2h<+Sl?+EY(w?h(?6Btw(IC zW&XZ?*!YEd-(@ipA7?!hrw;b(jOOsVp2p++@Sp1Gccf4qhYOtF(RwFNA`eD8lF>2j?aI#v5R&w&IdKG=(+9~%xbgyq6v;YK74VJ{;s7zHF}yV=DuDk_vcFC+kI zT=;NH&SPkMLSx^s(rPnXe%gzXre#>svPmoEdO87Hutc?Z*E|Wf8z=YiGYHK7jVLfs zl-2CLA@R!z+^4*E*`#nB)sQ7{+?$r9nd#i`W9jwbdh4S6Q_>ot-SR)DgC<~|sTvjT zDSq6S&)Om0U3>Z)D=d{3X;pZb0uT60jXED^8e-Zey2uRu zQn@{gKq6fyIHFueu12B>(lqo}j5Y>B@hT-C3O(j8?ZSTsq2TCO^alQ>y0+Qr%8<~>QJ(41raH&Z-B@ug!P$7BzzH=6$b z1@aG5Clpv=lH?D4+-Z?Ni(bGa9RN|DVdS>HO?-sWleTHxBBn`6lGDa_lkzV#0JHL>Vph}`-+*#Y(b5nf(N?CQWzzB_gq#^a-Sr)qi&V3$E zudZEtAfPl{2OG_Nd!w`)y7xL96#bkhzo*3KRGBAS2Wsi>Uf)sK3vc8_AMJEhz24?n z(i>nsF_X@GqKEWRco1ad8+-zf1OE%CYb%vjJAI z)+M}V()Y>R<$ikO>-0_IiE6Z3g^{UU=^E8p8QTEmmHrwduiYvyv|`c4L&BAD9Qm15 ze2~c`PLhJ<^l9DYj*3p$mxsg=io*BS%L+E#5>NGWV22zRW*(WedfFW>kzWClpA#i~ zxFvA@ss&mXi_nhRy+w+4Rb0dpYt1~L)_LkS!J^?x#^hsxT77Iu&D7-2S4m$~(g6cg z#GV5(5}E2h-_~V7$Fdt1$mTd8O_CvL(>wmm{`VX4;pA2HN1|j)93)Dwabtes#`3Cp zk4G5;x4&F|U-ab7uQQp2q*hew=FlLrLGWWOIF=0_95$hdj zHHk65s6p31Ek325Q5LwP)<>T{tsP5w#>1~?@3kd}OPA3L^<)k~I+?b?HGEMmZ>MVf zbJBguWvk+X(vKS;za?bH%chGWdR`I)_6C%AI_$;0tpx2dDw)&v>*en(KPlou*G=NI z8Nhja7JDwfFj{xcMW6l|G^pc4oU=)^9Tmf7cPx^*o#Jm^R%HY1Vynx{cyFyoHBC|w z7v&(C=BKL+oj#cYHH*jWr2@0W%}NGp03|1Vy$Xe5?Oif37^n8rj;@}UK0{$Ey*Ak0 zHP7(``Dmj@J(QSEiHj}}j;W~D%=w}FG)K#O%(_}ffu@}(+VB?B^v7rmh({znkZ92a zMrOTw`93mYrGAYKkcYiaG+s@roN6DMoO$b#NL2YkT{M*SwVeb9-=*DtHMhVV<2eOQxTT-Y*YE7FJbL}qV^D&wYR_x}6>6w;N7ECqV# zH0u258A?ogD)3!Exu=tM#kbar8v@aeV}tjRIKmaXHnoN-u*@`09M>@x{gz8B{FF;3 zr0CEu^wr1r7NKEwLNW5$o9K;=-GBYG-z~OZa^Q=q6w9+&01T4mV}I&JG+H?(#k)sq zh!FqxsAgIr+Fq6IOodp;noi3euezb>pDsBa06pyTFZRE5HgK}-Z=fu{;3)`PsCv3T zPR2&FFG=$`b1n&{U62TaC>JgC4(N6 zsFCHa%AR|703R(lYGuG>=OwFBz4xa%8S}qWW=?l=Ht__%3jh9bd9us)tzyD0(Jkce zl3=qp_?kbtCRK+p=lTIkc=d7BD!i(-^JGb%v)z3*AUqo-<@tx^hIX8UN+cCW;cj#rD*BY(< z>@PgKON(2Lr{WR}Ip`g7E)GL$$=W1psmM$>!S_D?Y4FYtHPFHqY6)7!`+XBZ%|X6% zVJ_np!nM~O=6q8PoV5>8dN=76G{_$<8!1fS>4N&1_lCD{|Hsv@F#Gz*D$jfT1K}jE zR|q4#=AhNI4>u9PA)3t?`Gt`{*MuJe@~79j@G1L8I8_pkBV>Td=~%z1eMG0Cm*UDE zJr(AQW8Ve63CV;`06?$%6NXCqDX#6wT5+tfKi8hU&dOMjxLo583A)MwyB)}M1-FK# zi>sWy&VF5x$7YB>vXGeRpVD4 z<|>7Ci}POd7JkjVeE?ncOX!$?caK+h8}H2`+Gln}=E3krfQt8$r|~zW4+JdYa^NSQ7jSHH zx86XxDRCS%SIhoO8^>v-JP@&>eo~V3TdVuIU5ih1u%yi}@ruio%}Q;;D_ggMu8X;j z_jV9Il&f9oB$g`!Iz)SyZf>a!7t#E`g~F=GGj z;U|Mre<;P>My0rc*6LW+8GEB0+wtR)3c8Liaa?MTN&!|#)c&CuHn z*zNp)@?nPPK+gTu%iPVrcrIR^_V*|g8Yc%zwfB-ADQdC?UUxxk_YUbsX#=3E7mx0p zXtW_e5S=47soofh*hgmVxm~$%asWP0!sW*+>oEIq0?L@I0Xjd+<{2g!DV^lZh@pqg z_hbKTp5!xN3mQ)umXvtn8JpIPTY2-&!}Z+a6`v(kw62m>?{Xb&z|j1vFFV=rCx87a zEU!bK_#mXZlfBR{%<`4|RS{G+7As=8Tqcb|m8=y=BExz{v zBkJi7BX+!tUt8ZLj=7Az;a&E?laUtSb_m2yn||8zQ!7JCDbK#;M;PV2#0K3?qX8;(b_&-8 zGzQQ(Oc#!-aRararyWx@Z5KuyA2lssm6Y6F9flN0UU8k@j^O}*-5R}Fu32fr+mnzy zpM+b#0Pkk7vWXOznV*-lYQmbes3@;SGVh%|3ei=vzikJyUsA+)8|l;bh4Jg0=E$(X%P_R zMTS2UCxQEjt-x0#NLsAL{gL7KPf1!%B?yq=>5`;y0?8P7s1ymjT5=fOGP5X z%VbF4oH9XhUOAGMP8nZhc#Iq=TvkpIZXizr&ynkaUn!8ZypWGWh6^i^w4f`3k>R|` zByd%wT@*o33!m~WCRB(XM6W~7=SmL|{?F)zs~tT=6a z?dL`>@E=$odV&AI2HJVL(IdFdYSSQ7@hO6N9PMF@Pok!Wjzwfzi|Hb1_D74 z|F=x=PYV!XA-Jm^8!3YS|7e3jU=etg9vf5$u{HnuMpQ@;v2p*B3H-HD|0@#_<^Q); zevmNe4?Pe+2(jV+`z=Ti{0|1>|6eW`BqaQYA{YdMd+3uq1^;_#V6dRzpV}hA2=D(d z6h8I1CpnIvV#c!6?y+_6(S<^2Uh{aDy$7i zphACajsX9kcnDg5>IsYd!Cw#z`nLnXf{3X4HzPsezfKC`f&SNK3xW9mpe_U!`2$!; z0Q?7tkO=>udZI#q5EO<8{tpHe!2v8R@JBdsy$ zez1VZfBC@M)B1(8ohP;geBBTWaTW0CxVd@L|9v^o^QpMnBhKzW6G=&IFK=s4Z+}la Tdu)ENkbnR-3yXrLBKH3S!VPG_ delta 11146 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Α^H3ghl_u$OrjDFbML8hJm~w{y+G5!GC@T zFNE(8t?~*$emnEO1@j>+^T!$xg7C%v@PYmW!^aQ$lTYxs_x^h?7%1=$Xz2g?9uN$M z{GkP~0RMk|56BMy{b6H%&>wz_&?*0)d;)-f14QT_2muQIAp`>Y!|4$^`16Y)f`C6j z3xIh4u)6>ssl Vxmj9aBZ3Lcht0$!qbiI2{{W*iFtGpt diff --git a/thesis-evaluation/figures/timeInCircuitCollectionStandalone.pdf b/thesis-evaluation/figures/timeInCircuitCollectionStandalone.pdf index 0ac73d450b31e2674ff06435be0939230c92b7c3..9712302ecacaa51f36b6dd8c07d5d2269d712b9e 100644 GIT binary patch delta 7279 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 LetrctMe_dxoI$2W delta 7286 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 diff --git a/thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf b/thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf index 92d1059ee7f8a387b498884554c5c2c2862fb5e0..ffd85b5141b0a5e054f7c5a7309cf4afb46d66cd 100644 GIT binary patch delta 6119 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 delta 6299 zcmZW~cR1DY-@cudGPASyINLdr6bIRkm67aCDCJN-WrSmI$toR=5spn{Wbc_xc2+{6 zh+p5|^E}scJ$(N7T%YT8-LLz8-}n3Vx;~#$No!L{O9x2-Q1~ZPQQa?slDhNQ)7i~4 ztpDMk<7@LCXM3MlYEK^@t}j++?XLB$R{eWFd^2zPb>L0JXWboHVpYQmi)(w#&QZs= z;qMv)!Y$voV$OVOPab1WFV8${>9DkrY2JZH$ApwtvoC{}EczT`2dCx? z{qyHSJ{-^fX1}ua{K`$g`OdVNOwA`92`JS3Nb(Q4YZ_AKSNsbqLq5&kXFAQaAOviB z=_-o;ew@1Wz5zyIFlpSc-=3%II%~Vd=O!5AE*n-s!3%sO*L7{^&J)$xnaMQk8hXeX z{S?X@l^*#t<=#Pjz3Z65o5Op4o|54Kk@FZgQdfw`2M#}*xtV$K~xJ>taL<(nPJWWK+Ao{HPihvlcb zcpW){4_1MeCS!?GavtAbsq>!@UV3X*&D*^60+xtBj|dA^X+)}cwNWnYWg1OBwn1&` zJ9GCBtnFS6w#>xI3dJqba;A7^t0I4>J9R6jInMGj$IncmRav_7-I3e+6*j`E1Efbl2i&{Ppvqr6h+`bYVP)fhXGn~uZ-CII|PIub@)+~ zZk9`v7{;5XV05+X*BX*Z*S`nmbDLAj*$!^l`ShG*23gN=D%d#K-U{Vp(19L_liEd7 zdiznPe6Y2lPz51I!69FjhF;Z-ntrnvv2I9H}lnw*kfR++6ANN>Do^0717W zgk2{Vn7-1L4n^FH)BR;jMPh!4K5`{BZ^%x_UbZ#mqgmZVJc=HBa?LFWYae zRy9&um4mcKZz?^zjUrKA#8^csMqM#GHQBV1z zjGt`>_j&-!YHVo`Yw|eXM8KGFLP)D3y%bthWn%?Kd(y*A@bM4>3O#)Bht=%cdSWi6 z+Ph)Y*X1PVcX(iYk}y&KbSeP~%Z}mLuFju?*MxMP@JBrJ%W6c73|s=}u7g;KPM^IBist>;|ih?zwBh46J8W6RF1H(1pQ=Du zuZ9TLXo`k3FKp07Uc4C4@zQ4Sji*ST5_dGo!5V*OV@!x@c*jQV3lohX@gYB`=VdSY zpL-t>-z`|0=GH1oFX1pU2bZP_AVXZ*g~fWJm_~Nx(Z;JCTsx=p&9_2>Hieo|hU6Z% z=YDpR>A0tOF)lJ_HvNvSBk&AZnuCY&pZg?v+NQRqC;)m^Zd#{}ov>|gS47~1`b|N^ zzyT8~!(EIwOWrs*sm`2*4c-z)J_4shRttCPZH?T^7^=0a^CJC4lBv~?=zpPv_$1*U zcc;H_VlOSUelfCM;$v`=kWI_V`@YUUxL4B*W=+LyQWejag5MJE+4Qg(f`rmh{&O<-gwecO>{8@)OiNiHcO z9@kYk+ERP>WiPd!VldyhMcb6IekGjTz3t1-e4uFKn+Q2Bu?!EcG&+e|yd|REs&6PE zb0;U_<;d>0EHw8_Kgu~s%e9pzrkn5+>|D<^%Wg=sMmOy8MM8q|TSif5d?yH%? z1*vjd9f?mENIIPke zy#>jFgNtnyXWZ7+osH3S6OEm;v1&F#_^cHVd-~?My>_z zV60s~aHCPZV{-o0%{-4fRwv&7m3Q?7X9o4zWJDQGB$&#QE*q851;2R67Hs^GA7C-b zs}Z^MtU4T76Qo|%tPrshj!iylXVGV=fHv=#}J znx;CTY@x+fi(glt?0X;I+UN-Q)>?4=0mJ7*+119tggQ9b^s`k3g+|IHgc8QZS2-yPj+v%vaFWHSFuJJ6&X+_=uy;D*eiU8G1-D^d;WbFuH8i0^IJ+VE=Pj1|t6@!2TwOFy;5;ZRG0` zYBJoFTJuHaBGI5}uj(x!(THvNWQ>w(e8TS`@xl0k{FStYyN5;ucUD7l=0RgS;`m+3 z{H}B|OalCVfyf~xshtI(OF%IQDa|NH!}i7=l1|FR;{x|C|EFHlzR&VZI$E58d9{XKR#8dS0;3j?WKY$MvhGZ~ zO*#roH zODbO$cCBY{99>)bRmO*}_mVnPPjsDaA++eQi^>J5_J!`hy-Z`iM8}!s4UQ^O+Rrf< zu`iJzF{dwJgD%!b%9P}HYR*j%y?a4gsw}Xzhp*O5w0!LsRl}3T%J;p$aEPaEz2BiV zkg1q~01?eL+n%c*IHisJpWb^Ep5%DF;W2ON6*E_4C^WF5NZhU2R)=8gtp3xI3W0uE z8FWL{=@8~jJWQ(_RNh5L#Z2fG#^L)$4A$Y9c~Wj(gjchu<0tn!L%& zqw38x0wF-TRlTvZ5QRe$4v)J$DC1;~;I8mHtMo3u0e4@CLOc>(cR<*W7IUGGrFn#% z0dzbn`(7C8y?*0D7$tqJmyoj<%3{Y#y9QhZX=O;{C%p!rJnR_l=5JHsEe4QL7a#WkGavW8qJpxvw@fe~r@x0} zpZxK%Fn0NMmbg!uPNw24IDjxOvxptn) zbEBMa9bF$6aMAO>o(; zwD^rBLByy_g~$HS#Mr2{d^vM&AP?4;IxSIuKyuB!E4|JrnuAO@_y^`dtg6&b;3~6R zL3Mg;;79f`^AGO@BZwar>SiS5uRlnN`;s%(&@o2hXm1^U>=(hF@uVtw(@FIx+3c-N zxJF^dU!0Pu*>^Td4V7C3=$z+tfOL#s+`9dC5OUV5*kk$)AtAwY0w_29@b zH&{rnCobIu=YW3e^`_svEL%jN)@X~OlKExQt5dh3N+Ehfvgh`4y!@w*d`Ly`hyv|H z!63~o)eeqC-4}ZD)#1`UTV>H={$H3kb2AblWSBICK+QyD;j%tXl8$pbp zb6%<05yo%}Cl$DbEv&{68xXqjB~o67Jo3sQW6E-zqd=OcCx#kkA4^8BDNi=!m4l0! zV~}`d_(4kN2_~&gVr_1)AWur#$4Y$oSw;8JH+9kyqh@@I8~m8xBmtha#NqYqS%Zw> ztzgf+NS~>X%6FYSD;gHpRzE1pjFS}!Y$-{_#u(3Wyxse^GjacR2H5zwH1DdAh64_x zSPtue7cupAF*hYgB0n#O=ozrV?=&udX1;D91Dt9rd{TPuG(X|HGClI_H~ z$_2;|VkX$IF$SLm{@xpOCN=w5N(F;EW--9h-}Sb+(<1k6Zv!cmPTOZ3gQINEu4;~ZnWJS*Pg!PRa{gTyRd)NMCZ(Kh75NtXZ{%p0qFm5g z!hPn^eHyHh?nz+w%x=KSNzd%@}D2laySnh?Iu8(8G#V(+l4-qW=adMmE8c6 zBS%*)o`CxNmz!M;yIm(B66c8ViN;7P@HqAD_77#1Tof*+^nu)-bo&ofO9@&|bI97M zk8i?rRtNaL*>_N$^iZ?eQ}L@itIc``DfL|uyxK*1{56HKVnASU-q!x=kCN&-G|7Q} zbm%j;qjkoFp*O%Jd(|!8#%>DvdC4q1*> ziqJr=-JzYIEm4WzdhB;<+~j`S>2`>IeojWtC`SM34`+zSyt0pU^jTGj;_1QC$IDNg zWf!$n@Om1vpZtzbe;tf;JgB}t7vQ`9{a1O3?0^ckdpu_05g-ek#Z))C&o_KJK00l@ z72tO~Jsoph(H%HF&THgTOM$8lG~VqvUU}$H5dEMBk(Rxe+aC+|{Fw4({dQPZFyVf{ z8?)A8aTT@u<+>bweXAsv&g)N;)CT;)BN1(d zxTy(lUj@iTp_AFLT+(|JZI#}G6ryR?6$bRlJi=(zIQE4ApVOZNhOsnn!cYr9WC zpUm{6c}|8_-72$lESoH@UY48k>|l>Ii?Vv&w)XESEvX9j>isR^WLKd>CKX0gMYM!{ z_kGu1QYMi6>T9=w*0QjM#2@)#e)^eqzUcA zy<6q(C)tmW(sDZqN-#;A9^U_h;o0BY&TIViH)@S$r>EzPjDx8_CSgc{Il+{TjucXE z&n5`I1ciVg2skn!MokBR@qzyH@Okjj{{IiK91?`MSc4+Z*N`9x^kNNmf$tw3{2cC| z(ErpDEY$hPpooM3brAr39tTE1!58=tV36E7J}?}Pgq*L*K@sP1Ah;a-yf+XWh59ca z!2gB-Lj0pVM+ic|kmn795J=Po5jha_La!X`f;o_!9Q6Mj8u(BCa!9!ggh&wheEJ|H z^g{X|Bm#Cp7zGAjI5Y|h`9H${(M2I(7e)buLV`i(QUHS>kPBDx&+tVY40REQfT1q% z$szvR9Pqz02nL}p0KpJB@cCvi9Ch9a7>tCRPYVozTu2KHfuEbLfBAzU2*d?p$iEoQ z3qv92n*a4e5s(Ww80aDn1_oV71_lL#^v;XGh%S0 diff --git a/thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf b/thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf index 7f336193dfdc049361d2ecc3151fce57b60541a6..2f748a072f3d2da6ed34be2282648ae5f849beba 100644 GIT binary patch delta 6557 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 diff --git a/thesis-evaluation/figures/timePerSingleQubitDecomposition.pdf b/thesis-evaluation/figures/timePerSingleQubitDecomposition.pdf index d1eb956b32612a482b65911cfc2eda4c3d3409db..36f127cd15c548bbcd7b0947a9cad3c73767f4dd 100644 GIT binary patch delta 6313 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%0ACQKGBy}4zIRJt3D^Fju*4mg zFN`dlN+f%XcnY;~i6Z+2$#$Q7#dW9QoYN!cq$QSHO}SPrUrp7ma@ZMEe|M*u+fXdV zPFd>MLc7{1-Ae%a)@$4cHB0N*UjMX^e@KWtd)UTK2TOi@RoB;%^9|k6{m%2q-QJ*Z ziBoT!3a4c`GOZ^I$sF*u%@y@Lu@Yx8?5a+7yX=-aVpmM`RtCy4W#2XXb4NN6JQu1h zfqr%et=&K=#cF>CVnDoXy{Y3tRV3>O<$2%f(PD4B_wqr^;!8m^OY99%_z!yKsTE*3NRFEn68r#5p@=_$Hpgfh5|2l14TQyZUr>Q= zF&Vv*@kX}LUd5z#_d8Cv8BZ%jUe(II$h~?d)`=V1$#p){ELuAal8i%)YFutnq&nLR zoRQjYGVRpZi+4t|NY&B3$c8InZgXV`oa#;*rS8D!1kR5!;Z4P;@HvZu(Y zXwTR0&f}(Ba0*K+8#{0AOk1njeTGuIg8HAGrc3VGc4+g^FlP*oj>+lL3~zp(Cl`I% z`T8#@7Iv5FR0ippI*?4EyHJAuLJ9gmO87^3-i@r3lU|TXO?T%8@5f&@@Wo-R$Lq+ zhk~uv>172Pc^GrK*pv=GMJ*YhSc-NmBJLr0kS0~DdVX%-QapC`^0KU7&}V&SVJlkI zc91)FAyI21^%0%`r8zE7SwJ4_aVFJ1rcuO+6fBq6e^^8syJ$Dq%-!})GmZaqPJ}#I z(O�r-5p)4+t8h-MYTdD~(xcE%m-#`?x$XMM9q4=~vADLKKq6SW>s2&-Gq+mJ%Yr zUUH*QUVnz3$kZMQGx2d=K~1`jeByH*j}}ev?HiHI;pg+IsRvEHNAJ<`r!+tG``N<> z>S3MPreerQzCQ96lfRmg3N%zEiD$F3?eC9@U?7jnXrlETT1qK>SpPJb&%t6G#p@3}OjO&Zd8mah(x>@F#Iu>$ap9NYmpF z2}e)Aaf%N$JRQ?kSmmw`n(cY^fw`y+-D}dvit~M7tNTfeJ~ssqxQSfS3_{g12WQV{hDnirQFiqm*1A%Dq{fx6Am1+@Jd_M@W+ zSmU*g;2jB#42D7d8*e8oWS>-q^$iuTF6*O9wxY!x*7%9w@$d_q<<(OMyBpKhk!yRp zaxa{DCjd@sRd|Zth-Bgp&6Mf`&76_Ffsb~~a^Lla-BK<5n-z`!vnaB&G)@us?$qyR z+pk@;f`Nh@;`FU_(@X%*g00YI!}N*aFU4D}c|zGp{;2A$GX;;s%1PH+%(SLaD(r@` zi0a1oWc}FlHd&M$to0rgE3o^BRnd{GZt}Rlc6)Z(^qk!Fu=WMQ;rVF;0(0tijCfrb5cv2eE(AX%tl-iXh)fl9kjULYRHRdP<d5;Pt4U+nOJ1pMHD0>tQlwCg6zkAPKorT&&oV_ zdS;pLbN@AI0+o_9lP5GY3G^;IiKM`cQdDRrK-3@A^=dWS3)!ow&Qc!`Yv*I;a=hYM z>Y2rmLG`Y2MI6)2k68W~oV4nA;n2N9T{JHSeb=~N*Zw=pCNFz*{l~I0*G!6DAXTTN z--tW%!0IUNZu-Q!2*xI#V?{2$^swn&Tl%g3ZSFbEnBUb)_>E<&D@CW9(fup}i7F=+ z#_eMM9?04JBgeVrKHn!#PK}>^l4r$vE=3KgbeY9&G|{BaB3K04jUOXDSte_MKQOFQ z2%z^$y3JS7!?H)DeL__uzYpf-Ray$7rZJ_NSZ9N8Ee}|s$I)?g7zW8*emIMhWx!3z zIq$W1z=^N)A+gDB4)6elZ^Doo#*Eq~R!NPPY5egbm(8Z?3!ljG=j%ddcf5|VyCYWb z*cyE)HqV28A(GWf-k>{X)Vft>{EyZ%l4_HDMV=g0l;UQWOK zHGkUw?)=B9*`w3n-&Zj%h?7~fvsbM3Z>9EsH^3zm&Jg78tyQr3k(KxgPb^gkwb{LD2kSZZO*KlM>Maeww3?4dG`?AjUqC;qpd@G~TZ;qTw zwdofb&K)86Mn4O?!D6;!r=2gdK+zq3YF{i9vBy1INWdG5w5C5&i*W3wBO?n-OAGP9 zJ+5fq4Z>O=@f2;bFN!8}Tw`mH-D@vkJpLpz z!OOo(6@MN;bl_LDhgTit-0sgJ5xG)r5GSZ|glP)Mb2=JwEtNbSRVG;#AjLj`uC$2v zd|mvUg?r{ugrXc}z-eMruHElB*?Q^@e}<}OW&(ZEF_IwsKDVFZ(fLgCS@PJ>5s!%( zdL?XJYin1{c4%%%;PZpA_nz=;N`)^_BZoO`irU|Q&g(o0zskU&)I6b}=aq^v0dNVD zeBd~dKMyQ48A;eho-{6J1PhD)KQ@T?C7g(u$YqV6D41z%Z*bsju z3jm9X{u>YghKT(K_%}`r00jRVPz(gRG_jb-|Kke={w?0c{1-z^T=-x001#o3OOrsv zfR}LM0MI3z_(e_sB`6LOzLbc#2tw>K5G?#}$pPZxqW?Mr0m5Q`XYwZxAV3s&spJ3% z;9r73fH?TSB|-evYd~SKex?~M}MF& z_;TZe0GE0J2m)Phau8Vf{{;LK4;i}Cm@#tln7*i8(Lj7^LUH?Q%S%>n>rJO~j0 delta 22 ecmX>;i}Cm@#tln7*o}=0jLb|eHm~uR%>n>rW(XMo diff --git a/thesis-evaluation/figures/totalTouchedGates.pdf b/thesis-evaluation/figures/totalTouchedGates.pdf index 40abe74a65a4296a81454a38736c5765ce0e17ee..f005bb94b975ab09b9cde7dae1dbfe76c80961e6 100644 GIT binary patch delta 22 dcmeC0!q_#1al-^xb`wJ*V-sV8&C^|rSpZmd2QmNv delta 22 dcmeC0!q_#1al-^xc4H$0BQsO;&C^|rSpZm{2RZ-% diff --git a/thesis-evaluation/figures/totalTwoQubitDecompositions.pdf b/thesis-evaluation/figures/totalTwoQubitDecompositions.pdf index 93cb67960d5abfb58525ebbe5dd63b1d1d49e35f..132c83d709785feed4e975c9b1b6e1b915f3e938 100644 GIT binary patch delta 22 ecmex7h4J$g#tlzh*-Z?Mj7^LUH@|UhX8{0j!wACw delta 22 ecmex7h4J$g#tlzh*^P}1jLb|eH@|UhX8{0j^9alU diff --git a/thesis-evaluation/figures/twoQubitCreationTime.pdf b/thesis-evaluation/figures/twoQubitCreationTime.pdf index 3b9cb00a4e7a289dc6f4c0b8e3cca576ec119c85..31c0c2d4f187489e02a78ba2b6434d3488eca474 100644 GIT binary patch delta 10536 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 Lad5!3l!*Qhmb}9~ delta 10404 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 niK~nNOGrvwSx^!T{Tn(6hRhO)ekMu;U Date: Sat, 14 Feb 2026 12:53:52 +0100 Subject: [PATCH 227/237] update qiskit python results without Rust timing code --- thesis-evaluation/evaluate_cache_qiskit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thesis-evaluation/evaluate_cache_qiskit.json b/thesis-evaluation/evaluate_cache_qiskit.json index ce68f809d0..788554811d 100644 --- a/thesis-evaluation/evaluate_cache_qiskit.json +++ b/thesis-evaluation/evaluate_cache_qiskit.json @@ -1 +1 @@ -{"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 343.54330000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 295.21914999999996, "timeInCircuitCollection": 58.103300000000004}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 159.76119999999997, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 182.90365000000003, "timeInCircuitCollection": 79.48235}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 153.97265000000002, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 182.4087, "timeInCircuitCollection": 98.20005}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 185.03105000000002, "timeInCircuitCollection": 39.53504999999999}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 870.63575, "timeInTwoQubitDecomposition": 480.2716500000001, "subCircuitComplexityChange": -65.99999999999997, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 184.22195, "timeInCircuitCollection": 244.47424999999998}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 577.7537000000001, "timeInTwoQubitDecomposition": 132.86515, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 2.0000000000000004, "twoQubitCreationTime": 178.18085000000002, "timeInCircuitCollection": 165.95174999999998}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 177.3191, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 179.31825000000006, "timeInCircuitCollection": 115.12845}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 294.06924999999995, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 178.501, "timeInCircuitCollection": 52.96925}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 186.69230000000002, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 179.4108, "timeInCircuitCollection": 166.7919}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 175.7082, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 181.0885, "timeInCircuitCollection": 66.4998}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 132.24415000000002, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.81040000000002, "timeInCircuitCollection": 65.3237}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 130.79319999999998, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 179.72724999999997, "timeInCircuitCollection": 54.5683}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 134.99695, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 191.7011, "timeInCircuitCollection": 67.07975}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 135.29895, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 179.7218, "timeInCircuitCollection": 130.57875}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 295.01525000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 178.13099999999997, "timeInCircuitCollection": 51.236349999999995}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 179.29979999999998, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 179.60395000000003, "timeInCircuitCollection": 123.0643}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 293.50190000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 179.5504, "timeInCircuitCollection": 34.0895}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 291.05845000000005, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 179.12944999999993, "timeInCircuitCollection": 81.93225}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 288.86820000000006, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 184.63150000000002, "timeInCircuitCollection": 48.12319999999999}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 151.21535, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 179.45475000000002, "timeInCircuitCollection": 83.52945000000001}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 164.7773, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 177.9284, "timeInCircuitCollection": 67.98870000000001}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 155.93705, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.49224999999998, "timeInCircuitCollection": 78.1205}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 308.2328, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 177.05255, "timeInCircuitCollection": 71.12490000000001}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 293.10975, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 177.5605, "timeInCircuitCollection": 122.1058}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 285.43385, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 188.28925000000004, "timeInCircuitCollection": 32.772200000000005}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 293.1708, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 184.8516, "timeInCircuitCollection": 33.564150000000005}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 288.0396, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 181.51809999999995, "timeInCircuitCollection": 42.056}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 155.25050000000002, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 179.67045000000005, "timeInCircuitCollection": 66.23519999999999}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 132.66865, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.0279, "timeInCircuitCollection": 72.4269}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 289.3162, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 180.0993, "timeInCircuitCollection": 34.069}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 299.75894999999997, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 182.3752, "timeInCircuitCollection": 49.293499999999995}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 132.8356, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 180.22440000000003, "timeInCircuitCollection": 57.563050000000004}} \ No newline at end of file +{"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 264.43199999999996, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 199.43335000000002, "timeInCircuitCollection": 59.2014}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 83.80085, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 168.27904999999998, "timeInCircuitCollection": 93.10855}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 77.07875000000001, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.32835000000003, "timeInCircuitCollection": 98.72450000000002}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 177.21545000000003, "timeInCircuitCollection": 39.446400000000004}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 761.3143500000001, "timeInTwoQubitDecomposition": 243.00244999999998, "subCircuitComplexityChange": -65.99999999999997, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 165.56415000000004, "timeInCircuitCollection": 254.7787}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 523.38045, "timeInTwoQubitDecomposition": 76.15485, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 2.0000000000000004, "twoQubitCreationTime": 158.5612, "timeInCircuitCollection": 181.98405000000005}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 85.7912, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 159.42615, "timeInCircuitCollection": 122.65265}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 260.73065, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 167.69315, "timeInCircuitCollection": 53.40375000000001}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 85.31580000000001, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 161.68800000000002, "timeInCircuitCollection": 189.5436}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 79.58419999999998, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 161.73495000000005, "timeInCircuitCollection": 67.783}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 80.73055000000001, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 159.6042, "timeInCircuitCollection": 65.9371}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 73.37, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 167.34759999999997, "timeInCircuitCollection": 48.5079}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 79.67949999999999, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 168.67844999999997, "timeInCircuitCollection": 65.31615}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 78.5832, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 169.59955000000005, "timeInCircuitCollection": 123.81310000000002}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 267.62335, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 162.4891, "timeInCircuitCollection": 52.226400000000005}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 97.20599999999999, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 170.1434, "timeInCircuitCollection": 124.39940000000001}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 258.92439999999993, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 160.23515, "timeInCircuitCollection": 35.643}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 262.54715, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 167.28905, "timeInCircuitCollection": 85.82059999999998}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 257.01705, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 172.08350000000002, "timeInCircuitCollection": 55.94029999999998}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 79.3452, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 172.99985000000004, "timeInCircuitCollection": 96.4836}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 88.747, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 167.66315, "timeInCircuitCollection": 71.0322}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 80.0976, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 161.01495, "timeInCircuitCollection": 81.10095}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 257.62100000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 165.66219999999998, "timeInCircuitCollection": 73.962}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 253.68435, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 167.87064999999996, "timeInCircuitCollection": 124.00604999999999}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 256.0220500000001, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 167.6567, "timeInCircuitCollection": 33.93079999999999}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 254.33225000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 169.9614, "timeInCircuitCollection": 34.624249999999996}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 245.74995000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 168.23465, "timeInCircuitCollection": 42.5586}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 80.17750000000001, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 165.7668, "timeInCircuitCollection": 65.5954}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 74.43695, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 159.21775000000002, "timeInCircuitCollection": 72.15804999999999}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 260.89835, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 158.89270000000002, "timeInCircuitCollection": 35.25915}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 257.26304999999996, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 163.44439999999997, "timeInCircuitCollection": 48.8252}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 80.70155, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 161.03295000000003, "timeInCircuitCollection": 57.965450000000004}} \ No newline at end of file From a3c68bff431ae39743df3e6f3d9cb57f82e9fdf6 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 14 Feb 2026 12:54:58 +0100 Subject: [PATCH 228/237] add Qiskit copyright notice to decomposition classes --- .../mlir/Passes/Decomposition/BasisDecomposer.h | 12 ++++++++++++ .../mlir/Passes/Decomposition/EulerDecomposition.h | 8 ++++---- .../mlir/Passes/Decomposition/WeylDecomposition.h | 12 ++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h index 8bb44a1a6e..dff506e749 100644 --- a/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h +++ b/mlir/include/mlir/Passes/Decomposition/BasisDecomposer.h @@ -28,6 +28,18 @@ namespace mlir::qco::decomposition { /** * Decomposer that must be initialized with a two-qubit basis gate that will * be used to generate a circuit equivalent to a canonical gate (RXX+RYY+RZZ). + * + * @note Adapted from TwoQubitBasisDecomposer in the IBM Qiskit framework. + * (C) Copyright IBM 2023 + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root + * directory of this source tree or at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice + * indicating that they have been altered from the originals. */ class TwoQubitBasisDecomposer { public: diff --git a/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h index 9250cb7f4c..f09009685e 100644 --- a/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/EulerDecomposition.h @@ -59,13 +59,13 @@ class EulerDecomposition { * @note Adapted from circuit_kak() in the IBM Qiskit framework. * (C) Copyright IBM 2022 * - * This code is licensed under the Apache License, Version 2.0. You - * may obtain a copy of this license in the LICENSE.txt file in the root + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root * directory of this source tree or at * http://www.apache.org/licenses/LICENSE-2.0. * - * Any modifications or derivative works of this code must retain - * this copyright notice, and modified files need to carry a notice + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice * indicating that they have been altered from the originals. */ [[nodiscard]] static OneQubitGateSequence diff --git a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h index 55e7ecc935..567fa9c35a 100644 --- a/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h +++ b/mlir/include/mlir/Passes/Decomposition/WeylDecomposition.h @@ -33,6 +33,18 @@ constexpr double SANITY_CHECK_PRECISION = 1e-12; * three parameters for a canonical gate (a, b, c). The matrices can then be * decomposed using a single-qubit decomposition into e.g. rotation gates and * the canonical gate is RXX(-2 * a), RYY(-2 * b), RZZ (-2 * c). + * + * @note Adapted from TwoQubitWeylDecomposition in the IBM Qiskit framework. + * (C) Copyright IBM 2023 + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root + * directory of this source tree or at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice + * indicating that they have been altered from the originals. */ class TwoQubitWeylDecomposition { public: From 97c11874e104e0ddee18c8a00af4513185f27bbe Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 14 Feb 2026 13:45:57 +0100 Subject: [PATCH 229/237] add XZX euler basis to mqt decomposition --- mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index eea288b437..890855b170 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -801,6 +801,7 @@ void populateGateDecompositionPatterns( basisGates.push_back({.type = qc::X, .parameter = {}, .qubitId = {1, 0}}); eulerBases.push_back(GateDecompositionPattern::EulerBasis::ZYZ); eulerBases.push_back(GateDecompositionPattern::EulerBasis::XYX); + eulerBases.push_back(GateDecompositionPattern::EulerBasis::XZX); eulerBases.push_back(GateDecompositionPattern::EulerBasis::ZXZ); patterns.add( patterns.getContext(), basisGates, eulerBases, false, true, From 0b9a10d6c3d99c7138878e549d3dda205acc691d Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sat, 14 Feb 2026 13:47:46 +0100 Subject: [PATCH 230/237] update evaluation results for mqt and figures --- thesis-evaluation/evaluate_cache_mqt.json | 2 +- .../figures/numberOfTwoQubitCreations.pdf | Bin 16590 -> 16590 bytes .../figures/subCircuitComplexityChange.pdf | Bin 20621 -> 20621 bytes .../successfulSingleQubitDecompositions.pdf | Bin 18978 -> 18978 bytes .../successfulTwoQubitDecompositions.pdf | Bin 18650 -> 18650 bytes .../figures/timeInCircuitCollection.pdf | Bin 22764 -> 22702 bytes .../timeInCircuitCollectionStandalone.pdf | Bin 18614 -> 18580 bytes .../timeInSingleQubitDecomposition.pdf | Bin 19518 -> 19596 bytes .../figures/timeInTwoQubitDecomposition.pdf | Bin 18716 -> 18681 bytes .../timePerSingleQubitDecomposition.pdf | Bin 18063 -> 18251 bytes .../figures/timePerTwoQubitDecomposition.pdf | Bin 17800 -> 17825 bytes .../figures/totalCircuitCollections.pdf | Bin 17485 -> 17485 bytes .../totalSingleQubitDecompositions.pdf | Bin 19783 -> 19783 bytes .../figures/totalTouchedGates.pdf | Bin 18954 -> 18954 bytes .../figures/totalTwoQubitDecompositions.pdf | Bin 19059 -> 19059 bytes .../figures/twoQubitCreationTime.pdf | Bin 23498 -> 23629 bytes 16 files changed, 1 insertion(+), 1 deletion(-) diff --git a/thesis-evaluation/evaluate_cache_mqt.json b/thesis-evaluation/evaluate_cache_mqt.json index 51525d60e1..62a646af57 100644 --- a/thesis-evaluation/evaluate_cache_mqt.json +++ b/thesis-evaluation/evaluate_cache_mqt.json @@ -1 +1 @@ -{"modular_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.3000000000000005, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 50.2}, "bmw_quark_cardinality_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.4000000000000004, "timeInSingleQubitDecomposition": 1.8500000000000003, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 13.999999999999995, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 11.999999999999996, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 46.5}, "draper_qft_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.0000000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 12.249999999999996, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 43.5}, "vqe_two_local_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.299999999999998, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 185.75000000000003, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 37.14999999999999}, "wstate_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -8.000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.2000000000000006, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 22.750000000000007, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.9}, "bmw_quark_copula_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.2000000000000006, "timeInSingleQubitDecomposition": 2.150000000000001, "timeInTwoQubitDecomposition": 14.449999999999996, "totalCircuitCollections": 32.999999999999986, "totalSingleQubitDecompositions": 8.000000000000002, "totalTouchedGates": 50.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 38.50000000000001}, "qaoa_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.2500000000000004, "timeInSingleQubitDecomposition": 1.2000000000000006, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 37.3}, "qftentangled_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -13.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.4, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 30.85, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.75}, "wstate_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.1, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 40.80000000000001}, "ghz_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 1.1500000000000001, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 14.349999999999996, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 34.3}, "qpeinexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -16.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 1.8000000000000007, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 22.650000000000002, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 35.65}, "vqe_real_amp_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.2000000000000004, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 44.650000000000006}, "vqe_su2_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 5.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.2000000000000002, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 35.1}, "qft_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.0, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 35.7}, "bmw_quark_cardinality_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -55.99999999999998, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 2.999999999999999, "timeInCircuitCollection": 1.0000000000000002, "timeInSingleQubitDecomposition": 2.5, "timeInTwoQubitDecomposition": 65.55000000000001, "totalCircuitCollections": 47.999999999999986, "totalSingleQubitDecompositions": 4.000000000000001, "totalTouchedGates": 50.999999999999986, "totalTwoQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 40.8}, "bv_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.05, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 36.95}, "ghz_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.2, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 35.95}, "vqe_su2_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 19.349999999999998, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 351.25, "totalCircuitCollections": 20.0, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 198.00000000000006, "totalTwoQubitDecompositions": 15.0, "twoQubitCreationTime": 37.55}, "qnn_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 3.099999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 19.249999999999996, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 13.000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 38.650000000000006}, "qpeexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 1.6500000000000006, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 13.600000000000005, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 34.45}, "qnn_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.3000000000000005, "timeInSingleQubitDecomposition": 1.4000000000000004, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 34.55}, "vqe_real_amp_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.449999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 187.54999999999998, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 38.7}, "qftentangled_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.65, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 36.45}, "bv_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 2.0000000000000004, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.15000000000000002, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 2.0000000000000004, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 35.25}, "randomcircuit_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.0500000000000003, "timeInSingleQubitDecomposition": 1.2000000000000004, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 35.55}, "qft_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -23.999999999999993, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.0000000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 32.00000000000001, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 35.900000000000006}, "qaoa_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -6.999999999999997, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.2000000000000006, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 33.95000000000001, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.05}} \ No newline at end of file +{"modular_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.1500000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 44.24999999999999}, "bmw_quark_cardinality_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.3000000000000005, "timeInSingleQubitDecomposition": 2.5500000000000003, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 13.999999999999995, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 11.999999999999996, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 46.3}, "draper_qft_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.1000000000000005, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 16.150000000000006, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 43.949999999999996}, "vqe_two_local_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 6.899999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 236.45000000000002, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 36.74999999999999}, "wstate_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -8.000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.1000000000000005, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 35.29999999999999, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.0}, "bmw_quark_copula_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.4000000000000004, "timeInSingleQubitDecomposition": 5.550000000000001, "timeInTwoQubitDecomposition": 19.249999999999996, "totalCircuitCollections": 32.999999999999986, "totalSingleQubitDecompositions": 8.000000000000002, "totalTouchedGates": 50.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 39.9}, "qaoa_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.0500000000000005, "timeInSingleQubitDecomposition": 2.200000000000001, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 37.1}, "qftentangled_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -13.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.1500000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 39.05, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.25000000000001}, "wstate_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.1, "timeInSingleQubitDecomposition": 1.3500000000000005, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 34.349999999999994}, "ghz_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 1.0500000000000003, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 18.6, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 33.050000000000004}, "qpeinexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -16.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 1.8000000000000005, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 28.64999999999999, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 34.3}, "vqe_real_amp_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.2500000000000002, "timeInSingleQubitDecomposition": 2.400000000000001, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 44.8}, "vqe_su2_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 5.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.4000000000000004, "timeInSingleQubitDecomposition": 2.100000000000001, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 34.2}, "qft_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.05, "timeInSingleQubitDecomposition": 1.5000000000000007, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 33.6}, "bmw_quark_cardinality_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -55.99999999999998, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 2.999999999999999, "timeInCircuitCollection": 1.0000000000000002, "timeInSingleQubitDecomposition": 2.999999999999999, "timeInTwoQubitDecomposition": 80.89999999999999, "totalCircuitCollections": 47.999999999999986, "totalSingleQubitDecompositions": 4.000000000000001, "totalTouchedGates": 50.999999999999986, "totalTwoQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 36.25}, "bv_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.05, "timeInSingleQubitDecomposition": 1.3500000000000003, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 35.55}, "ghz_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.15000000000000002, "timeInSingleQubitDecomposition": 1.5000000000000004, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 34.3}, "vqe_su2_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 19.4, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 452.15000000000003, "totalCircuitCollections": 20.0, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 198.00000000000006, "totalTwoQubitDecompositions": 15.0, "twoQubitCreationTime": 38.65000000000001}, "qnn_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 3.2999999999999994, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 24.749999999999993, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 13.000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 38.55}, "qpeexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 1.8000000000000005, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 17.7, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 35.45}, "qnn_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.3000000000000003, "timeInSingleQubitDecomposition": 2.250000000000001, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 33.55}, "vqe_real_amp_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.399999999999998, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 241.75000000000003, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 37.85}, "qftentangled_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.8500000000000002, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 34.2}, "bv_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 2.0000000000000004, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.0, "timeInSingleQubitDecomposition": 1.2500000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 2.0000000000000004, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 33.35000000000001}, "randomcircuit_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.0000000000000002, "timeInSingleQubitDecomposition": 2.1500000000000004, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 34.099999999999994}, "qft_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -23.999999999999993, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.0500000000000007, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 39.8, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 33.800000000000004}, "qaoa_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -6.999999999999997, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.2000000000000006, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 33.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 33.15}} \ No newline at end of file diff --git a/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf b/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf index 62f7520e7a7ae1c94a54d5c587227a7ffce27c72..18fe36dde63d296be9f62c1d46506b9201d5b2b3 100644 GIT binary patch delta 20 ccmX@t$at=ial<(qR$~)0Q;W@)ZGJNY09bekDgXcg delta 20 ccmX@t$at=ial<(qRwH8*W5dmtZGJNY09YLe9RL6T diff --git a/thesis-evaluation/figures/subCircuitComplexityChange.pdf b/thesis-evaluation/figures/subCircuitComplexityChange.pdf index f2d66e6837c19ed04bd19db9f602709509a3389a..957d2fb787516d7f435a9f5c53b40279799cf81f 100644 GIT binary patch delta 20 bcmeBO$k@A(aRZ|dtFeihspV!ipLP}iMdbzj delta 20 bcmeBO$k@A(aRZ|dtC6vZvEgPmpLP}iMT7lgo#to}nS&dE1Of5FAcP(ZC08z;YI{*Lx delta 20 ccmZ29g>lgo#to}nS&fWMj14xgcP(ZC08wZMEdT%j diff --git a/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf b/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf index 5df8037ca26bc339b9175909464d2d8163792028..dd1141fa92eb959e3b2d65cab9069f088d1d07ff 100644 GIT binary patch delta 20 bcmcaLk@40<#tlg>ti~p0rk0!2U5Z%%RHp|N delta 20 bcmcaLk@40<#tlg>tVYHr#s-_yU5Z%%R6qv> diff --git a/thesis-evaluation/figures/timeInCircuitCollection.pdf b/thesis-evaluation/figures/timeInCircuitCollection.pdf index cb26ceb1232dbaa01ef0ff1fcc86c49ead9b494c..4656de7558d74d10d2f4c6d7268848ff15ebce74 100644 GIT binary patch delta 10702 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 KLt0se;Qs*Te*_`` diff --git a/thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf b/thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf index ffd85b5141b0a5e054f7c5a7309cf4afb46d66cd..bebbaec524fe8f2921623109a5b2817eb502dbcf 100644 GIT binary patch delta 7034 zcmZuzbyyUQ*JVK@1nCZ!l-^*MrKM3Cq`MK24(Xx6Wyz&PDFrE!k`6%{LCU33DMb_% zcnJgi7Ju*a`#$BHKjyh}pL6HjbMDNY>B@_cmX8ifNM?`4KH80Q0*E*l~ zn;$Y-j2(aEIH9*%u=>~vsO09K>;j(6SG>l%;;^YLpT2Fs|6$#^#6ajt<;xIiEZj_O zz8I>JU?x`^fcWQV`&D>3lED6HJC%ii)6wydoY_Ud^BTUEl0+;)00@rxbw+p>gb4c; zcKSXrDfr8LnR#>e(bUR%cV{oKmHwn|n}xBp{&UZ4!8GHK0>C`rjTfb)@2hIB8qeWv z!IF%Tcm>N;(4F4E#_1!|`a4;-m#wNX?GX35{2KWtZMnmigc&Q+*MYjB1VpW?o~rZv zJDTLnUaZ=)x15*UdY4@xTxGP!BQ1jz1#F=dp?j@mw-ygWD@EI02{29eIW770q=gvr zH+sCc)HnDtdJ9$NDIHy&HyH@Xn%IlmSgafxtH%VSNo2PndXuo=@{2((+|!N zU5gu`LSkH9Z|+>NB8#qCmpgp#HrL^oD=#{<{mpGi=K0_{!4PvVZo(Tn=JSQ4t}(gu zyeI*6DueVj zj#WEuE=v)B*~y#wD)`}Rr{wF&#kRBt`$oPkmmIo;3`|37A{+Iu8!6{aR#7&W$*tMQ z-`9juDIzZt$x!A_6x$=_4q@%ehs>0i02)J!__vHI>6%WNc`k=!LI;@_jTm>C(7%{j z-Vmt~6ESD_4BC_qPbk=f9-~#!qi@u;Carzv;d{n(NjIc?SWKxwq1l zmHb6xgp#ZDEU`q$7g4<;nyvifvu*J>fu^N7Vc+X2SE5W}2wX1&>%e7;1FBk<@`##O zH9Kj626Xp7(GN>Gr(5>WF%~aa&#mBVmxBZ-9(uu2^|M2SR|y8H>YmMR!Ke7xUgBP_yo1LSQ9BYl z)leomX7FXw{L|U@PR;k~v(01-2C{!+O^AMW9Z*PKB@lmg$0o0pQJH93>nUGN?x1fE z5dkv`Lz1?Ht%+SflGsO;DcH!Kf#4|0e&aMxe}jSO9X7hf&1x$Hk=nmgARXgB3>oXwNW;lVJR1JmepyJVs~ z2yB-z+--j^Ae*5%%#XzQ%iLFo6SHh@a>SWCI*|pJEV39D7d<`3#s8p5a;z%&W^h;9 zRv;vhJc3Z8-&};iKR`>WwA*%{yRrjw4U)^9qfsH@wP{lR+KAQAy01|1)bF+}s8UXn zUSdwVxMlE~llOJd=li{~Mh%0NDXgSNNdOHFlXOd8V=J;`z1bXgF`a}&+VE2G`nx^j zbfkMK-qHrxlUh^8Net|JRYh-)(?$2o!R;RF44SuV%GZf_x@i~XGPX83RfUso$6xg? z9l!}Z9gbq{k235m7wBkZ+)&Y#H(oPxv!>`jCWy*$oQ2+G*%o@*^uDP2*r2BG3BV2Q zEV|V?=xiC#ugG^fMUl3a0GBbXe{r0Gw$}F1gl=xN+rY8P4f-|xp~7BT%>sIr^7&Ef z%K|Kf=)s|0D~yYSG;Hp=U*ecwTivV_gbXxpr&P6{zj`*@$Oh7Ea*7F!05s%lH?oGA&q%wjqj>2C1%=!Tmkq{A8xldasK9 zxp8A=I|M`XWcI1;C+slhO**=qb2jiv(iC@Pq+a^>sq9)|KT-BgJy?-4`$&nQW3v01 zXQH-)AzWNl)i#tI%~xhb=Jc(bk}d5U1K?(aC@P0ROUX9%V*AE{%0-}57+J2RXti6V zy)#H9|158`G&h?pW|FNe_l1~R#XCv@h+vI<6MRn9(1!)EgqDikHSl;dQKQ#d<*esQ z13Ww)uZvPNm0J+aD5$+JO+qqImo#^B0q)R{{4@TuA@B3zib?LrAfe)T+y%CJRvqtL z*mX-X=XLehuXw1L$D07&n-bTQ_#IlzrP(c=M;n+)($^Wpr#5RfCgu+4VgPC+ff(UcTXJTmR*pjc`@QnzK0gu ztLzgZoF+qOf7e{A6uvCv<9(S%3gJHYtvhr;62h0vMR<{Yz+(q+%)V)9s6y;{7oS`j9)Zy*Nwtmpx&wcb|&ud7Je1oQ{S*oLk$TJ ziI5>JY%iGJsVnaDrz1f(B~Nv(h@q?W&lVAyNH9|75cGZ_k;BbHkflZdXt?BC=}H}R zBdFCmCXm6z0`QwK4y!P+H>Sz8)_b<(ApW#%wTPBv5Zq(AsJRo7d85=Jm{oo2&6hd< z(0dQ2+3V_w?kjWfHq!9(nPbWcYuk)aI@sEnO(H9m{zwkf%zdzBWc9pvT1^wQE>hC4 zDXXe@?QnV_kf|2%Vr%a!-}JqPg2dg{>f}sVq;orh19jwW8PQwxuNj^%tOv1SMIZL} z<=7kDA&yB89|?BrerR?3hX7-(oOebneU-(9jqH_nV*Pak7TkNvA9R8Nnend~CsF8A zb{5lcz1@wJCE9c+G@FZ)Y1>iC!59hr(am*#f#JpsT-};VlIgFyvO|Zt;mdaHQ+>Q2 zA&)lGR{+fS#g`{gYY!ZUev3hJos}rZWu5(8yF z;GMPk*Vzx$J$pCTVJdI(e2;XKx0CEyy@_Lxt5sg4Z?!*idN; z(b{mve-;x}?WDr8KU2sLSMgf?lM*>~SSM*mGx&6J^M!A8;mQ&*ZT!ZQUuX>$Js!>8 zux&%D`A_t>M58M<;0s2UKERlwsV+Bkyt6@%U5Hh|%%%!9P$kHSjuG(catLjyGtnXwdAZDX z0dAG{E{O)MoYl`MSP*9qv-S}radRxr9j)xjn5~#*B@p^ZJJ$h_3)ct^3AfFC;>{+| zyFzWe4MA5vVHW7?R<2|I6bLFqn%0?)3H4|Y#|P!pB(0G3TJq}gFrw0h>w;bCn0I(a z>PPKi9fjMqrWC)1CW~3>m=CUgpEu!QymOnb_KJ6ZV!ze11xzPzQoq&NpUhZdzkh{L z?Ybe(e7Z}hJr?-?{#|g4yD0Q89g&MgneW=q_<5hdm~+oieNbXvH2A7%!?xf4omWNK ziY>a3P-+MyzPN|Wm_+xeRPSMk-TUj?ud}#!fF^2>$mC2bOr^Oc)29S0b@a6_zP?pr z-)7uzvRA1jN~EWqgMWJgL(j2Di!@c8EF-tV(8yXhFIPj4eg&S3JF)gbgEcA+2&KBb z8zEWM3OBep)3p{$-l$}Z7z<>7RfwIu8%I_mGubq3`ZOD(Bl0u*0@~~Y-qv~lG89xS`=fzitut;6-!ta&DZUI z;K4-~*u8mM*hE=`?hZ}Ui*Mzl8+9+|9lolSaO!&8NE;jJv;RVQQ?y=3geqdJR_x-? ztxWqa(*u*hC-&8G#lVP|-K(Rz4L-79MW$dw>&D<|5_Hyg&EBm`l#z)^XtGv+zgGkK0a4DU#3-Rr_f-6o;YVXlq&eOB6ybVi|`U{s)Iz< zp0~=gYr*Kn`E0ARS07+D+(r@yb0Y*=*oJIt!iPBSfk+i|J4`WnsK0%zq%}6V0sn^#yueU$bla*4a9=;|+mztT`f0%xbAp>YPPIwp ztMJnnTePK6=|V@bO|h@ZSkV^usmAVG!J!OA3k9W5#!CD$AM4k?y2$R<&^~=VJU4EX zQ=KT1paDAKpGk76Nv~xgoity=<))`Qx$t~9(YfzqxvsAmtQ@8PnhU_)%5oHu;C@j8 zGJCX759Iq68}sn*<0{ANT^RgCFB?U$_*-Ahy{4#coiSK&b(|tbCQiYf#30ObIG#sh z$I;1*q^?=F%gpNDXI@;&z7(eWwb?9(7k%cEdHq!Vlg?$3vjdBnu@@rUvUVn<(dG39 zk=!YRnKh$1G?;f1ei^#SJ+p30>|FOKM*J|G9Vjj%&7x{#O`f-ubFx?qcI%+>)>51+ zvR0du?@_m?S1-8h`oiveoSq-VE!J~5fk&QmYs_?q)m$65HRk3_!$he{ue?m!wHKj* z)qXH}zrABDI<6tT2}3M{%X6@`XlJ$W49*1Eb)eMcOC8ftW_oLM1Z0qboq_ns3362s zaC3rb=0U$Sk6%OUbpgYN8%brkFC&bO8wCvcoJTzw3M}9&j1MYqa_3}v?T|7IH;!M| zw(*|08!hwE?9I;b*sN!%3ljT4*o&f3#0@R<+@OmK_SnAk!y1wo<6wMm@RBCYCAIn) z??$J-4=}}A)z87=DWz|OJ;Ch?nBZ9lIe^)qdvMWIo;;s*G05i=xtP0OmTkGA`_qET zr@OLz??@ycTi;fI5Mc_7c*KSs#<4DOcHdQCen(?pzoV0M43`8a_kxU`465bPTOhGb z!WA?YgVtsw+3H*mt_9vMVc|hK)4vt?e#1)5TIGPDp>Cs!k4FXdJ;m7ID&G-tbwz80&p2J?PZv!5!n=trp8$^rX65j**^9gEh7>YQp-`-Sbij~_B=r=L6zcO z>Qn(mJ-Z$8TOoE~|T-(Kz zw-(H=ak$MdFhvPM1m#!*tc!q|&ijhm7Hwt9q6sT=&FhIY8Jx)>u*v{oJ7dpfN4ez@ z(ZPCo-p?jsw^RGs8QoDv4Hpp%KXnt#-9Bfx3sc_xK3UH6iHpJXVq;!my@t|!f@|U! zK_GvsS|>B^WzF(2ydHZpH>M&o&K$v0m@nx2f@UML#Gx>wZYo)w2`~ZjB^GPeda^th zP1al%b6iS%{cLUnM*WuC4j&-n)M}R^pA@=)Z7JOZ#-CrXYwXY^)lPwgh7%}O7bbvp z6=kHRe-g&xeC5=mMA*5GNqQBk3**2f2Uhg?5XD=CF-2Fj?MZ(MmWGmgvRB(d_BIqZ z#{?)A3sX{WB%i#BN!12KKMd@x=`>%-E=&yhRiUf0^8M{@zMvm+df)YnXJ5Y!CZspa zDVT7Q$KlSk(Z~*TiG8H9J(^k?^*eWZM#3X_>ddhvT>8Ro=D!;i)D>4U8M^V-ad-D2v|j;V@W z%LW^IRgvZ!E>Mpu2nqiGvR)6ck#`}VBXrNCEyCO3MfHR$`GN)rg9 zV)c5l9o7M2L+JK!!Hjt*gQNdpV%~*?n$>-Gv7cyEU=_VM=?-n<^UT0MJsIM>@>gGo6LzJ9SOZUF+6R-A%gA`j;{DJ&ZInbSTzUUx0` zRMW~%aAVy%CINblt=-h^B*V16>(2#Hi(YwflSgBy0V#evDXg^gh<&V9)b5L~tSMz8 zEr?Q3T9&yO4?#1-e5lr-VBgkWzLW*l2Gc%%#^qEcnhgr*G2CwyO0e<0Wh077+$vDMaY8cB1oG9Z|XlZLuH_R$7d%VGcqK!uCVxus@)Q z*i9&1LpF>LgpGmGVPC;#u3>9DgB>exOBdMq5(iseAj zH~d1OLDY;mtI7ewcB-j^#IeNcam3;X38W-8 zRh6kgCDIsAph zZyeVAIu*m;Y)}cPxD+5+-@> zhLYlN{GiW{|7}oG0)DO;NvX4^=1-gy0(K53B?13G#J>xmQh?Oi!}muq3<^7MD-;Po zkCQ;2vmc6*I**e=;$MtEIbcxKZ*h42|5XQrA)x1hDEK)NnAEv>!r|_2ub<#T_a(rzvqd64bROE uCJ9HOusfRUj0iBqz{|@Y{O?-0;pvQD3*uO?7CkvaT!NgBPgPfq{C@!QP5U$e delta 6875 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 diff --git a/thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf b/thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf index 2f748a072f3d2da6ed34be2282648ae5f849beba..4231db5082fe5db88f8fef4dd210bac2f81b46b9 100644 GIT binary patch delta 6663 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 V7Weq6%pD;x1WdrrE~EUE;9o{hr9l7y delta 6312 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 diff --git a/thesis-evaluation/figures/timePerTwoQubitDecomposition.pdf b/thesis-evaluation/figures/timePerTwoQubitDecomposition.pdf index e21a0187cbd50c9710895e39fef502fc998b92b4..0b3a7179e0b78bdb2275fcb65fae6abb2ea174e0 100644 GIT binary patch delta 6609 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 delta 20 ccmX>;i}Cm@#tq9oSdENLj14!h@tDm509WG&tN;K2 diff --git a/thesis-evaluation/figures/totalTouchedGates.pdf b/thesis-evaluation/figures/totalTouchedGates.pdf index f005bb94b975ab09b9cde7dae1dbfe76c80961e6..c8e2506cd12f1cb3ab35f06166db44dbe24a7bd6 100644 GIT binary patch delta 20 bcmeC0!q_#1al<56R$~)0Q;W^hU5i-&Ot}Wz delta 20 bcmeC0!q_#1al<56RwH8*V}s4pU5i-&OjriZ diff --git a/thesis-evaluation/figures/totalTwoQubitDecompositions.pdf b/thesis-evaluation/figures/totalTwoQubitDecompositions.pdf index 132c83d709785feed4e975c9b1b6e1b915f3e938..d778c3ddd621fb989c6fcc9450a73f59c3891ed3 100644 GIT binary patch delta 20 ccmex7h4J$g#tqM1S&dE1Of5FQacyS-0AaHTYXATM delta 20 ccmex7h4J$g#tqM1S&fWMj14!xacyS-0AW}NUH||9 diff --git a/thesis-evaluation/figures/twoQubitCreationTime.pdf b/thesis-evaluation/figures/twoQubitCreationTime.pdf index 31c0c2d4f187489e02a78ba2b6434d3488eca474..a7e2702307763f1c6fddbf04d0edf4a8b0e9c8fc 100644 GIT binary patch delta 10673 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

wP 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 RYFEX?q^@#us%oiS{eOG`YoP!D delta 10564 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 Date: Mon, 16 Feb 2026 12:29:52 +0100 Subject: [PATCH 231/237] fix evaluation graph y axis + add improved circuit calculation --- thesis-evaluation/total_evaluate.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/thesis-evaluation/total_evaluate.py b/thesis-evaluation/total_evaluate.py index 26109a4fbb..dce75a7420 100644 --- a/thesis-evaluation/total_evaluate.py +++ b/thesis-evaluation/total_evaluate.py @@ -253,9 +253,17 @@ def define_division_metric( # plt.xticks(x_values) # does not work for strings plt.xticks(rotation=45) - if ymin > 0: + if ymin >= 0: # let matplotlib handle non-positive values automatically - plt.ylim(bottom=0) + # plt.ylim(bottom=0) + # plt.margins(y=0.1) + ax = plt.gca() + ticks = ax.get_yticks() + if not np.any(np.isclose(ticks, 0)): + # ensure y axis starts at zero + plt.ylim(bottom=0) + else: + ax.set_yticks(ticks[ticks >= 0]) yint = [] locs, labels = plt.yticks() @@ -302,3 +310,12 @@ def define_division_metric( f"Median Qiskit {metric}:", np.median(np.ma.masked_invalid(y2[metric])) if metric in y2 else "-", ) + +print( + "Num of improved circuits (MQT): ", + len(list(filter(lambda x: x > 0, y1["subCircuitComplexityChange"]))), +) +print( + "Num of improved circuits (Qiskit): ", + len(list(filter(lambda x: x > 0, y2["subCircuitComplexityChange"]))), +) From b21868f337a1dc675959939cab7fb9ee0880c6c4 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 16 Feb 2026 12:30:27 +0100 Subject: [PATCH 232/237] update figures for corrected y axis --- .../figures/numberOfTwoQubitCreations.pdf | Bin 16590 -> 16590 bytes .../figures/subCircuitComplexityChange.pdf | Bin 20621 -> 20621 bytes .../successfulSingleQubitDecompositions.pdf | Bin 18978 -> 19014 bytes .../successfulTwoQubitDecompositions.pdf | Bin 18650 -> 18682 bytes .../figures/timeInCircuitCollection.pdf | Bin 22702 -> 22475 bytes .../timeInCircuitCollectionStandalone.pdf | Bin 18580 -> 18452 bytes .../timeInSingleQubitDecomposition.pdf | Bin 19596 -> 19596 bytes .../figures/timeInTwoQubitDecomposition.pdf | Bin 18681 -> 18702 bytes .../timePerSingleQubitDecomposition.pdf | Bin 18251 -> 18251 bytes .../figures/timePerTwoQubitDecomposition.pdf | Bin 17825 -> 17825 bytes .../figures/totalCircuitCollections.pdf | Bin 17485 -> 17525 bytes .../totalSingleQubitDecompositions.pdf | Bin 19783 -> 19570 bytes .../figures/totalTouchedGates.pdf | Bin 18954 -> 19016 bytes .../figures/totalTwoQubitDecompositions.pdf | Bin 19059 -> 18838 bytes .../figures/twoQubitCreationTime.pdf | Bin 23629 -> 23629 bytes 15 files changed, 0 insertions(+), 0 deletions(-) diff --git a/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf b/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf index 18fe36dde63d296be9f62c1d46506b9201d5b2b3..65e9d6c6036f2f19255b1e7900af1ecdab59359c 100644 GIT binary patch delta 22 ecmX@t$at=ial;uKb~8gGBMW28&6jO{GXnr+!w8Q6 delta 22 ecmX@t$at=ial;uKb`wKm6Ejnb&6jO{GXnr+zX*>2 diff --git a/thesis-evaluation/figures/subCircuitComplexityChange.pdf b/thesis-evaluation/figures/subCircuitComplexityChange.pdf index 957d2fb787516d7f435a9f5c53b40279799cf81f..83788290cb905dcfd65c54a6219ffbc223df503a 100644 GIT binary patch delta 22 dcmeBO$k@A(al?Oab~8gGBMTFQ&1^pHEC65c2Uh?9 delta 22 dcmeBO$k@A(al?Oab`wKm6EjoG&1^pHEC6622VejI diff --git a/thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf b/thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf index 2df8f3392acd3cb16109e0cf8ec4ea847340d39e..b56f3f4cc259286aecddb19ca555a0d87fd99857 100644 GIT binary patch delta 6842 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 diff --git a/thesis-evaluation/figures/timeInCircuitCollection.pdf b/thesis-evaluation/figures/timeInCircuitCollection.pdf index 4656de7558d74d10d2f4c6d7268848ff15ebce74..07cd40761ae0638747cbc6b6fc9b6a2dd753e40e 100644 GIT binary patch delta 11063 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 V3xUVOh_JZ%d9fH6WK?Cb{s(1i0I&c6 delta 11205 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 V^!_!u5DAzBl#HKWMOT&V{{WJYq(%S$ delta 7234 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 G>c0RAM$mTv delta 6578 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 Hr%wGpeCYwO delta 6132 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 LUS0()MbiHPQq}bz diff --git a/thesis-evaluation/figures/totalSingleQubitDecompositions.pdf b/thesis-evaluation/figures/totalSingleQubitDecompositions.pdf index c4097a8a06540ca2a91efa4a48c7656a001b4911..5f4f45a7ee32d8526393eba35fea3275e98c001c 100644 GIT binary patch delta 7373 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 diff --git a/thesis-evaluation/figures/totalTouchedGates.pdf b/thesis-evaluation/figures/totalTouchedGates.pdf index c8e2506cd12f1cb3ab35f06166db44dbe24a7bd6..49822f1919878f5c35a415b0d7b512b2b9cbf1b4 100644 GIT binary patch delta 6631 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 F{{VGCpr`-< delta 6573 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 K3#;j%sQ(9F(`{`4 diff --git a/thesis-evaluation/figures/totalTwoQubitDecompositions.pdf b/thesis-evaluation/figures/totalTwoQubitDecompositions.pdf index d778c3ddd621fb989c6fcc9450a73f59c3891ed3..10f8343d02d90d20150626bb994a33971a80b440 100644 GIT binary patch delta 7334 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 diff --git a/thesis-evaluation/figures/twoQubitCreationTime.pdf b/thesis-evaluation/figures/twoQubitCreationTime.pdf index a7e2702307763f1c6fddbf04d0edf4a8b0e9c8fc..5803a8fcdc09838428ad8ee4590f015fb64a0af2 100644 GIT binary patch delta 22 ecmX@RgYoPR#tqZM+06`%j4VtHHqQ%R&jJ8z5eReu delta 22 ecmX@RgYoPR#tqZM*-Z?MP0UO!HqQ%R&jJ8zJP3RM From c3d412b1510871d9a7034dbde0c3f1f594993b99 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 16 Feb 2026 13:05:55 +0100 Subject: [PATCH 233/237] Revert thesis-evaluation changes This reverts the following commits: * b21868f337a1dc675959939cab7fb9ee0880c6c4 * f4b6d7fc865a9fab6e7f67733d82bd64fa77c25f * 0b9a10d6c3d99c7138878e549d3dda205acc691d * e61854f57fd096e9bd7f9629086fcec55c327dac * 97755cf753f9ecea468ccbb54923f982346b1e5a * 8f58cb401706e3f457de7e3f148c8c33b7bf3063 * 310d35ab13ca7851d6d3b119a24626b547b0f93f --- mlir/include/mlir/Passes/Passes.h | 15 +- mlir/lib/Compiler/CompilerPipeline.cpp | 88 ++--- mlir/lib/Passes/GateDecompositionPass.cpp | 83 +---- .../Patterns/GateDecompositionPattern.cpp | 117 +------ thesis-evaluation/__init__.py | 0 thesis-evaluation/evaluate.py | 34 -- thesis-evaluation/evaluate_cache_mqt.json | 1 - .../evaluate_cache_qiskit-rust.json | 1 - thesis-evaluation/evaluate_cache_qiskit.json | 1 - .../figures/numberOfTwoQubitCreations.pdf | Bin 16590 -> 0 bytes .../figures/subCircuitComplexityChange.pdf | Bin 20621 -> 0 bytes .../successfulSingleQubitDecompositions.pdf | Bin 19014 -> 0 bytes .../successfulTwoQubitDecompositions.pdf | Bin 18682 -> 0 bytes .../figures/timeInCircuitCollection.pdf | Bin 22475 -> 0 bytes .../timeInCircuitCollectionStandalone.pdf | Bin 18452 -> 0 bytes .../timeInSingleQubitDecomposition.pdf | Bin 19596 -> 0 bytes .../figures/timeInTwoQubitDecomposition.pdf | Bin 18702 -> 0 bytes .../timePerSingleQubitDecomposition.pdf | Bin 18251 -> 0 bytes .../figures/timePerTwoQubitDecomposition.pdf | Bin 17825 -> 0 bytes .../figures/totalCircuitCollections.pdf | Bin 17525 -> 0 bytes .../totalSingleQubitDecompositions.pdf | Bin 19570 -> 0 bytes .../figures/totalTouchedGates.pdf | Bin 19016 -> 0 bytes .../figures/totalTwoQubitDecompositions.pdf | Bin 18838 -> 0 bytes .../figures/twoQubitCreationTime.pdf | Bin 23629 -> 0 bytes thesis-evaluation/qiskit_run.py | 256 -------------- thesis-evaluation/run.sh | 33 -- thesis-evaluation/total_evaluate.py | 321 ------------------ 27 files changed, 56 insertions(+), 894 deletions(-) delete mode 100644 thesis-evaluation/__init__.py delete mode 100644 thesis-evaluation/evaluate.py delete mode 100644 thesis-evaluation/evaluate_cache_mqt.json delete mode 100644 thesis-evaluation/evaluate_cache_qiskit-rust.json delete mode 100644 thesis-evaluation/evaluate_cache_qiskit.json delete mode 100644 thesis-evaluation/figures/numberOfTwoQubitCreations.pdf delete mode 100644 thesis-evaluation/figures/subCircuitComplexityChange.pdf delete mode 100644 thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf delete mode 100644 thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf delete mode 100644 thesis-evaluation/figures/timeInCircuitCollection.pdf delete mode 100644 thesis-evaluation/figures/timeInCircuitCollectionStandalone.pdf delete mode 100644 thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf delete mode 100644 thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf delete mode 100644 thesis-evaluation/figures/timePerSingleQubitDecomposition.pdf delete mode 100644 thesis-evaluation/figures/timePerTwoQubitDecomposition.pdf delete mode 100644 thesis-evaluation/figures/totalCircuitCollections.pdf delete mode 100644 thesis-evaluation/figures/totalSingleQubitDecompositions.pdf delete mode 100644 thesis-evaluation/figures/totalTouchedGates.pdf delete mode 100644 thesis-evaluation/figures/totalTwoQubitDecompositions.pdf delete mode 100644 thesis-evaluation/figures/twoQubitCreationTime.pdf delete mode 100644 thesis-evaluation/qiskit_run.py delete mode 100755 thesis-evaluation/run.sh delete mode 100644 thesis-evaluation/total_evaluate.py diff --git a/mlir/include/mlir/Passes/Passes.h b/mlir/include/mlir/Passes/Passes.h index ec21ae3523..34e5a770ba 100644 --- a/mlir/include/mlir/Passes/Passes.h +++ b/mlir/include/mlir/Passes/Passes.h @@ -27,20 +27,7 @@ namespace mlir::qco { #define GEN_PASS_DECL #include "mlir/Passes/Passes.h.inc" // IWYU pragma: export -void populateGateDecompositionPatterns( - mlir::RewritePatternSet& patterns, - llvm::Statistic& twoQubitCreationTime, - llvm::Statistic& numberOfTwoQubitCreations, - llvm::Statistic& successfulSingleQubitDecompositions, - llvm::Statistic& totalSingleQubitDecompositions, - llvm::Statistic& successfulTwoQubitDecompositions, - llvm::Statistic& totalTwoQubitDecompositions, - llvm::Statistic& totalCircuitCollections, - llvm::Statistic& totalTouchedGates, - llvm::Statistic& subCircuitComplexityChange, - llvm::Statistic& timeInCircuitCollection, - llvm::Statistic& timeInSingleQubitDecomposition, - llvm::Statistic& timeInTwoQubitDecomposition); +void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns); //===----------------------------------------------------------------------===// // Registration diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index 0ee6d530a2..8a09fe56d3 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -124,17 +124,17 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, } // Stage 2: QC canonicalization - // addCleanupPasses(pm); - // if (pm.run(module).failed()) { - // return failure(); - // } - // if (record != nullptr && config_.recordIntermediates) { - // record->afterInitialCanon = captureIR(module); - // if (config_.printIRAfterAllStages) { - // prettyPrintStage(module, "Initial QC Canonicalization", ++currentStage, - // totalStages); - // } - // } + addCleanupPasses(pm); + if (pm.run(module).failed()) { + return failure(); + } + if (record != nullptr && config_.recordIntermediates) { + record->afterInitialCanon = captureIR(module); + if (config_.printIRAfterAllStages) { + prettyPrintStage(module, "Initial QC Canonicalization", ++currentStage, + totalStages); + } + } pm.clear(); // Stage 3: QC-to-QCO conversion @@ -152,17 +152,17 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, pm.clear(); // Stage 4: QCO canonicalization - // addCleanupPasses(pm); - // if (failed(pm.run(module))) { - // return failure(); - // } - // if (record != nullptr && config_.recordIntermediates) { - // record->afterQCOCanon = captureIR(module); - // if (config_.printIRAfterAllStages) { - // prettyPrintStage(module, "Initial QCO Canonicalization", ++currentStage, - // totalStages); - // } - // } + addCleanupPasses(pm); + if (failed(pm.run(module))) { + return failure(); + } + if (record != nullptr && config_.recordIntermediates) { + record->afterQCOCanon = captureIR(module); + if (config_.printIRAfterAllStages) { + prettyPrintStage(module, "Initial QCO Canonicalization", ++currentStage, + totalStages); + } + } pm.clear(); // Stage 5: Optimization passes @@ -180,17 +180,17 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, pm.clear(); // Stage 6: QCO canonicalization - // addCleanupPasses(pm); - // if (failed(pm.run(module))) { - // return failure(); - // } - // if (record != nullptr && config_.recordIntermediates) { - // record->afterOptimizationCanon = captureIR(module); - // if (config_.printIRAfterAllStages) { - // prettyPrintStage(module, "Final QCO Canonicalization", ++currentStage, - // totalStages); - // } - // } + addCleanupPasses(pm); + if (failed(pm.run(module))) { + return failure(); + } + if (record != nullptr && config_.recordIntermediates) { + record->afterOptimizationCanon = captureIR(module); + if (config_.printIRAfterAllStages) { + prettyPrintStage(module, "Final QCO Canonicalization", ++currentStage, + totalStages); + } + } pm.clear(); // Stage 7: QCO-to-QC conversion @@ -208,17 +208,17 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, pm.clear(); // Stage 8: QC canonicalization - // addCleanupPasses(pm); - // if (failed(pm.run(module))) { - // return failure(); - // } - // if (record != nullptr && config_.recordIntermediates) { - // record->afterQCCanon = captureIR(module); - // if (config_.printIRAfterAllStages) { - // prettyPrintStage(module, "Final QC Canonicalization", ++currentStage, - // totalStages); - // } - // } + addCleanupPasses(pm); + if (failed(pm.run(module))) { + return failure(); + } + if (record != nullptr && config_.recordIntermediates) { + record->afterQCCanon = captureIR(module); + if (config_.printIRAfterAllStages) { + prettyPrintStage(module, "Final QC Canonicalization", ++currentStage, + totalStages); + } + } pm.clear(); // Stage 9: QC-to-QIR conversion (optional) diff --git a/mlir/lib/Passes/GateDecompositionPass.cpp b/mlir/lib/Passes/GateDecompositionPass.cpp index 5ac5ded2d4..ee90801549 100644 --- a/mlir/lib/Passes/GateDecompositionPass.cpp +++ b/mlir/lib/Passes/GateDecompositionPass.cpp @@ -28,46 +28,6 @@ namespace mlir::qco { struct GateDecompositionPass final : impl::GateDecompositionPassBase { - GateDecompositionPass() = default; - GateDecompositionPass(const GateDecompositionPass& other) - : impl::GateDecompositionPassBase{other}, - twoQubitCreationTime{this, "twoQubitCreationTime", - "Creation time of basis decomposers"}, - numberOfTwoQubitCreations{ - this, "numberOfTwoQubitCreations", - "Number of times basis decomposers are created"}, - successfulSingleQubitDecompositions{ - this, "successfulSingleQubitDecompositions", - "Number of times a single-qubit decomposition was applied"}, - totalSingleQubitDecompositions{this, "totalSingleQubitDecompositions", - "Number of times (only) a single-qubit " - "decomposition was calculated"}, - successfulTwoQubitDecompositions{ - this, "successfulTwoQubitDecompositions", - "Number of times a two-qubit decomposition was applied"}, - totalTwoQubitDecompositions{ - this, "totalTwoQubitDecompositions", - "Number of times a two-qubit decomposition was calculated"}, - totalCircuitCollections{this, "totalCircuitCollections", - "Number of times a sub-circuit was collected"}, - totalTouchedGates{ - this, "totalTouchedGates", - "Number of gates that were looked at (in sub-circuit collection)"}, - subCircuitComplexityChange{ - this, "subCircuitComplexityChange", - "Increase or decrease of complexity in sub-circuit"}, - timeInCircuitCollection{this, "timeInCircuitCollection", - "Time spent in circuit collection (µs)"}, - timeInSingleQubitDecomposition{ - this, "timeInSingleQubitDecomposition", - "Time spent in single-qubit decomposition (µs)"}, - timeInTwoQubitDecomposition{ - this, "timeInTwoQubitDecomposition", - "Time spent in single-qubit decomposition (µs)"} {} - GateDecompositionPass(GateDecompositionPass&& other) = delete; - GateDecompositionPass& operator=(const GateDecompositionPass& other) = delete; - GateDecompositionPass& operator=(GateDecompositionPass&& other) = delete; - void runOnOperation() override { // Get the current operation being operated on. auto op = getOperation(); @@ -75,13 +35,7 @@ struct GateDecompositionPass final // Define the set of patterns to use. mlir::RewritePatternSet patterns(ctx); - populateGateDecompositionPatterns( - patterns, twoQubitCreationTime, numberOfTwoQubitCreations, - successfulSingleQubitDecompositions, totalSingleQubitDecompositions, - successfulTwoQubitDecompositions, totalTwoQubitDecompositions, - totalCircuitCollections, totalTouchedGates, subCircuitComplexityChange, - timeInCircuitCollection, timeInSingleQubitDecomposition, - timeInTwoQubitDecomposition); + populateGateDecompositionPatterns(patterns); // Configure greedy driver mlir::GreedyRewriteConfig config; @@ -97,41 +51,6 @@ struct GateDecompositionPass final signalPassFailure(); } } - - Statistic twoQubitCreationTime{this, "twoQubitCreationTime", - "Creation time of basis decomposers"}; - Statistic numberOfTwoQubitCreations{ - this, "numberOfTwoQubitCreations", - "Number of times basis decomposers are created"}; - Statistic successfulSingleQubitDecompositions{ - this, "successfulSingleQubitDecompositions", - "Number of times a single-qubit decomposition was applied"}; - Statistic totalSingleQubitDecompositions{ - this, "totalSingleQubitDecompositions", - "Number of times (only) a single-qubit decomposition was calculated"}; - Statistic successfulTwoQubitDecompositions{ - this, "successfulTwoQubitDecompositions", - "Number of times a two-qubit decomposition was applied"}; - Statistic totalTwoQubitDecompositions{ - this, "totalTwoQubitDecompositions", - "Number of times a two-qubit decomposition was calculated"}; - Statistic totalCircuitCollections{ - this, "totalCircuitCollections", - "Number of times a sub-circuit was collected"}; - Statistic totalTouchedGates{ - this, "totalTouchedGates", - "Number of gates that were looked at (in sub-circuit collection)"}; - Statistic subCircuitComplexityChange{ - this, "subCircuitComplexityChange", - "Increase or decrease of complexity in sub-circuit"}; - Statistic timeInCircuitCollection{this, "timeInCircuitCollection", - "Time spent in circuit collection (µs)"}; - Statistic timeInSingleQubitDecomposition{ - this, "timeInSingleQubitDecomposition", - "Time spent in single-qubit decomposition (µs)"}; - Statistic timeInTwoQubitDecomposition{ - this, "timeInTwoQubitDecomposition", - "Time spent in single-qubit decomposition (µs)"}; }; } // namespace mlir::qco diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 890855b170..50d2e774b4 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -73,49 +73,18 @@ struct GateDecompositionPattern final * a decomposition if the (sub)circuit only contains * gates available as basis gates or euler bases */ - explicit GateDecompositionPattern( - mlir::MLIRContext* context, llvm::SmallVector basisGates, - llvm::SmallVector eulerBasis, bool singleQubitOnly, - bool forceApplication, llvm::Statistic& twoQubitCreationTime, - llvm::Statistic& numberOfTwoQubitCreations, - llvm::Statistic& successfulSingleQubitDecompositions, - llvm::Statistic& totalSingleQubitDecompositions, - llvm::Statistic& successfulTwoQubitDecompositions, - llvm::Statistic& totalTwoQubitDecompositions, - llvm::Statistic& totalCircuitCollections, - llvm::Statistic& totalTouchedGates, - llvm::Statistic& subCircuitComplexityChange, - llvm::Statistic& timeInCircuitCollection, - llvm::Statistic& timeInSingleQubitDecomposition, - llvm::Statistic& timeInTwoQubitDecomposition) + explicit GateDecompositionPattern(mlir::MLIRContext* context, + llvm::SmallVector basisGates, + llvm::SmallVector eulerBasis, + bool singleQubitOnly, bool forceApplication) : OpInterfaceRewritePattern(context), decomposerBasisGates{std::move(basisGates)}, decomposerEulerBases{std::move(eulerBasis)}, - singleQubitOnly{singleQubitOnly}, forceApplication{forceApplication}, - twoQubitCreationTime{twoQubitCreationTime}, - numberOfTwoQubitCreations{numberOfTwoQubitCreations}, - successfulSingleQubitDecompositions{ - successfulSingleQubitDecompositions}, - totalSingleQubitDecompositions{totalSingleQubitDecompositions}, - successfulTwoQubitDecompositions{successfulTwoQubitDecompositions}, - totalTwoQubitDecompositions{totalTwoQubitDecompositions}, - totalCircuitCollections{totalCircuitCollections}, - totalTouchedGates{totalTouchedGates}, - subCircuitComplexityChange{subCircuitComplexityChange}, - timeInCircuitCollection{timeInCircuitCollection}, - timeInSingleQubitDecomposition{timeInSingleQubitDecomposition}, - timeInTwoQubitDecomposition{timeInTwoQubitDecomposition} { - ++numberOfTwoQubitCreations; - auto startTime = std::chrono::steady_clock::now(); + singleQubitOnly{singleQubitOnly}, forceApplication{forceApplication} { for (auto&& basisGate : decomposerBasisGates) { basisDecomposers.push_back(decomposition::TwoQubitBasisDecomposer::create( basisGate, DEFAULT_FIDELITY)); } - auto endTime = std::chrono::steady_clock::now(); - twoQubitCreationTime += - std::chrono::duration_cast(endTime - - startTime) - .count(); auto&& [singleQubitGates, twoQubitGates] = getDecompositionGates(); availableSingleQubitGates = std::move(singleQubitGates); availableTwoQubitGates = std::move(twoQubitGates); @@ -134,23 +103,13 @@ struct GateDecompositionPattern final return mlir::failure(); } - auto collectSeries = [this](UnitaryOpInterface op, bool singleQubitOnly) { - ++totalCircuitCollections; + auto collectSeries = [](UnitaryOpInterface op, bool singleQubitOnly) { if (singleQubitOnly) { return TwoQubitSeries::getSingleQubitSeries(op); } return TwoQubitSeries::getTwoQubitSeries(op); }; - auto startTime = std::chrono::steady_clock::now(); auto series = collectSeries(op, singleQubitOnly); - auto endTime = std::chrono::steady_clock::now(); - timeInCircuitCollection += - std::chrono::duration_cast(endTime - - startTime) - .count(); - // not really accurate since it neglects the "past the series" gates that - // terminated the series - totalTouchedGates += series.gates.size(); auto containsForeignGates = !series.containsOnlyGates( availableSingleQubitGates, availableTwoQubitGates); @@ -173,10 +132,6 @@ struct GateDecompositionPattern final // cannot process decomposition without the matrix of the series return mlir::failure(); } - // only count the multiple decompositions as "one" since the number of - // euler bases is constant - ++totalSingleQubitDecompositions; - startTime = std::chrono::steady_clock::now(); for (auto&& eulerBasis : decomposerEulerBases) { auto sequence = decomposition::EulerDecomposition::generateCircuit( eulerBasis, *unitaryMatrix, true, std::nullopt); @@ -186,11 +141,6 @@ struct GateDecompositionPattern final bestSequence = std::make_pair(std::move(sequence), newComplexity); } } - endTime = std::chrono::steady_clock::now(); - timeInSingleQubitDecomposition += - std::chrono::duration_cast(endTime - - startTime) - .count(); } else { // two-qubit series; perform two-qubit basis decomposition const auto unitaryMatrix = series.getUnitaryMatrix(); @@ -202,13 +152,9 @@ struct GateDecompositionPattern final decomposition::TwoQubitWeylDecomposition::create(*unitaryMatrix, DEFAULT_FIDELITY); - // only count the multiple decompositions as "one" since the number of - // euler bases is constant - ++totalTwoQubitDecompositions; - startTime = std::chrono::steady_clock::now(); for (const auto& decomposer : basisDecomposers) { auto sequence = decomposer.twoQubitDecompose( - targetDecomposition, decomposerEulerBases, DEFAULT_FIDELITY, true, + targetDecomposition, decomposerEulerBases, DEFAULT_FIDELITY, false, std::nullopt); if (sequence) { // decomposition successful @@ -220,11 +166,6 @@ struct GateDecompositionPattern final } } } - endTime = std::chrono::steady_clock::now(); - timeInTwoQubitDecomposition += - std::chrono::duration_cast(endTime - - startTime) - .count(); } if (bestSequence.second == std::numeric_limits::max()) { @@ -240,13 +181,6 @@ struct GateDecompositionPattern final return mlir::failure(); } - if (series.isSingleQubitSeries()) { - ++successfulSingleQubitDecompositions; - } else { - ++successfulTwoQubitDecompositions; - } - subCircuitComplexityChange += series.complexity - bestSequence.second; - applySeries(rewriter, series, bestSequence.first); return mlir::success(); @@ -763,38 +697,13 @@ struct GateDecompositionPattern final // configuration of pattern bool singleQubitOnly; bool forceApplication; - - llvm::Statistic& twoQubitCreationTime; - llvm::Statistic& numberOfTwoQubitCreations; - llvm::Statistic& successfulSingleQubitDecompositions; - llvm::Statistic& totalSingleQubitDecompositions; - llvm::Statistic& successfulTwoQubitDecompositions; - llvm::Statistic& totalTwoQubitDecompositions; - llvm::Statistic& totalCircuitCollections; - llvm::Statistic& totalTouchedGates; - llvm::Statistic& subCircuitComplexityChange; - llvm::Statistic& timeInCircuitCollection; - llvm::Statistic& timeInSingleQubitDecomposition; - llvm::Statistic& timeInTwoQubitDecomposition; }; /** * @brief Populates the given pattern set with patterns for gate * decomposition. */ -void populateGateDecompositionPatterns( - mlir::RewritePatternSet& patterns, llvm::Statistic& twoQubitCreationTime, - llvm::Statistic& numberOfTwoQubitCreations, - llvm::Statistic& successfulSingleQubitDecompositions, - llvm::Statistic& totalSingleQubitDecompositions, - llvm::Statistic& successfulTwoQubitDecompositions, - llvm::Statistic& totalTwoQubitDecompositions, - llvm::Statistic& totalCircuitCollections, - llvm::Statistic& totalTouchedGates, - llvm::Statistic& subCircuitComplexityChange, - llvm::Statistic& timeInCircuitCollection, - llvm::Statistic& timeInSingleQubitDecomposition, - llvm::Statistic& timeInTwoQubitDecomposition) { +void populateGateDecompositionPatterns(mlir::RewritePatternSet& patterns) { llvm::SmallVector basisGates; llvm::SmallVector eulerBases; basisGates.push_back({.type = qc::X, .parameter = {}, .qubitId = {0, 1}}); @@ -803,14 +712,8 @@ void populateGateDecompositionPatterns( eulerBases.push_back(GateDecompositionPattern::EulerBasis::XYX); eulerBases.push_back(GateDecompositionPattern::EulerBasis::XZX); eulerBases.push_back(GateDecompositionPattern::EulerBasis::ZXZ); - patterns.add( - patterns.getContext(), basisGates, eulerBases, false, true, - twoQubitCreationTime, numberOfTwoQubitCreations, - successfulSingleQubitDecompositions, totalSingleQubitDecompositions, - successfulTwoQubitDecompositions, totalTwoQubitDecompositions, - totalCircuitCollections, totalTouchedGates, subCircuitComplexityChange, - timeInCircuitCollection, timeInSingleQubitDecomposition, - timeInTwoQubitDecomposition); + patterns.add(patterns.getContext(), basisGates, + eulerBases, false, false); } } // namespace mlir::qco diff --git a/thesis-evaluation/__init__.py b/thesis-evaluation/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/thesis-evaluation/evaluate.py b/thesis-evaluation/evaluate.py deleted file mode 100644 index dda4b4fc5b..0000000000 --- a/thesis-evaluation/evaluate.py +++ /dev/null @@ -1,34 +0,0 @@ -import matplotlib.pyplot as plt -import glob -import os - -INPUT_DIR = "./tmp-output" - -def read_stats(file_name) -> dict[str, int]: - result = {} - - with open(file_name, "r") as f: - for line in f.readlines(): - line = line.lstrip() - line.removeprefix("(S)") - line = line.lstrip() - elements = line.split(" ") - elements = list(filter(lambda x: x != " " and len(x) > 0, elements)) - - metric = elements[2] - value = int(elements[1]) - result[metric] = value if value < 2**31 else value - 2**32 - - return result - -def evaluate(): - all_stats = {} - for file in glob.glob(f"{INPUT_DIR}/*.statistic"): - print(f"Processing {file}...") - name = os.path.basename(file).removesuffix(".statistic") - name = name.removesuffix(".qasm") - name = name.removesuffix(".mlir") - all_stats[name] = read_stats(file) - - print(all_stats) - return all_stats diff --git a/thesis-evaluation/evaluate_cache_mqt.json b/thesis-evaluation/evaluate_cache_mqt.json deleted file mode 100644 index 62a646af57..0000000000 --- a/thesis-evaluation/evaluate_cache_mqt.json +++ /dev/null @@ -1 +0,0 @@ -{"modular_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.1500000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 44.24999999999999}, "bmw_quark_cardinality_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.3000000000000005, "timeInSingleQubitDecomposition": 2.5500000000000003, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 13.999999999999995, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 11.999999999999996, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 46.3}, "draper_qft_adder_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.1000000000000005, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 16.150000000000006, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 43.949999999999996}, "vqe_two_local_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 6.899999999999999, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 236.45000000000002, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 36.74999999999999}, "wstate_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -8.000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.1000000000000005, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 35.29999999999999, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 37.0}, "bmw_quark_copula_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.4000000000000004, "timeInSingleQubitDecomposition": 5.550000000000001, "timeInTwoQubitDecomposition": 19.249999999999996, "totalCircuitCollections": 32.999999999999986, "totalSingleQubitDecompositions": 8.000000000000002, "totalTouchedGates": 50.0, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 39.9}, "qaoa_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.0500000000000005, "timeInSingleQubitDecomposition": 2.200000000000001, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 37.1}, "qftentangled_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -13.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.1500000000000004, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 39.05, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.25000000000001}, "wstate_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.1, "timeInSingleQubitDecomposition": 1.3500000000000005, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 34.349999999999994}, "ghz_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -4.000000000000001, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 1.0500000000000003, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 18.6, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 33.050000000000004}, "qpeinexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -16.000000000000004, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 1.8000000000000005, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 28.64999999999999, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 34.3}, "vqe_real_amp_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.2500000000000002, "timeInSingleQubitDecomposition": 2.400000000000001, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 44.8}, "vqe_su2_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 5.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.4000000000000004, "timeInSingleQubitDecomposition": 2.100000000000001, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 34.2}, "qft_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.05, "timeInSingleQubitDecomposition": 1.5000000000000007, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 33.6}, "bmw_quark_cardinality_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -55.99999999999998, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 2.999999999999999, "timeInCircuitCollection": 1.0000000000000002, "timeInSingleQubitDecomposition": 2.999999999999999, "timeInTwoQubitDecomposition": 80.89999999999999, "totalCircuitCollections": 47.999999999999986, "totalSingleQubitDecompositions": 4.000000000000001, "totalTouchedGates": 50.999999999999986, "totalTwoQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 36.25}, "bv_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.05, "timeInSingleQubitDecomposition": 1.3500000000000003, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 35.55}, "ghz_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.15000000000000002, "timeInSingleQubitDecomposition": 1.5000000000000004, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 34.3}, "vqe_su2_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 19.4, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 452.15000000000003, "totalCircuitCollections": 20.0, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 198.00000000000006, "totalTwoQubitDecompositions": 15.0, "twoQubitCreationTime": 38.65000000000001}, "qnn_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 3.2999999999999994, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 24.749999999999993, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 13.000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 38.55}, "qpeexact_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 1.0000000000000002, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 1.8000000000000005, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 17.7, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 35.45}, "qnn_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.999999999999999, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.3000000000000003, "timeInSingleQubitDecomposition": 2.250000000000001, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 5.999999999999998, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 33.55}, "vqe_real_amp_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 0.0, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 7.399999999999998, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 241.75000000000003, "totalCircuitCollections": 11.999999999999996, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 67.99999999999999, "totalTwoQubitDecompositions": 9.0, "twoQubitCreationTime": 37.85}, "qftentangled_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.8500000000000002, "timeInSingleQubitDecomposition": 1.0000000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 34.2}, "bv_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": 2.0000000000000004, "successfulSingleQubitDecompositions": 2.0000000000000004, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 0.0, "timeInSingleQubitDecomposition": 1.2500000000000002, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.0000000000000004, "totalSingleQubitDecompositions": 2.0000000000000004, "totalTouchedGates": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 33.35000000000001}, "randomcircuit_indep_1_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -1.0000000000000002, "successfulSingleQubitDecompositions": 1.0000000000000002, "successfulTwoQubitDecompositions": 0.0, "timeInCircuitCollection": 1.0000000000000002, "timeInSingleQubitDecomposition": 2.1500000000000004, "timeInTwoQubitDecomposition": 0.0, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 1.0000000000000002, "totalTouchedGates": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "twoQubitCreationTime": 34.099999999999994}, "qft_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -23.999999999999993, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.0500000000000007, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 39.8, "totalCircuitCollections": 2.999999999999999, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 33.800000000000004}, "qaoa_indep_2_none_O0": {"numberOfTwoQubitCreations": 1.0000000000000002, "subCircuitComplexityChange": -6.999999999999997, "successfulSingleQubitDecompositions": 0.0, "successfulTwoQubitDecompositions": 1.0000000000000002, "timeInCircuitCollection": 2.2000000000000006, "timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 33.0, "totalCircuitCollections": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "totalTouchedGates": 8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 33.15}} \ No newline at end of file diff --git a/thesis-evaluation/evaluate_cache_qiskit-rust.json b/thesis-evaluation/evaluate_cache_qiskit-rust.json deleted file mode 100644 index 4227982bc2..0000000000 --- a/thesis-evaluation/evaluate_cache_qiskit-rust.json +++ /dev/null @@ -1 +0,0 @@ -{"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.8548499999999994, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 75.10000000000002, "timeInCircuitCollection": 58.458099999999995}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 47.050000000000004, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.699999999999996, "timeInCircuitCollection": 80.51124999999999}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 46.650000000000006, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.050000000000004, "timeInCircuitCollection": 96.09929999999997}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.099999999999994, "timeInCircuitCollection": 39.7703}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 9.42315, "timeInTwoQubitDecomposition": 138.75, "subCircuitComplexityChange": -65.99999999999997, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 42.599999999999994, "timeInCircuitCollection": 240.12545}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 6.2312, "timeInTwoQubitDecomposition": 36.25, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 2.0000000000000004, "twoQubitCreationTime": 36.349999999999994, "timeInCircuitCollection": 161.75055}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 57.84999999999999, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.5, "timeInCircuitCollection": 111.99449999999999}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.7302500000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 35.7, "timeInCircuitCollection": 53.66680000000001}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 60.5, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 35.7, "timeInCircuitCollection": 170.19494999999998}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 55.849999999999994, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.65, "timeInCircuitCollection": 65.83065000000002}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 37.20000000000001, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.6, "timeInCircuitCollection": 77.40250000000002}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 35.949999999999996, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.75, "timeInCircuitCollection": 48.24609999999999}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 38.20000000000001, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 35.95, "timeInCircuitCollection": 61.87939999999998}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 37.400000000000006, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.2, "timeInCircuitCollection": 119.5399}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 3.05645, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.8, "timeInCircuitCollection": 52.255399999999995}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 58.499999999999986, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.1, "timeInCircuitCollection": 111.47349999999999}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.3413, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 35.65, "timeInCircuitCollection": 34.41895}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.9997000000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 35.55, "timeInCircuitCollection": 80.91534999999999}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 2.40145, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 42.05, "timeInCircuitCollection": 50.45795}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 46.05, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.699999999999996, "timeInCircuitCollection": 85.81044999999999}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 47.34999999999999, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.24999999999999, "timeInCircuitCollection": 69.3054}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 54.2, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.849999999999994, "timeInCircuitCollection": 72.90065}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 3.4193000000000007, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.699999999999996, "timeInCircuitCollection": 70.28235}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 3.20185, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.45, "timeInCircuitCollection": 112.36549999999997}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.3529500000000003, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.699999999999996, "timeInCircuitCollection": 33.20055000000001}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.7712499999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.25, "timeInCircuitCollection": 35.09595}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.0303, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 42.15, "timeInCircuitCollection": 43.899950000000004}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 47.900000000000006, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 36.7, "timeInCircuitCollection": 66.68315}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 36.300000000000004, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 35.94999999999999, "timeInCircuitCollection": 71.29590000000002}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 2.7689000000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.050000000000004, "timeInCircuitCollection": 34.67245}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 3.1816999999999998, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 36.9, "timeInCircuitCollection": 47.785199999999996}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 36.0, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 37.550000000000004, "timeInCircuitCollection": 57.038399999999996}} \ No newline at end of file diff --git a/thesis-evaluation/evaluate_cache_qiskit.json b/thesis-evaluation/evaluate_cache_qiskit.json deleted file mode 100644 index 788554811d..0000000000 --- a/thesis-evaluation/evaluate_cache_qiskit.json +++ /dev/null @@ -1 +0,0 @@ -{"vqe_real_amp_indep_1_none_O0": {"timeInSingleQubitDecomposition": 264.43199999999996, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 199.43335000000002, "timeInCircuitCollection": 59.2014}, "randomcircuit_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 83.80085, "subCircuitComplexityChange": -2.999999999999999, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 168.27904999999998, "timeInCircuitCollection": 93.10855}, "qaoa_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 77.07875000000001, "subCircuitComplexityChange": -11.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 178.32835000000003, "timeInCircuitCollection": 98.72450000000002}, "modular_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 177.21545000000003, "timeInCircuitCollection": 39.446400000000004}, "bmw_quark_cardinality_indep_2_none_O0": {"timeInSingleQubitDecomposition": 761.3143500000001, "timeInTwoQubitDecomposition": 243.00244999999998, "subCircuitComplexityChange": -65.99999999999997, "totalTwoQubitDecompositions": 2.999999999999999, "totalSingleQubitDecompositions": 2.999999999999999, "twoQubitCreationTime": 165.56415000000004, "timeInCircuitCollection": 254.7787}, "bmw_quark_copula_indep_2_none_O0": {"timeInSingleQubitDecomposition": 523.38045, "timeInTwoQubitDecomposition": 76.15485, "subCircuitComplexityChange": -2.0000000000000004, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 2.0000000000000004, "twoQubitCreationTime": 158.5612, "timeInCircuitCollection": 181.98405000000005}, "vqe_two_local_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 85.7912, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 159.42615, "timeInCircuitCollection": 122.65265}, "dj_indep_1_none_O0": {"timeInSingleQubitDecomposition": 260.73065, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 167.69315, "timeInCircuitCollection": 53.40375000000001}, "vqe_su2_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 85.31580000000001, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 161.68800000000002, "timeInCircuitCollection": 189.5436}, "qft_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 79.58419999999998, "subCircuitComplexityChange": -26.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 161.73495000000005, "timeInCircuitCollection": 67.783}, "qpeexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 80.73055000000001, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 159.6042, "timeInCircuitCollection": 65.9371}, "ghz_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 73.37, "subCircuitComplexityChange": -8.000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 167.34759999999997, "timeInCircuitCollection": 48.5079}, "draper_qft_adder_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 79.67949999999999, "subCircuitComplexityChange": -5.999999999999998, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 168.67844999999997, "timeInCircuitCollection": 65.31615}, "qnn_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 78.5832, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 169.59955000000005, "timeInCircuitCollection": 123.81310000000002}, "qaoa_indep_1_none_O0": {"timeInSingleQubitDecomposition": 267.62335, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 162.4891, "timeInCircuitCollection": 52.226400000000005}, "vqe_real_amp_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 97.20599999999999, "subCircuitComplexityChange": -10.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 170.1434, "timeInCircuitCollection": 124.39940000000001}, "wstate_indep_1_none_O0": {"timeInSingleQubitDecomposition": 258.92439999999993, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 160.23515, "timeInCircuitCollection": 35.643}, "vqe_su2_indep_1_none_O0": {"timeInSingleQubitDecomposition": 262.54715, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 5.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 167.28905, "timeInCircuitCollection": 85.82059999999998}, "bv_indep_2_none_O0": {"timeInSingleQubitDecomposition": 257.01705, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 172.08350000000002, "timeInCircuitCollection": 55.94029999999998}, "qftentangled_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 79.3452, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 172.99985000000004, "timeInCircuitCollection": 96.4836}, "ae_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 88.747, "subCircuitComplexityChange": -20.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 167.66315, "timeInCircuitCollection": 71.0322}, "wstate_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 80.0976, "subCircuitComplexityChange": -9.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 161.01495, "timeInCircuitCollection": 81.10095}, "qnn_indep_1_none_O0": {"timeInSingleQubitDecomposition": 257.62100000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.999999999999999, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 165.66219999999998, "timeInCircuitCollection": 73.962}, "bmw_quark_cardinality_indep_1_none_O0": {"timeInSingleQubitDecomposition": 253.68435, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 167.87064999999996, "timeInCircuitCollection": 124.00604999999999}, "bv_indep_1_none_O0": {"timeInSingleQubitDecomposition": 256.0220500000001, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 0.0, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 167.6567, "timeInCircuitCollection": 33.93079999999999}, "qft_indep_1_none_O0": {"timeInSingleQubitDecomposition": 254.33225000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 169.9614, "timeInCircuitCollection": 34.624249999999996}, "qftentangled_indep_1_none_O0": {"timeInSingleQubitDecomposition": 245.74995000000004, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": 2.0000000000000004, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 168.23465, "timeInCircuitCollection": 42.5586}, "qpeinexact_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 80.17750000000001, "subCircuitComplexityChange": -21.000000000000007, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 165.7668, "timeInCircuitCollection": 65.5954}, "dj_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 74.43695, "subCircuitComplexityChange": -5.0, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 159.21775000000002, "timeInCircuitCollection": 72.15804999999999}, "ghz_indep_1_none_O0": {"timeInSingleQubitDecomposition": 260.89835, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 158.89270000000002, "timeInCircuitCollection": 35.25915}, "randomcircuit_indep_1_none_O0": {"timeInSingleQubitDecomposition": 257.26304999999996, "timeInTwoQubitDecomposition": 0.0, "subCircuitComplexityChange": -1.0000000000000002, "totalTwoQubitDecompositions": 0.0, "totalSingleQubitDecompositions": 1.0000000000000002, "twoQubitCreationTime": 163.44439999999997, "timeInCircuitCollection": 48.8252}, "grover_indep_2_none_O0": {"timeInSingleQubitDecomposition": 0.0, "timeInTwoQubitDecomposition": 80.70155, "subCircuitComplexityChange": -4.000000000000001, "totalTwoQubitDecompositions": 1.0000000000000002, "totalSingleQubitDecompositions": 0.0, "twoQubitCreationTime": 161.03295000000003, "timeInCircuitCollection": 57.965450000000004}} \ No newline at end of file diff --git a/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf b/thesis-evaluation/figures/numberOfTwoQubitCreations.pdf deleted file mode 100644 index 65e9d6c6036f2f19255b1e7900af1ecdab59359c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16590 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

sT#X6`K7lsWAAOa$a68?c|!wz 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 diff --git a/thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf b/thesis-evaluation/figures/successfulSingleQubitDecompositions.pdf deleted file mode 100644 index b56f3f4cc259286aecddb19ca555a0d87fd99857..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19014 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 diff --git a/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf b/thesis-evaluation/figures/successfulTwoQubitDecompositions.pdf deleted file mode 100644 index 9e0f2cdd199bf8de7149103092464875d4890c3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18682 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 a)Xm+*)%|2Tfd~Pg@ge5olGBhU{(k^AW3em% diff --git a/thesis-evaluation/figures/timeInCircuitCollection.pdf b/thesis-evaluation/figures/timeInCircuitCollection.pdf deleted file mode 100644 index 07cd40761ae0638747cbc6b6fc9b6a2dd753e40e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22475 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 O2#lDUTR}^a`2PU-xmK|N diff --git a/thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf b/thesis-evaluation/figures/timeInSingleQubitDecomposition.pdf deleted file mode 100644 index d5bd3fbf607c8ea05db214396cbbe53120c93527..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19596 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 Am;e9( diff --git a/thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf b/thesis-evaluation/figures/timeInTwoQubitDecomposition.pdf deleted file mode 100644 index 480c706931a4068e29ef466ee7e78171623ad6a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18702 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%0DCcp!0(A4{Y?HKZ)U??pdv4hxJ@|- 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 P__Plp2ZzFCMZ*6FrT~#E diff --git a/thesis-evaluation/figures/timePerSingleQubitDecomposition.pdf b/thesis-evaluation/figures/timePerSingleQubitDecomposition.pdf deleted file mode 100644 index 6c506a52cd41c7e404b2073ef22cb76a83194e3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18251 zcmb_^1z1#D7bqn$5=x`QARr_5<^HgA|M@7(jg)sAdQ3|A<~_KAV?@6 zB?uxVpu97v_uk*j|9-#sz2V#Jeb$Z@d!4n`+57Cnp(-WA3*+MlaTHC03u{3zFcfTW zatkCP0)||_?SKJ8B#m8+ZR{<;5LIIfj58PxG*AbNiGeV7X4r;;|L7oNZ|4F=95;aI zsF>Wsn7V)keq2g>xX5a_7`tG={67Gy#x5=xCp$0_dkKPQSQ?vI*;#;5KWm-rO*Jqs zV0{3slng)$#=`{+k+TIzkocKP{>(VF1BKZ1Cb(wRXPMiAXPt{5Nmr8C6SQ%r=KdaL|H^GMS4|I> zMF*mCw6D2BZ>yQCheyMO;llmb>oZ3K9q#Kdy}$KsyU!RJjoYt_8P0CsIXrM68(aUp z&NAlV9eJ095BKFa4>?|&n#d85&o{rjO04#oQR-ByO-K7cIc7AJf8LbnpemfvKoY~#mjkbQC_x|nJ;tf7J27Ly~00UEdoY4+)F&nlWdf^9gHjaVNw>!Ln zuVfl>32=&Yg(BIW@XYOhij9AIfwx(tYT8tl=qo8|mNq|2 z%JQJ&G($|KH++gi)#WAXUscmz>=qj2;8L{2*syMcvY@OcAk8bHQG&d7=xgG&W^ruY zhg-&N_b=7>N^R;?u_FU)!>2Bn_Zz!KWZKxqojJchSZhol%uV zgz{Uc4izPb{*|m=m6@W>E7Ee{0TKV)R~5cy;bamp+k;gy$#Kdvk^VjvHWMk4A8f1K zlL(pFE^-L8M)aI@j$v%3z3&%#I`Vsm-YAre%uJtsIMJ&5l=#9>XyqHln#{_6bX8zVYhD zh}a<4hkFB-;Ub0+h?@~NEiC){FUtzLio5yRtJRowVB*r^U1ZLdMejUyKVurTZ#vdL ze|X`8g-x=i_f2Q@*#i5>{7}Oz&UJrlX)pwq5R;f2WRzU~c?!!-GS1*vv-&BIR{ArAdq}E~9C4QXm&0AId$nkx~@>`mv&b_a2=vGs87;>I3;}ls)JrL@br|KIY zuv!**q>=2n5oj?|CE^e)!ElbH{?ogwVXYFDUDfT>Y*#W%2-GWh z7SVlLW;F?M@g7my6-nl?C`Y~zglBRHcLMI^WR8)UdYK2ve+UhlKz=}5b_zzRuDs)q ze)`&I@jk4S?Upw}HiDJjKSj$PzkP5eNLs!^d!RBn2Sb{C_EE66Ux);g4%%PkX z&Xm~xmcA+X#`%8T$hEvs7rd=;&vaGu>+xhvvcl9xj_E`cp_jyJN9Y&#h0IilT)WSn z;pW$>OF`>@Ab!Hv8KvAM{AA&I--9PzVwsj-R+dLK19b&9ePWcF;DaJdcm$;vK(T7 z>Yb zl)CtZ>JGl{Be8PnRzE(rmkY%oLZ66rPH(-+=c-<=C zjOJ=FXP{&;4~ov+O=C@%*Vlg0)vXs8XF*DqrdgZ#j-05QBCWH{*skYrg36|n!+l{) z$DDMMGceinetDq+-(|^1*wiv+gxeej)ENbNApfKC{!@EH|7 z5$#&WFve(`>ed?~F~Rly0RmwTOTa1`-u#093guwm7w>QVu zdnX(J2BfJIw!jV&O1Th|*%%|ymh8QBX5qF)?DplTFNKLLWyV^Xsg)z`HIMg4Y3htp zLbNw|^eovt>stJr2xu&l5pPv{;NrLa$#nq*2(xT{?-``3!J;0F>5^r|D!>85y?r5j z%4L7S%yXA)?H-t^+?)O3hKveTW`l*tBxTV)RRil`uYjZ8pD<~BzF6M201%@$CUNzS2 z;oCqV2%=YIBPMCJ%)$h)gg$^J3{N!H+5GIFaJaC4G*Uk|Ql<2zVKvzP#-qE&efrTb zQMxYWO;n*(rzc208HYD^x`^}Q>J74Ce;RvHLlXHHiW61LE%H(|OOYRFL$~dUgl>{Q znRUWEx+Lw?T>Ea;bZ%WdTy326QhZGg0QH>loeJD06?r<(o4-38zl zGMN=O#2A@ja(cc4O+!tng0{ri;l{;YeEnqoc(q!LR0_+**v8`VeBLN<*6DD+CK}X|LG;+tldkxUWN#{g;{m+9DAw(bU zOxwJ-*NJ|(rR1g__axEJ@rLg0QFci(&n4H{T@f((h(bV+-3R4VpAo=dE^e*o%Pv3k zGRHNbcvg^n&0WxeAd0K%MGFak)X-Id4ib(xyy96cMark#i&d;rL?4i^Ii{r|0}?U* zn*>bd>;sHc1A)!kAmb{&?5&B|2pKaG;@LOz(0eg49^z@M?7W3EIqKHRXSBWXg`a6T zE1!7;l5JN3vWYW3V?9JwRcz^0t(uYY&-!|dc0`^wM%{)f3OZz6Y8)+~Ydy6{p?I{k zDb8XeD)FVlX-+r$g44pW6+ z8gHX+jKkcvh1$u+!|-HZ4c}Oek53q{2peP$ksFPu z<)yMw85~LkLKU`DM~BigD-}dsS9Hr+TiXMuOCDd&GFo?67aW(nuEnjq?1{WeSxIgu zb?d7#Xbej8Xcfr7FTzF^iq}FHvmIPRB-2PagRBEk1xDjgaz(fJbkS+K_qFg5T4zV~ z=Oq^`g4;uMA5wqPdsJTj#p~W6c?j1iL29cl;QqtNEv|H#cC=PxZoDu_uA$(4`P7W< z%y|lRkC8$a-rB6;<}Ss}az1->TOQj~rKRDFL*UY@dd3oc{GDOLntoc<(}(F!+oNu+ z&i9^w(!ZLpAY`1*?G^}z4>3d)gv4Z4uP9SLDp#`Rh`Cf!#dhTapb@lyM&zU%rd|eS zBe3s2CM8`&JeVh+Z9od~-r=D{M*eB8rt<8^XP78JHD;}mYG2$z=y5q)EpBp-9JEj<&&mJv;7z%FLy_dNlR!RCL zHd@h3fiJKW+a6-{yLb-A7xm!dvh6h1T3bJ;s&Bo3Jg3m-~%y+CjD`g!l z$AX{nR@_n`oQUQ9VpZ?t_34z1wYSXeE`MA!gU4XA>w(l7t)2T($aO_?^#oU+=N;aU9)CmvgH2mG5*PdP+WVh4s@0WKGJdjKOq9}Eq ze2kkgwQi7-R6z)D=yOJ(^}f}}xpM91F6Dx9z0Vdl&PC!1t3pGP&Ju!&Qlvh-{W_bY zD4HAkwJX`Fe=XqZj02kQW~f8qvaKtD&bWgqS9u ziP2zoZ1yWUjBgm19@mq zCz@4BeECj%;lYQ(Cr8Qm^W9B=iLHB6q z3g6v&pR*11-+A$ck!ks92$z)kea~D16a1M5bh}?in zjLyZ#H}`=yq0&0%3r0)9HdYqva}J4%{w1dez1CzimfMn(@X4MuhSI=9?!2atzW=o< zj84dtW$uHP(2G;tY-DLo67?CYMYWgaRENHgH<#(4mvUP&b5Gw-k}M9Q>Bm#Yc_DIs z+vLI~!vnS9mSsqYo^+J2Gf5P=tH^m*-HZhUOJ*+7;~V7L)Dc8JRtzosHFGZJnXbD1|J74#Iq zNRuJvn+;OFgIZ~OZgjU|FqwN~Y8UkI{x-U!wApJ?ZN^1GLwIEI;$6+09sN+7bq6&w zLis z5SF`uzH}UY+v0R-`LGnMePi*P<2Hqb{pyTjNcoPU?rzK(&wB3;bxE$ZzAH;D4?KC{ zA07^q&W&qrF%}8X*(l2PeBD|!){1$5Gq#W9e4P*Ln*{%5#JM~Ac;@ZyX~8LXpD(q8 zUAsli+j+98s&CpvEF|?Nwo~+bd8O2bdI6+f+D2(XQbf@}P?%4QHhCV|){3RpXO55D-WrEYD;W=)wuBiR zY@`{Q+Pwn(@!?}+$QwyMm{MDA-eHQlL=ryKqu)`_^&n%RfKt8-aj_VxGlzkrP zNbD^b=(=v0|0X}rGBuaH8%GK=d^r=QqnpEF4KkCayCWKgAs8Wv7!_JoEG(hVYi+az zKiqZeuw~+vXbaA0=A!qfC>)$R_ubxpdh_!4y{euTeLezv`8})bPel1C`EPspKJ@i; zIfqhvOExTi^PCEfGa{bDDIt&8hCh>fo!+I>a<-f7)jg6+1%8|0hboe->F3x6t7l@3 z8V>SkoAo|hwFLMwP=NP08X2cRijV3-pD*xO8__@rDhW-JsQ9WdhP-4k- z#U|qS8h8iSH7UBjj|F>qHc{wzJ}03iWqCXilz?yVU&iTr)6fW?MuL{XZ2qkk&j_>fbTlPF))l@#S_09W$qfLog}#B#UV$l1j0 z4i;0=52ww(yJ@gogrl4CRB~XMVOuJQr(-_0d&<8uPNd1ai13=-oqikt%3Bicb^f6W zdVnV--U5Dg#2%fto66KMU4f{INuF8O<%Te_Bd~ad}k&l zwL*69~!~nO5E~d$IC7}sYiTeuk#KA^YRxW@^_}M{w+s1c7x+LwMOv6{y>iE z!ml2P6MuZEv4oedJKb|Ttt)%a`3Qw4OvCe30`C1*uwCFNPoK2P3*`8?SE_{81FleVTWzS1J z#lB3no9}DSiFi*fO)yIr&o1Cdr^M7Y z)T`rMH>j#U-%uA47iYAa6qnb?&6?Jx*9?I1L9;G}ZuB9+n|-zg7E! zduRJgFMGsWMmH__+$J>nCrljL->g*lqD}MMnJ?3#{xQh{FT;8Lxs%XiX8UC<(&v{0 zX7|$yxhiNl4kZ&r%4W|UJ(@ZA`sMIDQN$%5wi9OOpF3Ya??V*oe@_kq!G9g9I*7!h zG!MQAT98QuaRnP(6$?%1IYrfjThXi7>&s)Rp~0SrdmjZy1`MBJ$iQ_Vl)6i(eGW(6 zn!51>O0b(;zv&5_{}0Tcbc~YIDOTdZ-SjNTM!~$c=s>o*uHvVcWPSDQ+RLEO0N#65OjAF1#T$p5mbL+|Hl{(}FP8egnG5p(ST^UAU86uMWq5Qh$HWM&F-$TfT_%VFv%w+3pSZcxx|LDwWc* z`RjeGdVR%I=k7hV@em*BVn6$gESxjZnN{s;!TCZVzMF9zvs=EC842cIp6YUEp#fe+ zU6#FgsKK~83%d4H1!n0eZT{q^!g9zB-`&}&hFz3n^cT9lAorOgZgTH2Jh8ht3f~P0 zPY@n<|MWNE2_XJp8mstKp{h@z8iPoF)OBb%zZO&gp?VEAj|cM8sWE_-F+zKKDO=(E z0+J@nsSFaB7#;AO)AO5zi;cZK@4Mg0;KPHG@`v*ZNsM=40 z{9(>bJWp-N=@gj)F@z@c45{FHPS4@bU*YWM(@LK}40gNtH)7xj z`2VH`sU~OF3MH*OpgC;B8U0kx*_IKqb2O&jO-+22qv|<_i76W%1h$6`o!pB>KECK< z;iP@YqY@MUs+Arz7k(+^< z*QP@#s#c=_^S-}glgXJ`f9X+xjo7VFsiaF5@4}jzE)9~e)~s2~YO-1yPCcz2HA=$>)0SOwMTst=51K)N&gU_!TGu$k3NA#gAfmX^FW-6O zLCdq&iPGnc6`cpaS62_~%6SyueZ{sr9wcNwM8HE?^UjP)NwB9*PCbGl9=^hy>vqAr zNTlKhYg$_X7V%)NVQmHh!|n%dpDFcgteA+&z3in0CE6Ofd~#w8}}oTh*UvhaxR#jrXHd^_)xHD0Id8QV2}l)QLBLv2qz zkIzKHF!cOD=GhI=OlwNrvKODxJdabH8;Dqfo)aK4aZSDD;5B&*39MAgq^^wQdfCm- ztxZ~H^@c3QN&C@Q#aKmHn77n$D8vHbID;r`t5 zqa-d_TCD}W?|q_C~yaU)19IX}hiQ>=vNdnMruz09C& zxG*pGvpcOTc7FR;dek?*b?xpkl~VK_8l;^d!IL7yKXC_@Xg`G3VBLGyq=V0wwrH`Ti5w^I3038(PKJq3 zC@xDJ&DwCe=Es|!c5YcVk2ROSGU6b}p-zpV9QsTRL(@2k-iAxkk@voFUscn+df55e zt!Wz8by7=@Sc!5^==t z#W>_fW-Y~qkMk1OmPPz`ETo-eB1Xamg;-`tR96oDh2=|3HTjCzH>3K%C z^GzVbx}8(>QM=OM3@SbN%Yq_Sb8 z!SY?*#|4GgVORPF=pItkF)>V?K-vlM3%|hscF-q^D)EdKADto(Q7_(5q66qcdQ$=+ zPK^Z_=Lu*}h*IE4!T*g{I!3{X08A{ko9@r%dpIu=Cy#;E9HGei%}{PhdEWx%*>Wj) z7O8JTy}LI1%&L~%(OSaiPf<%>O77BJD#I%j)@V(!4!iZy6f^&PSeUTwe7_+&-^}rG zgzI(J%MOYI3EOH@U)^-$y=BQUpf^R%j5hDOZCmI$WgZlNBHs-It8i+_wylB3H;6bR zy%P$#9Uj{33@Jd#3BE~CCC`Un{YdK9-lwrOPq{ib=eFo~)Bg6^YvX4+V_4~#{M{ZJ z9LcDCER8d6_#{`}qBWo17=zGs9HL`B_aL~x=4Mh| zfa3bsE29JZ-|?Bb%=b?aPqA9vpp~NSV5o z69Xlb?7dDcq&2%Krnb-t`;1vr`bH9VV2ipEb5Edr@~I}2#f+}VT_jbugU5qxbosV1PCV7dm?G0t4~ zjoh44)!%qM4x7&Zv{EAQkyL@1d@xo?o0^6%Dh+=uK94aeW}v@QVwFlalQ&bGNk2sS z66YjFG5>V%{DWDYm(K2*0q2k%FD@==KW^ScPFjg~-ljt>^{==y`#I^KW|az1)_Yfb z^m9H_K3z=3 zWKQlG|Ck};j1bNATGqT%?x+5*k`CK9wqKe|p&x|#b#*fl_pNA5enkmq)z)@>&+eEk z8uQ^^)ik!!Rp=q9epqLIm1I%_Jy9rng#{$Ea{{j?L`MktADBj@TAghhlynt&iVh0) z;pm$KUyG@}`#^Xi|Jrapi>47g$AHhZ>$0x#W{#xmyO<fSI}?bWh>bJ(%eZLVf>KVf@lnY?>4gUW>2-YiqUyy`%JqQd(K_XfiqvlFJ` zgfI&Z`-A&dRJA6O07lwl7<5jj(Q_@|f;LP9rWCoM#$VK&3idrmD@D zY(eak*}C9sg)o=tg9o#3j#Yu3Iwf z!de|u)uS?BT07(nKt6D4yg5NcC&YMv;y0xu>ac5-Qg?E168K!)5mSqAo_GW|00nC`p7)OkrmDTHd+0l zGVcfaq(@74yrOVK+omHYhdO%bB4nQN<_+CS5Tt-oX$&Pe7RLC+QxQs9$ZGSS>y- zlt4B|qBB!ov^dc^w4)sekd zgWi`G_67}Hd)p}#+H*hrc`R?x9Vwo9SgS~@|9atPX6CO&ynGK0Q1<{yAoU%spiPy_46b1*;CFw5`buC>UP}OG#4zw$)C^u*$KC+@|Nb*he zw41D`Q%p$SQr^%|(@6DX_9erUcU&H4FXt1c<%9?o0W2W@&n*xn@HBM_SaToX zdVV!$L#6*qK|y%KfxR`=3mQe}%{eZJ?kk0c5stSxE%W*;yBuW_)6r|Gz3Oedv=!6? zyrr@OXRCQZ1Zq^GIw=sE3nK$tOG0@L@{jzqiL0s4*9XYq1l-yqV$MN;RTr|b^PCR7!{XA{=J5!oi zU)d*Rlw?+djiHU{MuKfdU#QUJjiIIwQ+Y^DEiXRtj0$TUOs~i>Ztq~$+x|jvec3DI zgo!vI9{;lkCzLi6N5W8n?UX6_?PRAqYuo*v&Jlv%-VgpH3l6&U0h%8i7_M3+XEBzL zmhj^o1d_9yfc=E9{7+mG0#&IB^=+Y+1V(0Bz*j$EqoYA`Jtnh}r*|;#06Sz4cWJCZi;SX*XG!S!>e*6aV2K(B*T^|R+sKb|J`Qz01 zjxOr)!g|-;dP0J(KU;PATA46GDKPJj32G(jDx_Rf55AUHuf_U$Jq2D`jkF->RL^Oi z&z^Ve^1srO^W=2L*Q&D851mZW%sMn|nNp4Lw$`O{iT$6Rn(O-{S~W&{1g3G1@^xOQ(h^EPieVd#sL#CsqntuR_2@y{8uk={G>*LA={Ym9OZ5n-w^zHWXK*GSQK zc-+)jwh)(KR7(@*r6tH!kRb-~yXU{F>H;&09y>X*YNzIEW4kNfGm)hRL z7%ZyCFe7JH4NFB+#Jh?NS~;lb6ki*&S&&~zf%8>5iwb}jWpt8^@7rmeLr_NX2VG+g zDtM6HPasYiQ6dwGa-u%Sfke9mYewe5R-JimZwx32S`V)36m8DOGaEYcaneDG7`L`j z_;hMkMVnWzHGZ?B*mf?DoAFRs?57=`yVKXT*?j$G+k-K?tEktrGxec!2N9)e4hx1?u5s^Vh zc0L!grHCl*2aI!#6=2iZA;F`r=VGle@u@6cog}x8@m@;jW;hJ5N~UXEB&(64!+@C5 zEQzI|5q!Zg`m5F#-MFoRYT>Eq+Dh{qqFIe$tHf{WVM-OU z>9=x09VMK3=GpaCr-FMGQxCWuXtoZABtNa4dd4O)a{}8Z1e|c#A8kFO$$>|~)m@tJ zSJ@*V9x7Rv`o6#Hix|>kZZZh2jAu>RRFBJJ{h%S{nZlP=J2}>Si+?e(lGoYyc~w26 zIJPm&JT#!mD)~8XstB&+*ifFDBpvhMNRGy(r;`L?_p0qErc9stB~`I5hJj5kG4P1@ zz5gBek?i%vf6E6D z#oyZ#Kh7H-A0mWE7&~KrmXy@gq_nuDFt?1gTs4gCoUumH$=OBH(%1>;3{f=xSq8TB zK@e;oDU7qJla+&uy%QMr^R%IctBK3;X+|s#z)2_@+X80-C4L|JM}LVQ=Ky(OP&f?C z%MU!c!vxU$z}Y>(y#UAmuqc#_U7W0r4-4`^q1Xr%`1`A52!ddVQw7d20;d9xPc(uc z(ssZhKkNy|zfV^F0)Ze>R_5jy;Cv$XK%zbvyPxIkU~CGUl>{`!^0tE|#tw{KLvyw< zcD4jVJTXr8V2HgPz=4Z9P;#+!!T@D+;6x+v-^vXrJ6m~xkU$SRE1(q$cm|XL3jmi& zU_roase!T2JQ~2MN-!F@G;uOEwZ^#EV9Z^>XrSWfwUd>FB>({Wnt&jt!0ASyDF(=Z z78XDTP-X>Wz@XM(hz%HG3m^lC0z(|YFsy<*19X8Qu3#7zqpd6Oatv_35u$Bn=3)s5 z<;Nk>zmG~D&(p7Ms=xK%A2WF@_Wv?%z?|3^TQ~!P`=bdIcQ(Zy1x3Swrz;*3z z0qm7a8arIW9PBuX+T~7MrkR{7Og!3{VlQ00jAg1PlbIlYjtl4X8L0KrVnyNH`dd zMuLC@Mj=sPB%rr~z&$4bEIA4Y0t5gWcbsGE5kLZt_zD31v16hI(LnwKhRu(eK?s1b zIhF`4Q`m$86b{=Lcya6J7(iP9@9}uR!y;Tj@Him_(8oC%@JuKGj)K7f27ln?2k;^R zgMdI|`|*R3KrRRf3!5J&;BYWj7+4`-b1eSjNy7>dpaBpp2#XU5^f?yFFXFKIvHoB& z@nbKbSP@~##El){u=5>0U+?l5O9EFpc@iP6+rnfHTX4MzjA7l2{eJ|-Wl2Y-!^ZH49OSeXEY!e0M13YI7+6m(qoa{x5(lEANoRfHcp zfYpQlOvkbY^a86B|47IBfYpg#=~!2O>I7CF{+Y0J9bW@uV0GhH!g7v+B7r#sXvONl z2?;F-%n4R60IT)S^h+mxE&LmZmDImlV#WW9 z?;pPT$EP453RY&=cm=r0u!21PGJ-4MOOILq`OYkGWBuJ(68O3SHY&RWgij7O_AWM7 zCSW%N9}FOz2W;u$;@~U>>De$#~e|B>)GY6Ys;}$!!Uqb** zj}che+eu+RG;v8v2oCJ}0zn*btHOa>lp6}?ghDyF{vjC;CyY4=uzoNQ;HG~50560D z^w%8x7YrN2VSiw|pD-8-jlBVWgP{QH{x29{?S6*|0+#V#FuHlnpLI7LpzrzFo?tg;`z_8KcfA$5w%<-?j0?2>K87d%fLSI4PUEtrw z6NCZb((f>Sz*GDV6NLVY7C|)NzJF_nM*K?;plIa3Wq?KjYjeN%6@>n-FHkfZYpt=r zzjOn5T0#Ea4hYkMFz@$v0CT^?VDNwG8W6=pf9DqlLjlp*Z{xwRUj8>293k*89B_dX z+6e+4{`bCUAnf@KhJXQ2D!;=}|B^in2+sZ`7k(HJW&YL|1qkqOn2VFKl?}$}_@g%( yR-PEZ9)Kb0_V!p)cI+?Y?9AGslTmk|B diff --git a/thesis-evaluation/figures/timePerTwoQubitDecomposition.pdf b/thesis-evaluation/figures/timePerTwoQubitDecomposition.pdf deleted file mode 100644 index 1f9df089b22637dc2d81a2ec0c9b6901fb7287ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17825 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 diff --git a/thesis-evaluation/figures/totalCircuitCollections.pdf b/thesis-evaluation/figures/totalCircuitCollections.pdf deleted file mode 100644 index c678afae330f48b2a0c2180a597b19465ccfdcef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17525 zcmb_^1z1$i7pQc12?#7LQXA~TQqqld3P^XC5-Kf7cSuW0OP6%3l+surAt54yAj-Ro z`U~a%etz$J>o?5YxihEloO5UHkzHLzju*lwfX`mO04i(2hk(E!7jt`jQBe@TwvVe7 zh+o>w)6CJu2E?y!W@F_6f&v3HLE_^0R?e2FfkOY7Am`%j34)yt;MZ3(x3{wJ1POk> zl)d38ujOgxX$2Da-l1;h>1pNe41%LB@%goE%`EMlZ9s@0cimkqw5&WqR{^v#asVk- zH#|Z7icSCtQa@_xAGIRL_#fmzerEthH;Q_9D}cK1*R=(-Yq07-;03AI=$I&MsSv)<7(Jsy-ih=r8gzdO2`7OJsYk9V>7^Ne$lX|W>SD@ojL>*)`$wJ}QlH#{qb=+- z^7mi6!a3wOs^PGhv3-|mj`o1~>s)BT#6IEB;c#V*%woa&Is0%o2OpPu&aL?o#hyyV z$drmeTOOk8(%$ux`(uW@$yQ(O-!^?=*)pr5vv;}YuxUpENlqC=Sc>69P+b=p<`a1= z@qERxrIxnMIIPu+pUXkfzi>0*^=dyI#kY*sVMmc|Z~3(-_b$1VHmldn(`(tD^P3*> ziuYkNby)A^+Q=+(vaZZ|CsX?^kP|lYkrM<-lKIMNUF3P{MxuF+hc=rscgV6`Nfke@ ztCWiP2GXkOqTG;o#dfJp{g*zcw_>T65=)G>UU`!@ipeIrlv9XrLi!%>#|GH(&VR)y z?Kf5$FQV6&kc?PsCHu54Q>_D@e+^SF}GKIEto>Z5K-&{wR@#1ozNBFs7xpGlliE>k}>x$u| z>fHrAENr3jQk}xuD%Q{LW|VW6upT*PU`8K%MACrbQ z8+I-7>FnGESI759ebS(_&Yth@9nlww#CmjJ23sR-HZxKvwQJ6*iH0_}D?Xx%P`?Mu z&VQ|^(=>~ufiKaRP&Ktkf11I}0L-QQ)ao_EgizaB1Ltj$C;@1&vSIa=-lGdRDXG4R ztU8L4h7Wlr;zAPfHJg;<*Xn|EBj`wzJme_ts`0N7DFm;}ZewyZJ)W5k8eSVLrr|!% zQHqQ&FyXz>XcFSYUttpkvD?s>zi%GXWL!`0TV2ig+3_`wa7g>4da>Kv4VHy6zy8gK z^)L8)A4VBiE4Opkbrj8QSf2N}%)CR++QvDd8#14DO<{f~`<~Th5ed3@Nttb00*E-t ztpURccXjv|-E1!Gj;o*OFeW~c_6HPV5`;6ZB}zt+vV<0!S`q5YNe~qwv2%xb`+Ath zrv!~I=}Fr!r7uN;O$cYV`068qB4T96y&e>*-#h2XWJh7p^sF{0P)Ea7FFC;8)pdfK zb5&dhx3gs_KaS2g^T}UJ*urou5sKKVlITuF2#)D z3`7iynYneZ3I#Y+KLU;F5Mxp@_-#_P6Ua*S8fvPKQpSXYtArmf+89AtT+A7iWjZZs zlqvDp3KT%4@c8AI&Gz+ z*XSE3#%CjK^$NNZZtFJ>Cr@nB?XN}%Rqqxs6j(~0h(i-}iWR!FOxXmu2h~$j?j%?~ zxm%%Y$zxzuu3M(kN&ZlHSjUWbP{l^1_qo+F6-~>D5wp)+wAy{B>%=)>CZ=`COlfqY z_O4LX7SeX}E+}R3Md|J_a0hXf!uy~*Xz>wUydB_z9uRevkf!$3f;MscOirn{`h!Pk=Ztp;^$ z)QFP2HOIN69=tLeKl8#~`x^n=;kYvw_tZ<(riZiZii#3-kHt)NGPdqRDr^sSF}-A! zrikN0s}xE_ywY-x-z)f(V{;mO=&0gkY0(tOuNXDP8g68$aE>)f>UZ2ytjhX=ul>od z$0)wJCY&}`ia5;oSubSdc^b_qPl52gzWZ!875BY4F^yIz<{U0dl(#v7NL=p(A{e(6 zoq4ZJv$#d>nJ98%KcH#yYKTcj+qhBhxvk*Ur#OA=^_F|$orxFbB(9Jy9j{HYE@RVs zgn3O$VY1Kw4&99z-qPx9b7SS92HIunNQh3E7l$WSq+@Ko`>17iEpPrNbU#LUF}`5J zczdPzwOfe=D2trvv+3nNIRwXN-j9X0AGNxnRK`~Q4;I;;xl^Q<;0lx#aowef+D-5? zxe=i~;Yq);T8a$V6<|!72>Qyx_W%wvNKo;@HvG(@f3JzEbnxY%zMZ|SBe(qc!4z`o zdCT<6%14@N~iMw>&>mKzQPO)|Ks-El#H%c;^ z7;41s8@jGLwhQsgIma2?~mHq=+1#q8=g8|B`Vkmd6UEhNUAKJ!~@tV{ZK zq*+O^4U?-))x~IqA-&Ox6m@XA)s}|Y{)D+QCUYYcJ*H8n;($W4OR$tEsv5>J|w>UV??ToDLW7R#V&Ad)OpM&6OpK%Al|C zhh_2%E%35kAZ82Qwue6Lk&>{(&5Doxl>1b2oZyIV&u^H9g;{_{S<7J}{uPzh=OTCw zjy3ZtqQc>8Hu=yEr3H})fe9Y^{#86=fToNPYjg1}8d5Gpzr=bKF1EZy5{6Lx!Z^x4 z{OS9HyW!#X^LFi{T$tPp{?G1`;w2jMXeh+RnFkdxQdTAgb#JLjzSh5q8<8YjDw&hd zf;E3>%hqXq*!0+*FHvcp7`2hz9!MQgiK6hVIl~yk^=~lWI3XFt(^^qbj7xls6 zG8pTKCETm!;|dL9lei0;5u2kgnDl*eu}_f`)MOXci-%(EVU>3UrkwZNjO5avPm&6N=!m_e zm%|oM%VCD$4L%JEFCQ7?<@3a8k#eu6&k5@boFEluW!jL;3YOYTlC;-3lv=hYnz~&r zx9qit*}R}1tBbBvri{BDlu+I@(ydrIp0(c3*&(9Nc>i>b!3ixL{)Y7nh zr|A;2YF-UqZ19Z<12?J`QVDCaug>Usf9lOfKF5u@f8}xS0)z5gt)e zukFCBv)u7Zr-qa6KXP-Rx-HkhFUVCe)o%J^LnXfK;k37+4u+wAEj1=9ViE z@^o;(SA8|NI+2fRdx=srzU;$f-7#eyT(<6|W!yyhxrbsbF)y(0Kg>6w7I%*p7zq0? zyQP;YDis{NK+!>R$NHv9rSJfTu-~!%VMGf9y{hV6r<&&au<{tAYcAvpBz})8!rT-cxmXvU+6!M z8m;YuLm2OZdyM+a@d+erNuzb)^q-%(_PVR^SZ#2;9wo0wEK8{E)o)$7TSJ| zFbytSb&+TeG_P|>MUHnK?9qcd&Zg@OQTy=KZQZM7*Lr1Me*D&dUESVBrQpGH zwI1;_i}>)@LPPJ=C0|I%Z#8#;o?~;l2N3WIIjz>2(q>maS+fvW6}GFtsl}sWCl%K+ z(*QS5>de?lp&CKQ&=-nKYPpJcjsz|6(>RNv*V`Z^J|P@jC`34ev6xYZMK~kH;=T&Y zxNwGV5|Z@-`va%a=l8M@5hd7|g%B(9#4MsiM^O4GJ$VeR9R5wd1h?!)%x9}Y5KSA; z!YgHAxkEI8V#93e0bMde)!n9d-W4v0+NB0?8{!2{3*r&TnaPUr0Pc4&)!EUUS1ZFs&(SfePC&`#uYNk2AUM`(P3 zu};TtnFa5ygv{mhv6Eoc_*pb&cw>n!UBo9%o+o;KMZwGGVyg$*(gYgs$BB-S#WY-B zj_MgAYk+Pr0bezJtrmQI@UeCl@ia*XokjEBUH#?fZE-hC|%Jk{p@D|O1%hT-nf zSVD3tD@MW1q|%jE!*Eg)C!b2U*GUcvqVCb$g+{PVwdVdjUtN%9v;q;1YbBW$OOdUiZ7lT93CGlw8gL;Uh%t*6i@A$p?c5W2)+~zO-;B?8MFW3 zE@~Z#_{W+JV79!#9i5-JJMMjSfreI9gMeAxhlMtRrA|m}rg8d)V>1Bq?61uP*2%)+NEk#WI$6^!Dp|?R2r;?T1>G^BYyM zG;-HYzU;+rU4MNrf4On`w)oc%pVmhv12bRdu=euZWHi6$IzbN=nRs?Td`ZmwcGexT zNo8?0X~)30SEgO|bBwZ?+hmh|oFOxXqtcF7p=9e+P7eIBEGd4yOU8KEq9LO_4kB1- zH*55GcQ2Z>4{X>R(N-(x+-aKkt1Rpu*ag4xCrNvn0W&ARO!AQH3Dx1l+?C4MAU=Yz zC5gQdAB7xTTHzYZ`;5Ix$2+^aG;v?kwnJw7)G(b~TI_3r3-qCkSc}xT>uVQ-y+2S$ zmVNRyufWhI3DxeVp*{scwL*@F{#NU*RBwF%hV2CtU#^Tu_`bxpBF8% zPCUvd9&??CD465JNA-EM9LlQ|beiEL_e~0;-OYl}}`pLaZC-Gk}D5{?45V494$J zP-Sk<2n8}uaGEy^=cvuu9WAgU34nOZh7AINN6652?M1|H> z!#!{BPOO_fD?^NT;5E!o=V*%11>S#T#>^a}{yF02i?SIzbmXI}b*%Gdcn$1ZjZSt% zU-pUO^7rvMXRp5=zEwRMQ=nO2yaW^IsL5$EEGjm)%qVb}-v<%SJnRh#{2WctNI#&^ zps)SWa-on3oc)neU2Hm6t-2@ z+%nT2v(~5F?7SDQ-S#=D61?pE@^<^pPa_@ThsXDrZpYpTIo!BC|K8-*;mYdP$M-MW z{XXt~6nuI6fI{Txd9nSMuS?2NI|qj%Q3GA1U)lo9xX!!q99A%lcyuIq)ZCPW7-D(( zw++7K_!QZi%Q>z%Q>phTQ(+94h-@z8VGUe?pN2w!=8=ku(YI}(7IZ7g$%0(CCVi})Vx3~8;@A12y_|_m(8sQrs;EMn1Y~SI^?#ETAc^ANbhwv8>B}l>f`& zh%DRlGOZ~j^0HvaJ~7y3V8z=p#X8Y@ujOVQFGY`4KJR`>+xZ7lXf>$zJ0h0uL57o{)RIuKUI#}*Zr{%`D2Iobkh>G zkqL(iz<+N0p6+&{Iw)b6fY~sCf+ZXlxhu;Z|?1&gF_l+vOUTzP^FSyi=Z-+DLt_vf~ zrcXytg?%uv7Eh-Zp;lMzb@N^p2$l@DGm;-!2TzKw;Wdk&ce5xqs5DpDSk!EPG`*X1 zX;^-0k*D#>>@%gse49;0BX1KmC%bFsy(k5-;)$6=+Hek8R(SY?4#H zV2a4o9wn|K| z{i*%Yw{y~}+RbMO_n-UcKX!?c;QtnxSj-4fCm2`yy-FAEYc2FI+Bzgd-=@R-eY;4m z4mROY6EfYO3r)sw398}rGBq~Appv4dwOpPTp@~$ortZk#D5aQSb9*6^C!(s9MN&68 z|0;A98v5ze%_}>)5qq|TPVw?aJ3Wcigmp%}m_}alM&6ODM(OcsfA>{={u3mXbkl$wW9W4w%@s<#H9 z4wkeD+`W#9j)@0j73Ak9GvB;2HCit}XOLbkJ-SZ&NhXwMU^!uUF{mz4w9C32Tfy1y zsbf%`y;Of|P`L7}Bw1$jLj|e4)XC+VUB;KJ)H1wH=J{eLQ9GX`24<9#2ixKuUShj> zhdf)4rnYSFl=YRnAu>0}aiOQlWftd$`?T7%EQY*uYo!=q@uaC_SK{hcFIMpqe{K{hD3_1Su zxa5cV!2}?Gpj}OoiF=Z`8y#9}=-CD@Mtm}d3O;+BAkamqc&eq)C(v=yZ$vOZ(5U>( zt_C*Y+_~N^8l5xH8fUnpUBJ|Ya9aARU(11?BF7UtJMf5!effs{F+I8%ja_$?iX1T; zhFizf>Zorb*PWLE0!~+$*cD0gv0AV8#PAb96?`>&60(k~E3tHG^6)$?RM}XIb&FOG zXJAJC6c%hH8CY!V6bGe2WOn@)tL5w@&HQrcWJ zS8SQv*m`v5G#w~A&!7Z#s`m>ePyvDeCPg-0)g6l&H{>8YpMU4UvYyyzfu@1Vb_dau zHa2d<&=rhHbPD+$a_P;8WC|vzKmQukzY!|3BRre#s@CLe)MM2HGrRgq;}Ok{Nh%r+P_DCT(lJrgOX+MEOanoHHv|V2x~e z=X#QZzZZpCRn4;Y6K2CF6%=%Ls~vAhJ{w{qt0#)&Oz~jWID9}~Cd_A=$o^(8U?Dfz z+TT}GkqjK{Up{2}7#%T|*lI)5pP|es8>1(XRxP3k-w8N))9~m3;THRm=5y%vmnYoB zH>c6XZ=X~CW{iD?@K8sdzX}fq{cqu+{9-Sdu=NYoap$?I?KaN7+&lXx)0)GSxDxCQ zP3-0ttmyoZ&&cqF&k6AR9Jg%T^^SSeLgl+FUMW%rroJ}u4jQ5Fx4KLWV`@v)eOl0; zNH{2q5ECVX&ajgTgIlq^W~}ET9x=bJWz~Elmohm`zBh3Rb6Bh4<$4ggQ`wQnmvRE9 zFLM-Ey#3xOzNH9XO)2lRTB1tq)5jLmXjX>YjC<#p$C=l5F(=qj+&)|;^`gzIh_0@S zW5k=yTQ+ZWm~D+0i=8Y=crw;EtLOxR^JL5rA9SqOIKx@VCX4hQl}H-3l* zz160!0%ybnmY8?27_RI~emOU&dA0@-S2+{JmO-yIH6w-!bCQNHI}In{3tNw4@{l*b zvZPQI8fjJ3jG|3~zGE!%X1Q4|I(T1r%~JJgIcxgtq}qJ?=L@cl*XAs*BoBBpKK`6p z_cRzu)pD>DfxuCXo?F{Ips>5dui46An03pGJbCf_>Ur{1o7=Q6El&<{zTGioIYWY| zbM#*%2t)pXe-zVHcJ7ln^^ds|JNHI?G<6o+zPLC@YnEPWi-7clZw|*bzg0FScMBbA zG^xBQTIA1n)iB*ByfreKUA$o-b(^`t*pshDh(Ugr0Gf44rsTQPE#o3uNoQ#(eWAK^p3k33fRygKE`9472MwHxu4 z3DyTCe`>0XG|fw0l7BUC;6+x+(A@OYGmt5H%2lOqX6KzTvML$o+Mx#3UJiG4rIS`;|~4n@p+P$vIr`hrOa!Z zT;|MaYOXn8tqajy*LN*q1Kg7Ad5Udsm?%G0?V;hlPQhDPk(x;B(zKm=AfN0MPb53a zOCUAvm~aRU#PL6$ChxUmS2(b^?^v~eeD0~EfZ`bpoe}#&|Ku@O)lj^P0j!IwR7Vob zCW&pu(&^HxC)2~kp-4=;s)WRpG<^aRNC*>;*yng=55ceWxAJFO4SW-ZW>3;qS+q1h zYnJetOBsjLkLHo>h~+tu8`QLaK=RyA_jrWE6xxId%R{rcsR(T@SxIK5xI*Z4f!MI1 zyUD@4=Z)dGX!L^f7ddMyju4_)OR0tP0pXwx^oQqpk`M?AsKHZ=EutYJL(}O}dX2~+ z?xZO^&wSn92ajLXu1t9gO7rf%D!KXL$W}MiG<00~lm3lsq2g(!>E7E3QogTMMOYp) z;ukTkMAyd8o?(BLD-gq6%;LZy$RWU-(gE7AViop)#fi^P@l_S~3uX3FGs zyLd$%u7v_}pA%vmj<^z^af_@20*f!KqQ6rKJ0n>Wm{B@C!z&kPE;2}lDuk@kV8WoptRXk{2O(c(v5DPf#rpQj!= zgPt>jI)OiUpE{GC6jEJ28WkR#$LQlJuJ|n3UCG$znr(-t1P2%TZ=Wg~uk4Nq z!j(D54q^>BUKUMMvVt_9Ac&q?BD_;ezJ@5V7LoI;Qu=^z4(&d%Y*@dJ)D@w}qLjUu zHl(vwgI*@0)tl}RVZUKvwcIo*g55{|)EHT6>2^QLOWW&`tIBBdCym8JZ-bl;4_@26@HNj8(1#-685y&G_^HY<^AF1snMfv7>r!jM`wugjCc-$ zfc=3e<3z|YmxaDmodEpbPJYjlQeHO_t_^Plz7-5CluzH{-qbO(Gf*DEYs_l3mcU!k zLe7T1X74^ynRn(u2f;NjY-D@ zT4==QHFU|qY`0L_>u!;Mwsp}l@u#?@G{#xx7Oklizw^{75k5)`H5ohyt^|lj=Q7AW z2tFL(?r;Zabjsv9e>fm%F(8+qqdcZu%P33HH%BLm46T@@dZW9@rQcNc*_*8y2hSa1 zu%<;UdzY)R)1Q`SHr_CE!`%S?#Q7#Vomr^RW^JU*E1ymb6}E4d0gj{MS2y3ilSO!k z39XL>L(YMBwk2XfX`$m~TGTb(uLHSR1z1 zaEwd-G`>zeypT&_&gf#9clBYz7eSKRn`8^AbEgfs5ZOiQPc!g|L50FR@S9TW3oI`z2Za#A^0~ zG5ilVBUUI67N4ioN(-dgB5x7EEUX_erktyaaj(u^Br0y3TM5OG_O`MBjMhtlAT+Xl_Y3yu5o-kv# zz`YbOfswp=OxEUov*(ZAVMc!{(){?IBq@8d{7u*f8oCsn^%8mNw&LqGa(OLd4r@G02{K*O2@LAR z7U3cn6;u=~5)>L3gIP25YmL*=wJ+6bzOJ+D>+YB}d_E`>?HYX?v5u{MtoF_*X-74< zDgknIUb{^sI7;!eiR^+QWb2y@xQ2Eg&qbC4Dd339?A`|=ocF*t&V_x(Sr%RQY`M{X(vXlSD@Sa)2YDM7q2zA>YP>(y z+Zg>=BJ_1t+2hbrh513@@R7L4rUc$lKN+57NUvyb(EG9vjEslnkB4e;`uS(ClW^}p z3$bEe>Z6@AqZ%-yTGp@jVt(LvL#v>tlx=1y@rF$eye*i1(EOBiPFGAcdY>2=(Svme9nMK;0|tv4Pi z)TDR&7z@>YBh<34_u(9M&wo~m`@Y6Td29?>ncbdZVC#90qOBlgwEv3Ql}CE84Q3=2 zUa7gEUY8wZx;frF`86#Ktqfnxj%{fGf`D33L0MV`1;!cFoDr14fKA>1 zJ}Lm-PqBdg+flA2iNYPVryn0Yh>=rl%>%B&2TKtdZIa>r4<8mxl}mY$76@^07Ep84KAT_fiU151_YMXFS(gGwo)) z!xg1RP+P+2xO)DFARnYd^C3sbL_?AYZagXbE%9HE3 zeRsjJ(SXw0_PZ3Ff`AAd`7?tf&Iy19IH%>$qWRX znfWoD15B%K(DhJ{ldXU(X9^3kQ0>6xpgb`?2aGH#CT7}(4~?ZMF|r2Y+P<2)Btx(6 zl=qJ?FXZy<1Swt$V^)t$ql@RYa&8QmM-V-Hn5`!ZBOl!^K!f0&!Oa;l9}IY={=i_7&VcjX_=jys4- zXDKn6_?Zq1^Bj3^tQz&WEWq$rX9a_~0x4|6L zEbLw`Saura7&JPT<5elY5GH zKp#t!eo<)a?m?ap??deH_Vkpy_zb!c)VCx*X(^^c~f znHaVL`p;0#89^!({s*TRyc&w`7?QZHquhHT*2OO^Qn~LepG@Np<6sj-N_ZN+$&JbZ z@#wX^jW4t9c?p9Ay~Mh6i+kEhe_28sq-dcI2R;|@-D8|_OT&AdI8{w(T~2Zw#kH{3 z_ZmWL(>QJQjC9kuN-SM+sNATRoq|T?xf!bs@#S=n}}L2d~q$QB@&_u}%H*-Q^@kV>dod8vgPN zd!G;(G#Ym0yAleWU!6%lc|1&fc|&>iDfQ%%-;<%;Zf(=Pd(+Mmi05x!wuLW!iK<#% zJv_#^W_mUJ3>lsgT|xx^z-_C>DH2NqFEt05-83a3u+CM{jt4Ea?>j1_al8VLmqD(g zqGN7se4dtTQ8C>2E-ZD{f-K&Q3!Mx#iT6Sz7xQ`bQ@safACr4@Lm}u5aznGC`JE&K zM!4jbsZ5VLK`h3xhq@mP68Aa4Gc<#$GG;`UXe=>>dk83Z&8eDba1Nz}M5W0z4_T@rKpu4ZBh^II)(-*mprmWAoZ_5!OZb-W${ zO$}}Q6$zaY*5Scjc4 z$g+qY#uPoL*f12BfRaD!Co8wdXcPGgzH;syh1&?aWsXu(?Q*Y6&0a0G-qAO-9C_`O z-{k{{;_r>*?+Ib2Szi27W*$~QTB;fvGP>L{R`zDPURq|(9;ijp-NRGb*32E4%&%hh zqYZ__@%d46WUM?a-0fUFUEDzs)Vit(;@9#r_dHEEL*W3N#1%6qAZbhLSJ2<*OMOq& z;)Q^r5D>2b@ahc_1h%~(P{6$a30f!=s%D<c3Q87gD!DhPFURMw0O_9BftIJOyA{y326D}Szjoe0+XJAA-^0q=${C;E%Ff2t6U6Uq z2TVW!TR_qv)WNzcz&%I^SiLkrsCO4FAo~r31b~}^_$`1eGvEdaB+vqI+W{4D%MqX7 z34jBj20;Lc@&niaEdZdsfUh%v#4>(8J4;VnfYbE|NV?Pu7VI?@c)m3|NjaH21HQ;fU*RDcRV1I z5QKms@R4vZNKgn2f}-*Yfv-GJ1rE@Ox{n$ULqNXQFhSJ#|69TTc^?LhMHLi`p9Kzs z0Cs^AfRF%C06>8K2m;YQ0)=E*xO|I}BhffcLZw zyc0qNg-#1x5P4c70f&PC`VkN)z~Fbh0svk(@SMYtsCfb)I8X}#!a~)j1<1-o2?Her zRE@%asx*`U0U7|o;-hfFfjOr_`AHnAKD8edCIQq17$qVU*(lafHBe6Vf@QH?8vuddK|leHfoX6QRRHBb&ETiHe%1iB_&*6bg&-gZL9u;GKFSXMf{z-7;_1|w z0E0qZ{{;m_6c~(udhZ7SBmxQgSx|=X-3Cy0@b7XeYrrm0Ht~;gY7Z!z_*qVE<%dn6 z?BSmUMc3&y00U(kKMRU;1Q-tJ5TF%h17{SZ5TFy3T>viTpXH}b{H#$z0rZZ4TBF+k zD5tvqejhdFuY$4*lvYvo-=C-bGs2MuTn4a`0G>w`1PJl8bp-@L?OgzU8lck;jDVIF z2+-_lpEd~C_B?Hwg8(H1Eqq`-hW_whRvS&}j=;q<`>s>U#id`q2-#Bj9_VQy4#7+7FbTAb}q!zk4y@YmZY%D8~l4z0Uy}aH4-h6!@RRzP<|C`Nt|{x>)Ou&=*6 zQ6l+mBud0T`Tp*8zbDu8E8AJ30u12sK?(9S+1v~8lBcZy_#6{>6#t$q4J3@C!l;Wt zyyNQV;^}B-4)TWaK>)&eK(?Nqt{%et{7!$}<8yJh;lk$!0v}5+3m^ggpVM3|twH8y z77hTKpCEvtrwHs^oMlj{`xj+|p}<)j5Oo0$Ayf#4h~9KU?B+bLjG$X0=3}&GENA!M*h1G2?6}XuYIuJ`9%u+CTB1b z4#Ypd&J_e==YRJB?iRIxqJDqT47^?d2gSekA%52{3dyhIAP`ir{cm_cwDB7bh#>s; zaYDc89QC~Yh64)wO}`K*9PsnM;DsWQzv>Oxi90rBJ4Y+` z)Bhes%gz^g0Rm!qO&1r`5_Rek6rHUB_W(fo?gu6od%DiMgLngNka2As{fw&B~Ej zQW7MrcikNg5|+30wsdy00|{$c+M&HbP@sVhNLrd0?P`N*DE5yIif*pnAlOL*VFOJo zN3^v!Nc6|0g0HuduD7K(8U+7Qp=IgqjrMc}iC`{?g>~&MZ5&+fK!~4rJ>9Hz(cU0q z04z!oKn3mV4H8yy0Z@?pnalsoRY0cyKnL=)vF* zz}Go-wyo!Ki{%5B)ZOuO1pF^_7-BC>c8V2EdE)7W{RdE&J}dP-A9%Pks;I<-su z_MnGOGO&#O86FBl0qGSw4?~)k0ip6Z3|GvJ*~Pu8Ve*&MMvKNl~67MWRUK&%Z}P>mP}8-!(%nQZAGSr&(d_^3SfN8QPjiZ_tTvld+gOc#lyIn|U~DJ!%b3ud|! zgG81%(QJhMk?ZJ2chy zf(j!L%cXeZBKNaL(@FwJm|+XmExl{Pgc>4a10&Yfq1YZ8n_s~pG9Qv~(VEYsUY{1q&{L?uU#%DyPkZ%^BN0UD{*}(~+^V><_nie@6;z|r)KX18 z7(C@Vr*JvTtfd4}ao!~hna-pfev}u1Ce(fCMN}%g_y1+OE^Af@?rE(&XPLc^5 zQnXJ~TWNs$6!@6_D$ZShA&1S55hcEkC{08wUm))`R@Ze0#iipVv!F2D1$ zu+uwl%I$#0J;$ua$r+*RI8sZXV3P2deftA=B~fWZi`x>GWVSv0=>o4qAhRcJ-iIQZ zo0IwYhLr7^N^=G=G+vI8XwG5sz@numys9}ysNU4o!3veUzL6>WeRu^!_N#Y_qrtL} z63t6d2Fa3g>TO-^L!fQCS8wQRw{yq*Ei<~@jA9mI8_3v;dq+@o;v~yAckhIf7ot>V zc+ZH@4{XE5o`bGrZ3fqQ_=RE{l1A!oAINs$GL>tYgj<@R4Y6!E+EjVWvkO00yi=P) zUZU0`SOSI0(>! zmT$Z$X9k_a?VH3NnS{|>R?;UcPla@O-GhwR&@y6wGL&qTb(*e zgk2JynL7$^u%GE8c&#w7(gB@RUZ1G-@y-qWq8g85Ae*WwpxUk}`n_I9ZHy;6p(cXQ zMJjHia=EqJ*w2xhpG3fft3~(W3Q` zs2FR|)fk1CxX?7~P)V=e1r=m_YaOAR79UfxPP}L@mQcsz;f|H0cb=ps+#&)rYzYe0 zs+VYUqm{7iy8K*Y7+j3(6UGRX`K=jsp+(s4{1z71U*s8~(LoBSe3Mg;`5IMV_Gn`z zF)s&2riGzW;|X5Pp)Umn*2hwo3KMHL8zPFplQgIWGL+4fbL(g&H~TvlXM2aLhh{YN zy#zubk7tx_22Jd1OHJU6<}yl6P@#$*!n1je8$woa%*+ZB#(6sVy(q6hpR&7$g9o>Q zOh;oA@aZ5ZQQTV(>mIP%ALX(Kqjz73Z&ws&WHk8Yrf=*ezj$p);hN7l7g|4hs)vwVe+R5-RzzvVtcKuyQd=B)g>8F&xt@h|g`(6O1Jnd__r1|enaqCL9?4ocK!CT-KOpHeNZM93Pagjc6p0tyqDnY#mR zutDEZAZ47SdGvOyh5;J=+)IV?c?p?I_`8=W4W2YocZU5suW#lv9X^jWZO8k}5mAXx zd7Xsv`Z~f|b!rH!n#I`y;Jy*SeLSHMQn-q|JX~rrW@^wmUDLcoT|%5|dtkBU3huyX z$Wupm>=NOM6(`*7gt7>}lu@i|Zg6S6$(3TjmhVAOL03^C+v`@LcnePM6w^IeiL8B* zuNxw$&cxHt_9!ngb=&i$34QJ<0#DJZ=diRc)J!F>PA5eK?1D32e7oewJ>)bQkR6J* ztt{0%y>6JZJ79UUgO){yB-e&bp?#pxf$&UBnO1lhGphZSNo)L?zo_Kb}c-9ThB;+T7w>=;ybeKI~gZv-1{ z{d{RouBaLzW5U>z9BW7xV2q8cR)ls;NFV}Ku!8#>_o12MyYQ9x*MqUUC_yDxG~Qb& zEmgbOZe>*_qmY%Avo8s1I^Y%*1H#W-pnd%-5@=iLb%@L@nY6U_k!h@m{*yrS0w4vPr`t zw8N+po-}k!H_GPA*$H(A^}df>8A%FnWuu{DWz9W6Ac8X|({O2%VkR#K4k)Vb6>rch%rzwH{#y^(Cj%O_ZQ z^jW@x3aX$O|89O3evH~3G6h^jX>~l&oB|#96%J1ho~_=A_W0O3Z1>=Rzz+}ouN3bS zyn_c6p+K3fdc*Gh95r?oNo^1X_OoY;=g6>RC-l5ZNGpXZ3Y~CAFj{YZE)DCb_$5(d zV$aA0erkaWF?mwj8l!9}nXVI#viozKuw~`-*bi%nFL_x@T{8t7R=%cWU{%k;?VGfU zs?;%Ls09Z#YgI*2EZ0C%0l~Zig&tQPccG!^=Y!-4fZY+ps_0g-9U9<>9MIh*G*X<2 zZ@vCSuAp+Pso2>E-BssGyeHv}^I=Bv@&*1K`t`s|)g5;eJD((H@|JUwCRr(tW!DXw z-e**xSa+;ffn>W571cF8t_T;npMqN0SG5UmYaa&GK{afI4PrSe@e zYiWjK#yNu$)F7r09I&5U$7^tCgJ-Eh{B!lqA3iSWRWRu*y&EvzJh0`bfw4JXX|l&x z(WDJ>t8ST`=A6r4t}|X>m#P`|41Jb)h6dYRoI$j?BH0(!v(0@Cw%GzWdhqzvpx4&EzCQDy&{D ziUmd7C{Avtv=N9gjcnHLv}<2<<7Tjixqou5dKuk%`^U zA*`X*sHnCJEZbW#`~D;uizJh879`Vlmby|hJCeU)Zj!(QEj;I#2*ldNA&L6E+p!5` zblP}a4k{%&;=C$;;zh!wDU>ohblL7%D#RDPi|;Yl-!+Xb8V7uRC&y1;Ka`w0s3I*K zZJFB!FSeiP!+EWM>UVs2cO|{pKBRj&vc!W{s=(#NS5zFH2=4P+Dz7uI{PoD^_Sq3m zD?YNnuB(X=KybK?OIW)@P6)Ne*!;wtoM!hV^^0_qtS)@n!i9&N4e{C{7AZk$@ESAp z=gv6FnclbiF3K9qBnwg+Po1Y9FA4bjL7N=swb3Q2NO2NN0NaAkYvO>&D=XY7kgI$?g zacXdGSKrbP>(yCwq5B&=gA0M(d>=kqnVBsoj3YYvZ%|%&4!Haxz~$dRQNOzLTQi@J z#U3zfdLWyl^YVhl)xK*ScFh5*FGaJ1lGhL4Lu>S^L2bsaa60H{6#w@|8rR_=wo< z-1cVepwaD7w?LiifOC+EozpxQVsii&b1)cIEvl%LF)|FVzEnF6wc5q~S(i+V#;JlKB`6yAKIBH`vL8YB<-=kQ%80F9`{(@PVP7 z{WV;6?;`V+DPCV!w6>3XATVjm)n&Tj z@a^mRR%DnC!XCe@s1)c-b&+IVY6n`D*qpR{WTfyUO--rqc4+>h;+UHZ!>7HiE)M*m z+>7@nJ2g?oS11ST6DU_(lIc~V-7+DzoEIs}-$u%d4r<*P-vzHIaO4zelfQ_`2sPSp zTBeN{8VI=<;TJ=CP)UBpNAZ@G;gf5j#Ny8=H|JaO>4{>VMT?C9_^ssAJ3FD?62HK- zr9IHc%|>idYp-I>eY)z#IW!FUV1?fpSl+DcKYoI>V3Fle7U+@^|jv z%2zfaESk3*32#*v=3+T%+Q`FV?-GL^nvZddVO*9fNHog z=0r%LByG5$DD8#Dkl@(B#7mpF-XO(%U=zc%3>wHhA;G6OnJekd(2r3y^B7Jz)8Dfl zZdLIWGio6^&0NR!jGuk4=H)0(Dw}US&r4n{lXU3}JQxoc(3)RlaY?a}aO3GxG*J`s zmoHRh$z`~o+b^C4?b>CL;+hC`>Jl#;P;~wT2Ok+KY4D^p3fxCg`v@98DbL;0hQ$tT z5SioQQl;;RXCH=ByN+lQ_B-%rS5cL{%ean(dbOjYcHS>CIhbjO`jt;Fd8Mpng7jB# zM`M`ql4uVN&4xf)zSDbg9T4S>b9I6X{gUyJ_bOVKmiAJ_-Yb5)>%MFp!)h%TbtLt^ zsD+l=tVLgU%uUDYHs=9sScx(6T_$;%zH0yd(+~<+^&jN6U*eF!&5K}W}l5c(qdvTXLV`_0i zxrviKZTsQu+3SAgvbOxCDauIG32}dQ*GOI)$ro=Q3TslA56NjhIgT~ z{Py9ODf0(SNA5@SUo&R?eZP(-b(HALc-BZJlAGV$8^73e{AvHwRFe6v!wqxk#^X=l zgfHH1HDZ3|^m%3V=6C&`YhEhb4+UnIN3$y0qBV&f5hVFnH1DrQFDZCr-^P;4E=(A{ zqQoFIYNZ+8NFmnL#^G2i{ngW=){MRW+~{E3+b1iBv9Vk$)gKQW+U-W@pF9}6m?k_4 z@>pHPpToHz<)6?w(pewrdU$iwdpMmdHaBs%u^w4>^C15aRe$SX=|Fh4DSf16S+ytO z46|*8ry=YcLY|NzW?7|a9~?@~y`nK2#39Rz#X(iY`Z#TaVlm>DmC5=PO+#niH6cpM zK@L6&B3-LHAFtbo&F3Fn&=lr8Yudywd=)t>k($PIOZ}6c4K+EQ!QJucOJamOZ?;J~ zw&_?M-YCnL-_uS|dPVHb;=6bcACWJfxx~;>JaVhHZo|^vB1-)0l1vHh(Ou-+kZf0CYdJD7R zk{gX8D{5aoYJ;rPv}q`w9xRiLk)Gk8y3jto(C$y!lu8h^GF=rLQehG7KCm*>@{zFj z&2)PUnAdv13IC#HR5hH{gGZj4yh$J=mEPlmLE_kXo5+4|bBiJE#O&sMMQWESZmNC{svzHSuKS_9&8ks)cU@bf9kR(FpG8wp4{NUm% zk+<2K=hj%|&Xk?^c10VJq!>s#rAhQ=_b%SLG<8jip*U~ksi^0~xRiL$h?ber*SoLn zE57lL3)ex+*n(-0gr{)jpL;n!H!P6gKcF?LhpZV)D8HfplJLDQ&R4z56kXrPLvH%L zq%iKRCZZ!{%bpBQz;z2MUO64h_9^&$Ni^`PHP<=#E zF+BAycoBDJnPu%13{Tlo5f%OK7>Y!`#9)Yf3}7f0thHNEC_VJ8Nf32&4Mw=;_uM^4 zoqCt@X5jaAUONX>ILfux1%;|j$Wo@&JXe;>Od4J&+hU#Nq(cc-TV+WdM{MrMbi7eZ=&X-% znkDeKdUs?wlFp`~S;0@yS7Nf0hqx^5Xgh`;vemX6&kB&;8;p@|95_wr`aB)7- z&--}r24>PuZOxURiqEx7y;l%K>CkSy_>`Lh%B;CF*H%*X+2|u==Xs$_JK`m?GcW8t zjRM{reLR<_2fYSM+hDJOSKWY?&O&3|y|_5Dy`;GOW`6dxHltS%c}>HQHcw=U^orB4 zU{A*z!Iy>=iLN=;Ht_H6eCg$im}0)FubTG~NxqJbLk8KDNboqnnm_yHtyEA0ov)f6;OT z;=c*{hPVn-vOB6o(* zGCsz3CqUgMxN;6l$BCx-6ihHX%)ej)1OI^)kcw4v#f6Z{?@0#>oozmxNdJ;^zBc*o z?I~XFWne#*PQfv;R^3pSH5wjw_xAFpd)cEc3Fm%W+jmA;Nns!27YAsq*0XL-^eUPf z^0uwqwH2>g(nivsL1+|a6baFta^Q0T^QeLbdc+5H?B19zWeI*;s2e&T#dVA=` zQ3Ujzqq?cHPSFiENi)>trKK}UVI0;;^o#SdM9ZqL?2SSbt|{Qku5VRz-ym9_dh6Kv z{7gaeYY9Hv(p7IAAy@i)?5v$z`I{fI-#E$j@0ap(Z!5nEfqC+-+qZul>70xH{x%X% z?ZGp9_Al>BT*rhg>UG5xtZXiMK6MDcd}&XMX1J!Mf%NQ-vw}RISIp>&dCO}Or$lEu zU)4i=*6bM`HFEulsr*YD#gY>Pdu&GI-fXAv9?tZDWuk1({7v&4GaNa~OcI|^g zrXq16i#YDN?SSdW3AQ)=bW|w8cWyrIvhT$~48+yh(YL3nu_{DefhU(rsEBL^?9Ej+ z?jbxLe4*bDzBYTzPwqdCBYhi7?Yk+#Da^yX>+mb{V9@{m42fu~xVC~x>%P(+HDisf z*YmbLzPEckuG39JD8pS@&244Pi6aczM}|)C$BJb0+_Lk$awMP`tn{k*oeE80(tES3 zK|M_E=!@hqw)!Of*E#KRq@4-~DM?D`8}75>;5q`IH@1^ujqLBsId%FJ6NksCwudg@ zb?a8nE(cM$6dihfeM;i;b&~qh)f+1+AE-kY6Q4Gt=V{~G3<#vOYk(!g=oRNo-pu;* z8F!qe9Yaw`=k4Bwy?l9ofPAgylil29c6-yACob0cimDyMO=KDxv4jUGBD+fXI^Fl% z{SA%dUnwrJ>rtMQUh4NwLukwW52=DCX>KMQOMm;3BGw|eIls7PK1^xBUi)O#%t5koD`k_NkIDQX?n ztNUqf`il6j7ZZ9-UzsHkJ{mauudI|(W6^FR0%T-iwsmr zcDEU4ZH^Dle7|SJaS8`diC*R!Lu47waAJ|}RP^#A!YDkOBq;ikxWNnD!s?62$#xL%b85dzo zSuUr@^pr3i0{a73tnomW92Y`}yKy}@Lk1+{+pm9_V(Qpj?akF;64tH&XKjK&?t8xX znBEUgQOqhJXzk{K#SK0y_GC@>46rV9B;U0y>jwcI@s0vd?0wDDUTd__3tpoZ%q>oe zV|1%tPuf#TaET#P=ociB8+VR9fCiqq$&jq%Gw+bwF|+GjvU`N}+8M5L3PGnt_0T_g z%r&%C?&AV>agp{=hTSZ#{)v2w{NnL=H+e7;kEkRzE-~4FgaUGxO+acthTTi_8`G_< zi8@2S*sh7==duYGi<@tc(6=tYkw7J zP5#FOcIr!{KFs7sIj^dntXk%b1|=h>UB4>YqIvF;J+P6RW(Wuct>Qdq5Qs-0sG)|h zaX*oDkr`Qx7cgmu2l2;`5qW3nw-)xkEB`olRa9PZ>s`M8=RqmPe)?Ya#Pr>qk8{jJd#t1V} zYSMKNRDPcn{iUK16So;ZR3GhoM`Ypb+oPLj(_LD_&B-+$uj$Vr|1|Fi?#acGx35Bk ze>LGjhm+dV$hDY!nvBWM^JUwLc@wloJL`kFeOcOl-JQtJvzh6%wa;&ikWXy-nJCw8 z#P_i?_Z({l3ZQ2^iMeHs2d=GA&Qh>E3*ry5EOipP^9ctYey&%uoU<=4A24IV$ibe?q1?tSje~Eo68%k= z6e90`~tR-<_n z(Xu%g&a#>-v+Hc{(}YcSSLxN(?zWyHol^pQkw4HO7yr2H0+q3cdEEp+*5R2r{Hr+ zpeqLZgUF*7NfiJl#2-l4J>tCE$+73b9PWFp4<007E%JNJE_SE(W$}B9?pkl^4c+*1+`PcNcbO2e@Ps-;(E0E(Th- zU8hu@INKS+&cG6M^^wW(+}J_Gu}6qR{|m-v%o}splEm0x{DF2UL`zWNvXjc+xRlAc z&xtGDZwuu^?73OLan@-bHUQnJqXxE=g2I-9?p1Qn;BOS;*;yYgBV0wO7Vvxm^mFFz z<8}ON+8h$RKiea`_(_VXS!~Ju;U`A(D74Dw-EU{^N_ zVc&}G^Z`O5tG2f5drrsn)A3vUYnLq@4ApvwsvgzZ$`DQKA}5QaF0v7e@18>IDbWfH z`Ug+8h<2Sz8<=!W1fL!Zy2af$4^ob)y8TdMvOsybp6#+3G}lDPr|W{D9%=C+5cl%)#nxsxCou~MnLNIvfoZt@3cH=`cAe%_;~ zHKdX`M{`88lvj?@Al@XW2xI#+c`R6XKRwnj5lmf5yfe77UU0}U@5JdNIVE>k7@8ZgJ!=^1$K1dYI(=o$_Kj~ zgLDcy7erEG`GkGqxeaySir-9ZCBGi>!PN%AeL+Duu9SOkfcUe2*hiYZnbE{@d3cgN z@)il~LLn2&uo@!O{Q`CEUK-$g_ijf%IhxbvOs9ss_RF zGtUPZX5mcIEuZCSVq(cZiX0n!+wvkS(S5)hn#j!VfLn_AClYEULbMd=_S ztI^+VBp6UvW%iw^g0q%XW;d+WBUL9Vv&qRlw_o@JukM@QSp+{Nkops|uMkm(Szt!p z$g{w^#j`7|9slZW6WLjA*J|DwDH9f=6h2p2i#$O(sX&OSN8olMn8ZX$E9%i&w&iH! z3SQ(+p3avIium-kr*k2z*f?_MY)?V$l;{Zx`-91_hE|sgAib}3#3JD4CFVNTGvSiY zzulJaCW=k6SDVY+iZ@Q|M(=SV-MVT&$ltq^;IQR7&{tCSJ)0Eg>amvI*<$W8)n1b! z*D|<@@I;GPWRla1sKtUCcMY?BH|O6B^93$TL0{UIHz2`9Qm@IQFIBgftPD#eaN&m4 zMt_mZC0=D6;m&%%Kb5TW=#tKROc0|0Q|k^U zcofA1##0l>+bLavljL@G`^kyCibQ6nG_<%|;P#Z?&B-m+si#0=FX5O@iqFGUgQBor z&7P0gIV31NGCSGK`S)HA{FJx`g7|}N4TWeV6);`G^J)m7I`nK7lT zWgIwjEuBWUAC0~Aa+zXg?SRvgfNCu2C0#6w))VVciSx?p%GR;Um8^F-Qw_>ZlT-9A zlaIumEY5Yz^8d`pn9D@U*w9{7k#>#5D^3-I2R!_W3r(NlV%eOWFm4QXlrh8@{?Z zEd^X}=EIMr+q?wv1aB%11eh;mltjHdh^2GbOLiH4=2xmNQJ7Q8?5yc!#$#?|S0m2a zf2M2q*&9loIvkqd8rW;E_veT86)WhjB9t9XoZX_n8K@2fyP7E8=ZwxM;E;Tr)78sE zJog}t)$;c5&;=q}Fb-8-UdSm6=K{Tf0-Pk#iOfRsB-UUT zE9V>wTPp$^Ll6X1i;1(7Q>k%J!RD0U5eDpF{nrgm5nv9656r>(`Ko1dH#J{>DJ%?c z1Qvv-8)((R7V~_qhr#?z*j zGDs1oyG388P;4FNNZ81z0L}%gB!&4#>>HjFwB!D z>~>Cl`Ydm8PUg0hVM@05~?6?CyB_X$bIL1kIVhfbCU ztOm54nW$!HTkF!f#MkSkw#K&-9hx8b-c92l6YMa+*I}9w`*eRV^Sa=3g3yMP#QVf7 z`eAgpWIsPviuB)Q&@%uHeqvT}j|kI>3Ah?KsZ5HzA#hcjZ5wtTLcKJ}q%bWT$Q_%E zY^(L&p4}fPd;I-PglLoag=J$SL+c@Frcd$h0?=hYgKh6GV;y%$;2T*-i=(7eaCBsB z`mOHq=Wqnd3p;Yk?Nr&P16HzYL@uUS#q+>+vcp<5ow6GI3Kev zy3n2>pDS-Ms|lo4rFV+m!!A%?3S2%e`TU}Be4LHtQ$YJE!Z|IX6#0WKTS09VPh45T zx_3P11; zxg41fS@RNfG4Iz9a~fDPc)GO^&uZ!+#7i&ylzDpxflIIL@N`Q?x%rzb#g5nWxLIGd z#n*Jh^EdjswqEI3v^^Yml|hWo&DMv`e~l-n*$3!oc*DELlr&6(NLNcqFqQiuc$|i}eu^Gf+`rttSi(%Y$f0e|{ zgW3vPbE&N6ur4!pKU`(1`BLfBU`>wMq$roM&Hp#ljGIGa+Y4`pCt`# zZInJg3hijA@1tw!>V+|io?hPa_LiPNXJK{ApJm`}6k=ga9~9cl+S9?^+szXM`FRdo z*T>5H3a;O{>rQ(|EZa$3OYZs5H1$ysk=VFg#<_%r70_upsE|EeMuMmgBp zqJfj!n4{XpAk1c|m%F7kaEctz6#MJ$_GniS9GJoSp*`I|!fvhr>fYCYlDEAl8YtTW zhrEIR4p)J)7l4(p7y2sNl~@?t&5O2akjTGe-;r%!MEnvwb9Q>8|{PF<@e0gTIQvDSP1jzk{``tDQ3%qgA@zXcv7< zLBrDbgd%_-V2<_ovG)`DFyt765Mc2Ck3{^x9R~(PRB%9QaA3YC1``7zUL4ZU=fI)!AN)%9PQ854kFi^(inENmw0pB+e z1^Q#gM2aDS{6`%oKOqJtN{q=dNMMMCUZ2`C^Wng{?6%{*4BBIEX z90@od1W=EFKmh`Oz=Z>FMSy1>hQ#!PgG7K_3}6-}KS{us5ircaFaeWe;GYN$!$1HB zfU(3Fm?A)*6Q=xy940@}9}FZo<^qgi5e97xX_y>HCvw4n`;maxHh+-zpP_yf00e&w z0R<=qx`|-00xUpas+uqN`4q2Qtr4B02>WAxxZ<6~N3XgX0QK%p?#{}}~? z6c|i=a_{E=NCXn}SHdX54;{eh!N1c9uK~Tl=)^zLi9TR-;;(d~D?fDtqYwW~7`#rd zfiW<;@mIo7jsS}QG6ZnN=)frnDF(;{qZfd?`Dgk|C;rMYOabIhe3E0z|41jY{&64E z=06Fe7Z|Bx^1olG{pYj?1-OqBTL_q5fQcBZ2v}eUCa45T+Mts+D}j#9x7c7pzWNU|bmB_!M(8IqgF+BXO?B?z4UVOAOdQ7%||ee*OSIBm(HPE$BaWm_QBl1G)aJgNQ*f zME|=E_*%{>b$~DXrJWcAqn!WlD+U4Vk^grcaD@PbiTV8p7cdz1+jzh(^l$iq z!BFt8aKQ+Sul)CT5Cj|nsKLKrKo9^Qf2|V{{T*k(o&VYnz~wi-fDs5F>ijnhFhUGt zfc^y+`0NnilYglb#k}kKYn>>@K>oY0sF=uaeZ?>v2EViu69M9mU+csG=l4q;5&{Ip zzt$lzRs{3=TOLTU-+2WFH0GCfkW=Ia_1 x4-ME2kg$%M8^+|Fco-E|TQ?x+I=KLhs+YH=r}v48fr!CjU}A1=1x-cb{{wOpKZF1P diff --git a/thesis-evaluation/figures/totalTouchedGates.pdf b/thesis-evaluation/figures/totalTouchedGates.pdf deleted file mode 100644 index 49822f1919878f5c35a415b0d7b512b2b9cbf1b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19016 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≪fr8$&dPk?;s3GzU)l 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 Md{$OjbvgY12WH+ScmMzZ diff --git a/thesis-evaluation/figures/totalTwoQubitDecompositions.pdf b/thesis-evaluation/figures/totalTwoQubitDecompositions.pdf deleted file mode 100644 index 10f8343d02d90d20150626bb994a33971a80b440..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18838 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 JoQ6E!{{vr#3|#;K diff --git a/thesis-evaluation/figures/twoQubitCreationTime.pdf b/thesis-evaluation/figures/twoQubitCreationTime.pdf deleted file mode 100644 index 5803a8fcdc09838428ad8ee4590f015fb64a0af2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23629 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 list[QuantumCircuit]: - subcircuits = [] - - # collect subcircuit instructions - current = [] - num_qubits = 0 - for instr in qc.data: - if instr.name == "barrier" or instr.name == "measure": - # Finish current subcircuit if it has content - if len(current) > 0: - circuit = QuantumCircuit(num_qubits) - for x in current: - remapped_qubits = [] - for y in x.qubits: - if num_qubits > 1: - prev_qubit_pos = qc.qubits.index(y) - else: - prev_qubit_pos = 0 - remapped_qubits.append(circuit.qubits[prev_qubit_pos]) - circuit.append(x.operation, remapped_qubits, x.clbits) - subcircuits.append(circuit) - # Start a fresh one - current = [] - num_qubits = 0 - else: - current.append(instr) - num_qubits = max(num_qubits, len(instr.qubits)) - - # Append the last chunk if non-empty - if len(current) > 0: - circuit = QuantumCircuit(num_qubits) - for x in current: - remapped_qubits = [] - for y in x.qubits: - if num_qubits > 1: - prev_qubit_pos = qc.qubits.index(y) - else: - prev_qubit_pos = 0 - remapped_qubits.append(circuit.qubits[prev_qubit_pos]) - circuit.append(x.operation, remapped_qubits, x.clbits) - subcircuits.append(circuit) - - return subcircuits - - -def circuit_length(qc: QuantumCircuit) -> int: - return qc.size(lambda instr: instr.name != "barrier" and instr.name != "measure") - - -def contains_foreign_gates(qc: QuantumCircuit) -> bool: - return qc.size(lambda instr: instr.name not in ["rz", "rx", "ry", "cx"]) > 0 - - -def circuit_complexity(qc: QuantumCircuit) -> int: - num_one_qubit_gates = qc.size( - lambda instr: len(instr.qubits) == 1 and instr.name != "barrier" - ) - num_two_qubit_gates = qc.size( - lambda instr: len(instr.qubits) == 2 and instr.name != "barrier" - ) - num_other_gates = qc.size( - lambda instr: len(instr.qubits) != 1 and len(instr.qubits) != 2 - ) - - return num_one_qubit_gates + 10 * num_two_qubit_gates - - -def read_rust_timings_file( - file_name="/tmp/qiskit_rust.timing", remove_file=False -) -> dict[str, list[int]]: - patterns = { - "timePerTwoQubitDecomposition": re.compile( - r"TwoQubitBasisDecomposer::generate_sequence\(\):\s+(\d+)us" - ), - "twoQubitCreationTime": re.compile( - r"TwoQubitBasisDecomposer::new_inner\(\):\s+(\d+)us" - ), - "timePerSingleQubitDecomposition": re.compile( - r"unitary_to_gate_sequence_inner\(\):\s+(\d+)ns" - ), - } - - timings: dict[str, list[int]] = {} - with open(file_name) as f: - for line in f.readlines(): - print(line) - for metric, pattern in patterns.items(): - match = pattern.search(line) - if match: - timings[metric] = timings.get(metric, []) + [int(match.group(1))] - if remove_file: - os.remove(file_name) - return timings - - -def evaluate(evaluate_rust_timings=False): - stats = {} - - otherCX = QuantumCircuit(2) - otherCX.cx(1, 0) - otherCXGate = otherCX.to_gate() - oneQubitDecs = [ - OneQubitEulerDecomposer("ZYZ"), - OneQubitEulerDecomposer("ZXZ"), - OneQubitEulerDecomposer("XYX"), - OneQubitEulerDecomposer("XZX"), - ] - - print(f"Processing '{MQT_BENCH_DIR}/{MQT_BENCH_PATTERN}'...") - for file in glob.glob(f"{MQT_BENCH_DIR}/{MQT_BENCH_PATTERN}"): - name = os.path.basename(file).removesuffix(".qasm") - print(f"Decomposing {name} ({file})") - - start_time = time.perf_counter_ns() - dec = TwoQubitBasisDecomposer(CXGate(), euler_basis="ZYZ") - dec2 = TwoQubitBasisDecomposer(otherCXGate, euler_basis="ZYZ") - end_time = time.perf_counter_ns() - creation_time_us = (end_time - start_time) / 1000 - print(f"Python Basis Decomposition Creation: {creation_time_us}µs") - if evaluate_rust_timings: - rust_stats = read_rust_timings_file(remove_file=True) - creation_time_us = np.sum(rust_stats["twoQubitCreationTime"]) - twoQubitDecs = [dec, dec2] - - with open(file, "r") as f: - content = f.read() - try: - if is_qasm3: - content = "\n".join( - filter(lambda line: not "measure " in line, content.splitlines()) - ) - qc: QuantumCircuit = qasm3.loads(content) - else: - qc: QuantumCircuit = qasm2.loads(content) - except Exception as e: - print(f"FAILED: {name} ({e})") - continue - - # respect barriers - start_time = time.perf_counter_ns() - subcircuits = split_by_barriers(qc) - end_time = time.perf_counter_ns() - collection_time_us = (end_time - start_time) / 1000 - - decomposition_times_1q = [] - decomposition_times_2q = [] - complexity_changes = [] - num_two_qubit_decompositions = 0 - num_single_qubit_decompositions = 0 - for subcircuit in subcircuits: - if circuit_length(subcircuit) < 3 and not contains_foreign_gates( - subcircuit - ): - print(f"SKIPPED (length: {circuit_length(subcircuit)})") - continue - m = Operator(subcircuit) - decomposed_circuits = [] - if len(subcircuit.qubits) == 1: - start_time = time.perf_counter_ns() - for dec in oneQubitDecs: - decomposed_circuits.append(dec(m)) - end_time = time.perf_counter_ns() - decomposition_time = (end_time - start_time) / 1000 - - if evaluate_rust_timings: - rust_stats = read_rust_timings_file(remove_file=True) - decomposition_times_1q.append( - # measured in nanoseconds, convert to microseconds - np.sum(rust_stats["timePerSingleQubitDecomposition"]) / 1000 - ) - else: - decomposition_times_1q.append(decomposition_time) - - num_single_qubit_decompositions += 1 - elif len(subcircuit.qubits) == 2: - start_time = time.perf_counter_ns() - for dec in twoQubitDecs: - decomposed_circuits.append(dec(m)) - end_time = time.perf_counter_ns() - decomposition_time = (end_time - start_time) / 1000 - - if evaluate_rust_timings: - rust_stats = read_rust_timings_file(remove_file=True) - decomposition_times_2q.append( - np.sum(rust_stats["timePerTwoQubitDecomposition"][1:]) - ) - else: - decomposition_times_2q.append(decomposition_time) - - num_two_qubit_decompositions += 1 - else: - raise RuntimeError("Invalid circuit size!") - - before_complexity = circuit_complexity(subcircuit) - after_complexities = [circuit_complexity(x) for x in decomposed_circuits] - best_decomposed_circuit = decomposed_circuits[ - after_complexities.index(min(after_complexities)) - ] - best_after_complexity = min(after_complexities) - - print(subcircuit) - print(f"{before_complexity} -> {best_after_complexity}") - print(best_decomposed_circuit) - - print(Operator(subcircuit).data) - print("vs") - print(Operator(best_decomposed_circuit).data) - - complexity_changes.append(before_complexity - best_after_complexity) - - stats[name] = { - "timeInSingleQubitDecomposition": sum(decomposition_times_1q), - "timeInTwoQubitDecomposition": sum(decomposition_times_2q), - "subCircuitComplexityChange": sum(complexity_changes), - "totalTwoQubitDecompositions": num_two_qubit_decompositions, - "totalSingleQubitDecompositions": num_single_qubit_decompositions, - "twoQubitCreationTime": creation_time_us, - "timeInCircuitCollection": collection_time_us, - } - - print() - print(stats) - print(f"Total benchmarks: {len(stats)}") - return stats - - -if __name__ == "__main__": - evaluate_rust_timings = sys.argv[-1] == "--rust-timings" - print(f"Rust evaluation: {evaluate_rust_timings} ({sys.argv[-1]})") - evaluate(evaluate_rust_timings) diff --git a/thesis-evaluation/run.sh b/thesis-evaluation/run.sh deleted file mode 100755 index fce20ceb76..0000000000 --- a/thesis-evaluation/run.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh - -OUTPUT_DIR="tmp-output" - -MQT_CORE_BUILD_DIR="../build.release" -MQT_BENCH_BENCHMARK_DIR="../../mqt-bench/generated_benchmarks/v3_qasm3" -MQT_BENCH_PATTERN="*.qasm" - -mkdir -p "${OUTPUT_DIR}/broken" - -benchmark_count="$(eza -1 ${MQT_BENCH_BENCHMARK_DIR}/${MQT_BENCH_PATTERN} | wc -l)" -i=0 -success=0 - -for benchmark_path in ${MQT_BENCH_BENCHMARK_DIR}/${MQT_BENCH_PATTERN}; do - i=$((i + 1)) - benchmark="$(basename "${benchmark_path}")" - echo "${i}/${benchmark_count}: ${benchmark}" - result="$("${MQT_CORE_BUILD_DIR}/mlir/tools/mqt-cc/mqt-cc" --mlir-timing --mlir-statistics "${benchmark_path}" 2>&1)" - if [ "${?}" -eq 0 ]; then - echo "${result}" | rg 'Total Execution Time' -A 8 >"${OUTPUT_DIR}/${benchmark}.timing" - echo "${result}" | rg '\(S\) ' >"${OUTPUT_DIR}/${benchmark}.statistic" - echo "${result}" | rg 'module \{' -A 99999999999999 >"${OUTPUT_DIR}/${benchmark}.mlir" - echo "${result}" >"${OUTPUT_DIR}/${benchmark}.all" - echo "SUCCESS" - success=$((success + 1)) - else - echo "${result}" >"${OUTPUT_DIR}/broken/${benchmark}.error" - echo "FAILED" - fi -done - -echo "COMPLETED: ${success}/${i}" diff --git a/thesis-evaluation/total_evaluate.py b/thesis-evaluation/total_evaluate.py deleted file mode 100644 index dce75a7420..0000000000 --- a/thesis-evaluation/total_evaluate.py +++ /dev/null @@ -1,321 +0,0 @@ -import evaluate -import qiskit_run - -import matplotlib.pyplot as plt -from matplotlib.ticker import MaxNLocator -import math -import json -import os -import numpy as np -import subprocess - -OUT_DIR = "./figures" -if not os.path.exists(OUT_DIR): - os.makedirs(OUT_DIR, exist_ok=True) - -QISKIT_CACHE_FILE = "./evaluate_cache_qiskit.json" -QISKIT_RUST_CACHE_FILE = "./evaluate_cache_qiskit-rust.json" -MQT_CACHE_FILE = "./evaluate_cache_mqt.json" - - -def average_results( - all_results: list[dict[str, dict[str, int | float]]], -) -> dict[str, dict[str, int | float]]: - result = {} - for r in all_results: - for benchmark_name, measurements in r.items(): - if not benchmark_name in result: - result[benchmark_name] = {} - for metric_name, value in measurements.items(): - if not metric_name in result[benchmark_name]: - result[benchmark_name][metric_name] = 0.0 - result[benchmark_name][metric_name] += value / ITERATIONS - return result - - -if os.path.exists(QISKIT_CACHE_FILE): - with open(QISKIT_CACHE_FILE, "r") as f: - qiskit_results = json.load(f) -else: - ITERATIONS = 20 - all_results = [] - for _ in range(ITERATIONS): - all_results.append(qiskit_run.evaluate()) - - # [benchmark_name -> [metric_name -> value]] - qiskit_results = average_results(all_results) - - with open(QISKIT_CACHE_FILE, "w") as f: - json.dump(qiskit_results, f) - -if os.path.exists(MQT_CACHE_FILE): - with open(MQT_CACHE_FILE, "r") as f: - mqt_results = json.load(f) -else: - ITERATIONS = 20 - all_results = [] - for _ in range(ITERATIONS): - # evaluate.evalue() will only process statistics files; need to re-run mqt-cc using run.sh - subprocess.run(["./run.sh"]).check_returncode() - all_results.append(evaluate.evaluate()) - - # [benchmark_name -> [metric_name -> value]] - mqt_results = average_results(all_results) - - with open(MQT_CACHE_FILE, "w") as f: - json.dump(mqt_results, f) - -if os.path.exists(QISKIT_RUST_CACHE_FILE): - with open(QISKIT_RUST_CACHE_FILE, "r") as f: - qiskit_rust_results = json.load(f) -else: - ITERATIONS = 20 - all_results = [] - for _ in range(ITERATIONS): - all_results.append(qiskit_run.evaluate(evaluate_rust_timings=True)) - - # [benchmark_name -> [metric_name -> value]] - qiskit_rust_results = average_results(all_results) - - with open(QISKIT_RUST_CACHE_FILE, "w") as f: - json.dump(qiskit_rust_results, f) - -print("In MQT, but not Qiskit: ", mqt_results.keys() - qiskit_results.keys()) -print("In Qiskit, but not MQT: ", qiskit_results.keys() - mqt_results.keys()) - - -x: dict[str, list[str]] = {} -y1: dict[str, list[int | float]] = {} -y2: dict[str, list[int | float]] = {} - - -def define_division_metric( - new_metric: str, old_metric: str, divisor_metric: str, benchmark_name: str -): - y1[new_metric] = y1.get(new_metric, []) + [ - m[old_metric] / m[divisor_metric] if m[divisor_metric] > 0 else float("nan") - ] - y2[new_metric] = y2.get(new_metric, []) + [ - q[old_metric] / q[divisor_metric] if q[divisor_metric] > 0 else float("nan") - ] - x[new_metric] = x.get(new_metric, []) + [benchmark_name] - - -aliases_qiskit = { - "totalSingleQubitDecompositions": "successfulSingleQubitDecompositions", - "totalTwoQubitDecompositions": "successfulTwoQubitDecompositions", -} -aliases_mqt = { - "timeInCircuitCollection": "timeInCircuitCollectionStandalone", -} -names = sorted(mqt_results.keys() & qiskit_results.keys()) -for name in names: - m = mqt_results[name] - # q = qiskit_results[name] - q = qiskit_rust_results[name] - - for metric in m.keys() | q.keys(): - if metric in m: - y1[metric] = y1.get(metric, []) + [m[metric]] - if metric in aliases_mqt: - y1[aliases_mqt[metric]] = y1.get(aliases_mqt[metric], []) + [m[metric]] - if metric in q: - y2[metric] = y2.get(metric, []) + [q[metric]] - if metric in aliases_qiskit: - y2[aliases_qiskit[metric]] = y2.get(aliases_qiskit[metric], []) + [ - q[metric] - ] - - define_division_metric( - "timePerSingleQubitDecomposition", - "timeInSingleQubitDecomposition", - "totalSingleQubitDecompositions", - name, - ) - define_division_metric( - "timePerTwoQubitDecomposition", - "timeInTwoQubitDecomposition", - "totalTwoQubitDecompositions", - name, - ) - - for metric in y1.keys() | y2.keys(): - x[metric] = names - -titles = { - "subCircuitComplexityChange": "Complexity Improvement after Decomposition", - "successfulSingleQubitDecompositions": "Number of Successful Single-Qubit Decompositions", - "successfulTwoQubitDecompositions": "Number of Successful Two-Qubit Decompositions", - "timeInCircuitCollection": "Sub-Circuit Collection Time [µs]", - "timeInCircuitCollectionStandalone": "Sub-Circuit Collection Time [µs]", - "timeInSingleQubitDecomposition": "Total Time for Single-Qubit Decompositions [µs]", - "timeInTwoQubitDecomposition": "Total Time for Two-Qubit Decompositions [µs]", - "totalCircuitCollections": "Number of Sub-Circuit Collections", - "totalSingleQubitDecompositions": "Total Number of Single-Qubit Decompositions", - "totalTouchedGates": "Total Number of Gates in Collected Sub-Circuits", - "totalTwoQubitDecompositions": "Total Number of Two-Qubit Decompositions", - "timePerTwoQubitDecomposition": "Time / Two-Qubit Decomposition [µs]", - "timePerSingleQubitDecomposition": "Time / Single-Qubit Decomposition [µs]", - "twoQubitCreationTime": "Time for Creation of Two-Qubit Basis Decomposers [µs]", -} -legend_positions = { - "totalTouchedGates": "upper left", - "timeInCircuitCollection": "upper right", - "timeInCircuitCollectionStandalone": "upper left", - "timePerSingleQubitDecomposition": "upper left", - "timePerTwoQubitDecomposition": "lower right", - "totalTouchedGates": "upper left", - "twoQubitCreationTime": "lower right", -} -pruneFunctions = { - "timeInCircuitCollection": lambda value: np.isclose(value, 0), - "timeInCircuitCollectionStandalone": lambda value: np.isclose(value, 0), - "timeInSingleQubitDecomposition": lambda value: np.isclose(value, 0), - "timeInTwoQubitDecomposition": lambda value: np.isclose(value, 0), - "timePerTwoQubitDecomposition": lambda value: np.isclose(value, 0), - "timePerSingleQubitDecomposition": lambda value: np.isclose(value, 0), - "twoQubitCreationTime": lambda value: np.isclose(value, 0), -} -# modifications = { -# "subCircuitComplexityChange": lambda value: -1.0 * value, -# } -for metric in x.keys(): - plt.title(titles.get(metric, metric)) - # x_values = x[metric] # use for benchmark names on x axis - x_values = [str(i) for i in range(len(x[metric]))] - - # if metric in modifications: - # for y in y1[metric]: - # y = modifications[metric](y) - # for y in y2[metric]: - # y = modifications[metric](y) - - DEFAULT_POINT_SIZE = 100 - scale1 = [] - scale2 = [] - num_erased_y = 0 - for i in range(len(y1[metric])): - if metric in y1: - y1i = y1[metric][i - num_erased_y] - else: - y1i = None - if metric in y2: - y2i = y2[metric][i - num_erased_y] - else: - y2i = None - if ( - (y1i == None and y2i == None) - or (y1i != None and y2i != None and math.isnan(y1i) and math.isnan(y2i)) - or ( - y1i != None - and y2i != None - and pruneFunctions.get(metric, lambda _: False)(y1i) - and pruneFunctions.get(metric, lambda _: False)(y2i) - ) - ): - x_values.pop(i - num_erased_y) - if metric in y1: - y1[metric].pop(i - num_erased_y) - if metric in y2: - y2[metric].pop(i - num_erased_y) - num_erased_y += 1 - elif ( - y1i == None - or math.isnan(y1i) - or pruneFunctions.get(metric, lambda _: False)(y1i) - ): - scale1.append(0) - scale2.append(DEFAULT_POINT_SIZE) - elif ( - y2i == None - or math.isnan(y2i) - or pruneFunctions.get(metric, lambda _: False)(y2i) - ): - scale1.append(DEFAULT_POINT_SIZE) - scale2.append(0) - else: - scale1.append(DEFAULT_POINT_SIZE) - scale2.append(DEFAULT_POINT_SIZE) - - ymin = float("inf") - if metric in y1: - mqt_scatter = plt.scatter( - x_values, y1[metric], color="blue", s=scale1, alpha=0.4 - ) - mqt_scatter.set_label("MQT") - ymin = min(ymin, min(y1[metric])) - if metric in y2: - qiskit_scatter = plt.scatter( - x_values, y2[metric], color="red", s=scale2, alpha=0.4 - ) - qiskit_scatter.set_label("Qiskit") - ymin = min(ymin, min(y2[metric])) - - # plt.xticks(x_values) # does not work for strings - plt.xticks(rotation=45) - if ymin >= 0: - # let matplotlib handle non-positive values automatically - # plt.ylim(bottom=0) - # plt.margins(y=0.1) - ax = plt.gca() - ticks = ax.get_yticks() - if not np.any(np.isclose(ticks, 0)): - # ensure y axis starts at zero - plt.ylim(bottom=0) - else: - ax.set_yticks(ticks[ticks >= 0]) - - yint = [] - locs, labels = plt.yticks() - for each in locs: - yint.append(int(each)) - plt.yticks(yint) - - leg = plt.legend(loc=legend_positions.get(metric, "best")) - for handle in leg.legend_handles: - handle.set_sizes([DEFAULT_POINT_SIZE]) - plt.savefig( - f"{OUT_DIR}/{metric}.pdf", format="pdf", bbox_inches="tight", pad_inches=0 - ) - #plt.show() - plt.clf() - -for i, name in enumerate(names): - if "_indep_1_none_O0" in name: - print( - i, " & \\code{", name.removesuffix("_indep_1_none_O0"), "} & 1\\\\", sep="" - ) - elif "_indep_2_none_O0" in name: - print( - i, " & \\code{", name.removesuffix("_indep_2_none_O0"), "} & 2\\\\", sep="" - ) - else: - print("UNKNOWN") - -for metric in x.keys(): - print() - print( - f"Average MQT {metric}:", - np.average(np.ma.masked_invalid(y1[metric])) if metric in y1 else "-", - ) - print( - f"Average Qiskit {metric}:", - np.average(np.ma.masked_invalid(y2[metric])) if metric in y2 else "-", - ) - print( - f"Median MQT {metric}:", - np.median(np.ma.masked_invalid(y1[metric])) if metric in y1 else "-", - ) - print( - f"Median Qiskit {metric}:", - np.median(np.ma.masked_invalid(y2[metric])) if metric in y2 else "-", - ) - -print( - "Num of improved circuits (MQT): ", - len(list(filter(lambda x: x > 0, y1["subCircuitComplexityChange"]))), -) -print( - "Num of improved circuits (Qiskit): ", - len(list(filter(lambda x: x > 0, y2["subCircuitComplexityChange"]))), -) From 8a54aa1343882d9d99211213846c33db5f59bc09 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Sun, 22 Feb 2026 13:21:55 +0100 Subject: [PATCH 234/237] fix compilation issues after merge --- mlir/include/mlir/Passes/Decomposition/Helpers.h | 2 +- mlir/lib/Passes/Patterns/Decomposition/Helpers.cpp | 3 ++- mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp | 8 +++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/mlir/include/mlir/Passes/Decomposition/Helpers.h b/mlir/include/mlir/Passes/Decomposition/Helpers.h index ec48ab1cb8..ce58abc47d 100644 --- a/mlir/include/mlir/Passes/Decomposition/Helpers.h +++ b/mlir/include/mlir/Passes/Decomposition/Helpers.h @@ -11,7 +11,7 @@ #pragma once #include "ir/operations/OpType.hpp" -#include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QCO/IR/QCOInterfaces.h" #include #include diff --git a/mlir/lib/Passes/Patterns/Decomposition/Helpers.cpp b/mlir/lib/Passes/Patterns/Decomposition/Helpers.cpp index 770d6d8816..862a24ba27 100644 --- a/mlir/lib/Passes/Patterns/Decomposition/Helpers.cpp +++ b/mlir/lib/Passes/Patterns/Decomposition/Helpers.cpp @@ -11,7 +11,8 @@ #include "mlir/Passes/Decomposition/Helpers.h" #include "ir/operations/OpType.hpp" -#include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QCO/IR/QCOInterfaces.h" +#include "mlir/Dialect/QCO/IR/QCOOps.h" #include #include diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 50d2e774b4..98936becb3 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -9,7 +9,8 @@ */ #include "ir/operations/OpType.hpp" -#include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QCO/IR/QCOInterfaces.h" +#include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Passes/Decomposition/BasisDecomposer.h" #include "mlir/Passes/Decomposition/EulerBasis.h" #include "mlir/Passes/Decomposition/EulerDecomposition.h" @@ -553,8 +554,9 @@ struct GateDecompositionPattern final (collectInQubits(inQubitsAndParams), ...); return rewriter.create( location, ctrlQubits, mlir::ValueRange{inQubits}, - rewriter.create(location, - std::forward(inQubitsAndParams)...)); + [&](auto&& in) -> llvm::SmallVector { + return rewriter.create(location, in)->getResults(); + }); } /** From d058d0385208ebae57a4c931e089132a872f8f99 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Mon, 23 Feb 2026 22:27:36 +0100 Subject: [PATCH 235/237] fix linter issue --- mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp index 98936becb3..1a991d4f32 100644 --- a/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp +++ b/mlir/lib/Passes/Patterns/GateDecompositionPattern.cpp @@ -551,7 +551,7 @@ struct GateDecompositionPattern final inQubits.push_back(std::forward(x)); } }; - (collectInQubits(inQubitsAndParams), ...); + (collectInQubits(std::forward(inQubitsAndParams)), ...); return rewriter.create( location, ctrlQubits, mlir::ValueRange{inQubits}, [&](auto&& in) -> llvm::SmallVector { From 7bc9a35239a011bb61bbd9137bc85b490db9e00c Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Wed, 25 Feb 2026 11:53:11 +0100 Subject: [PATCH 236/237] remove eigen alignment limit from cmake --- mlir/lib/Dialect/QCO/IR/CMakeLists.txt | 3 --- mlir/lib/Passes/CMakeLists.txt | 3 --- mlir/unittests/Passes/Decomposition/CMakeLists.txt | 2 -- 3 files changed, 8 deletions(-) diff --git a/mlir/lib/Dialect/QCO/IR/CMakeLists.txt b/mlir/lib/Dialect/QCO/IR/CMakeLists.txt index 2adea8543e..776101ba06 100644 --- a/mlir/lib/Dialect/QCO/IR/CMakeLists.txt +++ b/mlir/lib/Dialect/QCO/IR/CMakeLists.txt @@ -53,6 +53,3 @@ target_sources( ${MQT_MLIR_BUILD_INCLUDE_DIR} FILES ${IR_HEADERS_BUILD}) - -# fix alignment issues for stack-allocated fixed-size matrices -target_compile_definitions(MLIRQCODialect PUBLIC EIGEN_MAX_STATIC_ALIGN_BYTES=16) diff --git a/mlir/lib/Passes/CMakeLists.txt b/mlir/lib/Passes/CMakeLists.txt index 58962248bd..5af2a221e3 100644 --- a/mlir/lib/Passes/CMakeLists.txt +++ b/mlir/lib/Passes/CMakeLists.txt @@ -41,6 +41,3 @@ target_sources( ${MQT_MLIR_BUILD_INCLUDE_DIR} FILES ${PASSES_HEADERS_BUILD}) - -# fix alignment issues for stack-allocated fixed-size matrices -target_compile_definitions(QcoPasses PUBLIC EIGEN_MAX_STATIC_ALIGN_BYTES=16) diff --git a/mlir/unittests/Passes/Decomposition/CMakeLists.txt b/mlir/unittests/Passes/Decomposition/CMakeLists.txt index 8dda1061e0..878daea605 100644 --- a/mlir/unittests/Passes/Decomposition/CMakeLists.txt +++ b/mlir/unittests/Passes/Decomposition/CMakeLists.txt @@ -24,8 +24,6 @@ if(NOT TARGET ${testname}) MQT::CoreIR Eigen3::Eigen QcoPasses) - # fix alignment issues for stack-allocated fixed-size matrices - target_compile_definitions(${testname} PRIVATE EIGEN_MAX_STATIC_ALIGN_BYTES=16) # discover tests gtest_discover_tests(${testname} DISCOVERY_TIMEOUT 60) set_target_properties(${testname} PROPERTIES FOLDER unittests) From b88cc8950ea64eb0df0bd6b62c8d7aa1ad9430d6 Mon Sep 17 00:00:00 2001 From: Tamino Bauknecht Date: Tue, 10 Mar 2026 15:16:28 +0100 Subject: [PATCH 237/237] cleanup [skip ci] --- test.mlir | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 test.mlir diff --git a/test.mlir b/test.mlir deleted file mode 100644 index 00f505ef3b..0000000000 --- a/test.mlir +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -module { - func.func @main() attributes { passthrough = ["entry_point"] } { - %q0 = qc.alloc : !qc.qubit - %q1 = qc.alloc : !qc.qubit - - qc.h %q0 : !qc.qubit - qc.ctrl(%q0) { qc.x %q1 : !qc.qubit } : !qc.qubit - - %c0 = qc.measure %q0 : !qc.qubit -> i1 - %c1 = qc.measure %q1 : !qc.qubit -> i1 - - qc.dealloc %q0 : !qc.qubit - qc.dealloc %q1 : !qc.qubit - - func.return - } -}