mqt-core 3.3.2__cp312-cp312-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mqt/core/__init__.py +89 -0
- mqt/core/__main__.py +55 -0
- mqt/core/_commands.py +52 -0
- mqt/core/_compat/__init__.py +11 -0
- mqt/core/_compat/typing.py +29 -0
- mqt/core/_version.py +34 -0
- mqt/core/_version.pyi +12 -0
- mqt/core/bin/mqt-core-algorithms.dll +0 -0
- mqt/core/bin/mqt-core-circuit-optimizer.dll +0 -0
- mqt/core/bin/mqt-core-dd.dll +0 -0
- mqt/core/bin/mqt-core-ds.dll +0 -0
- mqt/core/bin/mqt-core-fomac.dll +0 -0
- mqt/core/bin/mqt-core-ir.dll +0 -0
- mqt/core/bin/mqt-core-na-fomac.dll +0 -0
- mqt/core/bin/mqt-core-na.dll +0 -0
- mqt/core/bin/mqt-core-qasm.dll +0 -0
- mqt/core/bin/mqt-core-qdmi-driver.dll +0 -0
- mqt/core/bin/mqt-core-qdmi-na-device.dll +0 -0
- mqt/core/bin/mqt-core-zx.dll +0 -0
- mqt/core/dd.cp312-win_amd64.pyd +0 -0
- mqt/core/dd.pyi +1016 -0
- mqt/core/dd_evaluation.py +368 -0
- mqt/core/fomac.cp312-win_amd64.pyd +0 -0
- mqt/core/fomac.pyi +125 -0
- mqt/core/include/mqt-core/algorithms/BernsteinVazirani.hpp +39 -0
- mqt/core/include/mqt-core/algorithms/GHZState.hpp +18 -0
- mqt/core/include/mqt-core/algorithms/Grover.hpp +33 -0
- mqt/core/include/mqt-core/algorithms/QFT.hpp +21 -0
- mqt/core/include/mqt-core/algorithms/QPE.hpp +30 -0
- mqt/core/include/mqt-core/algorithms/RandomCliffordCircuit.hpp +22 -0
- mqt/core/include/mqt-core/algorithms/StatePreparation.hpp +43 -0
- mqt/core/include/mqt-core/algorithms/WState.hpp +18 -0
- mqt/core/include/mqt-core/algorithms/mqt_core_algorithms_export.h +43 -0
- mqt/core/include/mqt-core/boost/config/abi/borland_prefix.hpp +27 -0
- mqt/core/include/mqt-core/boost/config/abi/borland_suffix.hpp +12 -0
- mqt/core/include/mqt-core/boost/config/abi/msvc_prefix.hpp +22 -0
- mqt/core/include/mqt-core/boost/config/abi/msvc_suffix.hpp +8 -0
- mqt/core/include/mqt-core/boost/config/abi_prefix.hpp +25 -0
- mqt/core/include/mqt-core/boost/config/abi_suffix.hpp +25 -0
- mqt/core/include/mqt-core/boost/config/assert_cxx03.hpp +211 -0
- mqt/core/include/mqt-core/boost/config/assert_cxx11.hpp +212 -0
- mqt/core/include/mqt-core/boost/config/assert_cxx14.hpp +47 -0
- mqt/core/include/mqt-core/boost/config/assert_cxx17.hpp +65 -0
- mqt/core/include/mqt-core/boost/config/assert_cxx20.hpp +59 -0
- mqt/core/include/mqt-core/boost/config/assert_cxx23.hpp +41 -0
- mqt/core/include/mqt-core/boost/config/assert_cxx98.hpp +23 -0
- mqt/core/include/mqt-core/boost/config/auto_link.hpp +525 -0
- mqt/core/include/mqt-core/boost/config/compiler/borland.hpp +342 -0
- mqt/core/include/mqt-core/boost/config/compiler/clang.hpp +370 -0
- mqt/core/include/mqt-core/boost/config/compiler/clang_version.hpp +89 -0
- mqt/core/include/mqt-core/boost/config/compiler/codegear.hpp +389 -0
- mqt/core/include/mqt-core/boost/config/compiler/comeau.hpp +59 -0
- mqt/core/include/mqt-core/boost/config/compiler/common_edg.hpp +185 -0
- mqt/core/include/mqt-core/boost/config/compiler/compaq_cxx.hpp +19 -0
- mqt/core/include/mqt-core/boost/config/compiler/cray.hpp +446 -0
- mqt/core/include/mqt-core/boost/config/compiler/diab.hpp +26 -0
- mqt/core/include/mqt-core/boost/config/compiler/digitalmars.hpp +146 -0
- mqt/core/include/mqt-core/boost/config/compiler/gcc.hpp +386 -0
- mqt/core/include/mqt-core/boost/config/compiler/gcc_xml.hpp +115 -0
- mqt/core/include/mqt-core/boost/config/compiler/greenhills.hpp +28 -0
- mqt/core/include/mqt-core/boost/config/compiler/hp_acc.hpp +153 -0
- mqt/core/include/mqt-core/boost/config/compiler/intel.hpp +577 -0
- mqt/core/include/mqt-core/boost/config/compiler/kai.hpp +33 -0
- mqt/core/include/mqt-core/boost/config/compiler/metrowerks.hpp +201 -0
- mqt/core/include/mqt-core/boost/config/compiler/mpw.hpp +143 -0
- mqt/core/include/mqt-core/boost/config/compiler/nvcc.hpp +64 -0
- mqt/core/include/mqt-core/boost/config/compiler/pathscale.hpp +141 -0
- mqt/core/include/mqt-core/boost/config/compiler/pgi.hpp +23 -0
- mqt/core/include/mqt-core/boost/config/compiler/sgi_mipspro.hpp +29 -0
- mqt/core/include/mqt-core/boost/config/compiler/sunpro_cc.hpp +225 -0
- mqt/core/include/mqt-core/boost/config/compiler/vacpp.hpp +189 -0
- mqt/core/include/mqt-core/boost/config/compiler/visualc.hpp +398 -0
- mqt/core/include/mqt-core/boost/config/compiler/xlcpp.hpp +303 -0
- mqt/core/include/mqt-core/boost/config/compiler/xlcpp_zos.hpp +174 -0
- mqt/core/include/mqt-core/boost/config/detail/cxx_composite.hpp +218 -0
- mqt/core/include/mqt-core/boost/config/detail/posix_features.hpp +95 -0
- mqt/core/include/mqt-core/boost/config/detail/select_compiler_config.hpp +157 -0
- mqt/core/include/mqt-core/boost/config/detail/select_platform_config.hpp +147 -0
- mqt/core/include/mqt-core/boost/config/detail/select_stdlib_config.hpp +121 -0
- mqt/core/include/mqt-core/boost/config/detail/suffix.hpp +1334 -0
- mqt/core/include/mqt-core/boost/config/header_deprecated.hpp +26 -0
- mqt/core/include/mqt-core/boost/config/helper_macros.hpp +37 -0
- mqt/core/include/mqt-core/boost/config/no_tr1/cmath.hpp +28 -0
- mqt/core/include/mqt-core/boost/config/no_tr1/complex.hpp +28 -0
- mqt/core/include/mqt-core/boost/config/no_tr1/functional.hpp +28 -0
- mqt/core/include/mqt-core/boost/config/no_tr1/memory.hpp +28 -0
- mqt/core/include/mqt-core/boost/config/no_tr1/utility.hpp +28 -0
- mqt/core/include/mqt-core/boost/config/platform/aix.hpp +33 -0
- mqt/core/include/mqt-core/boost/config/platform/amigaos.hpp +15 -0
- mqt/core/include/mqt-core/boost/config/platform/beos.hpp +26 -0
- mqt/core/include/mqt-core/boost/config/platform/bsd.hpp +83 -0
- mqt/core/include/mqt-core/boost/config/platform/cloudabi.hpp +18 -0
- mqt/core/include/mqt-core/boost/config/platform/cray.hpp +18 -0
- mqt/core/include/mqt-core/boost/config/platform/cygwin.hpp +71 -0
- mqt/core/include/mqt-core/boost/config/platform/haiku.hpp +31 -0
- mqt/core/include/mqt-core/boost/config/platform/hpux.hpp +87 -0
- mqt/core/include/mqt-core/boost/config/platform/irix.hpp +31 -0
- mqt/core/include/mqt-core/boost/config/platform/linux.hpp +106 -0
- mqt/core/include/mqt-core/boost/config/platform/macos.hpp +87 -0
- mqt/core/include/mqt-core/boost/config/platform/qnxnto.hpp +31 -0
- mqt/core/include/mqt-core/boost/config/platform/solaris.hpp +31 -0
- mqt/core/include/mqt-core/boost/config/platform/symbian.hpp +97 -0
- mqt/core/include/mqt-core/boost/config/platform/vms.hpp +25 -0
- mqt/core/include/mqt-core/boost/config/platform/vxworks.hpp +422 -0
- mqt/core/include/mqt-core/boost/config/platform/wasm.hpp +23 -0
- mqt/core/include/mqt-core/boost/config/platform/win32.hpp +90 -0
- mqt/core/include/mqt-core/boost/config/platform/zos.hpp +32 -0
- mqt/core/include/mqt-core/boost/config/pragma_message.hpp +31 -0
- mqt/core/include/mqt-core/boost/config/requires_threads.hpp +92 -0
- mqt/core/include/mqt-core/boost/config/stdlib/dinkumware.hpp +324 -0
- mqt/core/include/mqt-core/boost/config/stdlib/libcomo.hpp +93 -0
- mqt/core/include/mqt-core/boost/config/stdlib/libcpp.hpp +180 -0
- mqt/core/include/mqt-core/boost/config/stdlib/libstdcpp3.hpp +482 -0
- mqt/core/include/mqt-core/boost/config/stdlib/modena.hpp +79 -0
- mqt/core/include/mqt-core/boost/config/stdlib/msl.hpp +98 -0
- mqt/core/include/mqt-core/boost/config/stdlib/roguewave.hpp +208 -0
- mqt/core/include/mqt-core/boost/config/stdlib/sgi.hpp +168 -0
- mqt/core/include/mqt-core/boost/config/stdlib/stlport.hpp +258 -0
- mqt/core/include/mqt-core/boost/config/stdlib/vacpp.hpp +74 -0
- mqt/core/include/mqt-core/boost/config/stdlib/xlcpp_zos.hpp +61 -0
- mqt/core/include/mqt-core/boost/config/user.hpp +133 -0
- mqt/core/include/mqt-core/boost/config/warning_disable.hpp +47 -0
- mqt/core/include/mqt-core/boost/config/workaround.hpp +305 -0
- mqt/core/include/mqt-core/boost/config.hpp +67 -0
- mqt/core/include/mqt-core/boost/cstdint.hpp +556 -0
- mqt/core/include/mqt-core/boost/cxx11_char_types.hpp +70 -0
- mqt/core/include/mqt-core/boost/detail/workaround.hpp +10 -0
- mqt/core/include/mqt-core/boost/limits.hpp +146 -0
- mqt/core/include/mqt-core/boost/multiprecision/complex128.hpp +24 -0
- mqt/core/include/mqt-core/boost/multiprecision/complex_adaptor.hpp +1046 -0
- mqt/core/include/mqt-core/boost/multiprecision/concepts/mp_number_archetypes.hpp +257 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_bin_float/io.hpp +698 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_bin_float/transcendental.hpp +157 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_bin_float.hpp +2297 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_complex.hpp +12 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_dec_float.hpp +3690 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_int/add.hpp +368 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_int/add_unsigned.hpp +387 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_int/bitwise.hpp +889 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_int/checked.hpp +178 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_int/comparison.hpp +374 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_int/cpp_int_config.hpp +161 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_int/divide.hpp +703 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_int/import_export.hpp +248 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_int/intel_intrinsics.hpp +138 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_int/limits.hpp +282 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_int/literals.hpp +295 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_int/misc.hpp +1457 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_int/multiply.hpp +848 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_int/serialize.hpp +211 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_int/value_pack.hpp +42 -0
- mqt/core/include/mqt-core/boost/multiprecision/cpp_int.hpp +2360 -0
- mqt/core/include/mqt-core/boost/multiprecision/debug_adaptor.hpp +760 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/assert.hpp +29 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/atomic.hpp +62 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/bitscan.hpp +317 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/check_cpp11_config.hpp +64 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/constexpr.hpp +88 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/default_ops.hpp +4052 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/digits.hpp +49 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/dynamic_array.hpp +44 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/empty_value.hpp +87 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/endian.hpp +35 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/et_ops.hpp +1831 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/float128_functions.hpp +95 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/float_string_cvt.hpp +333 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/fpclassify.hpp +101 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/functions/constants.hpp +288 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/functions/pow.hpp +905 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/functions/trig.hpp +1058 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/functions/trunc.hpp +82 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/generic_interconvert.hpp +687 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/hash.hpp +56 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/integer_ops.hpp +474 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/itos.hpp +39 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/min_max.hpp +106 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/no_et_ops.hpp +661 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/no_exceptions_support.hpp +55 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/number_base.hpp +1656 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/number_compare.hpp +848 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/precision.hpp +313 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/rebind.hpp +19 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/standalone_config.hpp +148 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/static_array.hpp +42 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/string_helpers.hpp +48 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/tables.hpp +80 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/ublas_interop.hpp +75 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/uniform_int_distribution.hpp +212 -0
- mqt/core/include/mqt-core/boost/multiprecision/detail/utype_helper.hpp +374 -0
- mqt/core/include/mqt-core/boost/multiprecision/eigen.hpp +248 -0
- mqt/core/include/mqt-core/boost/multiprecision/float128.hpp +920 -0
- mqt/core/include/mqt-core/boost/multiprecision/fwd.hpp +268 -0
- mqt/core/include/mqt-core/boost/multiprecision/gmp.hpp +4060 -0
- mqt/core/include/mqt-core/boost/multiprecision/integer.hpp +363 -0
- mqt/core/include/mqt-core/boost/multiprecision/logged_adaptor.hpp +834 -0
- mqt/core/include/mqt-core/boost/multiprecision/miller_rabin.hpp +221 -0
- mqt/core/include/mqt-core/boost/multiprecision/mpc.hpp +1721 -0
- mqt/core/include/mqt-core/boost/multiprecision/mpfi.hpp +2559 -0
- mqt/core/include/mqt-core/boost/multiprecision/mpfr.hpp +3644 -0
- mqt/core/include/mqt-core/boost/multiprecision/number.hpp +2500 -0
- mqt/core/include/mqt-core/boost/multiprecision/random.hpp +23 -0
- mqt/core/include/mqt-core/boost/multiprecision/rational_adaptor.hpp +1289 -0
- mqt/core/include/mqt-core/boost/multiprecision/tommath.hpp +1034 -0
- mqt/core/include/mqt-core/boost/multiprecision/traits/explicit_conversion.hpp +67 -0
- mqt/core/include/mqt-core/boost/multiprecision/traits/extract_exponent_type.hpp +28 -0
- mqt/core/include/mqt-core/boost/multiprecision/traits/is_backend.hpp +91 -0
- mqt/core/include/mqt-core/boost/multiprecision/traits/is_byte_container.hpp +51 -0
- mqt/core/include/mqt-core/boost/multiprecision/traits/is_complex.hpp +22 -0
- mqt/core/include/mqt-core/boost/multiprecision/traits/is_convertible_arithmetic.hpp +51 -0
- mqt/core/include/mqt-core/boost/multiprecision/traits/is_restricted_conversion.hpp +47 -0
- mqt/core/include/mqt-core/boost/multiprecision/traits/is_variable_precision.hpp +25 -0
- mqt/core/include/mqt-core/boost/multiprecision/traits/max_digits10.hpp +79 -0
- mqt/core/include/mqt-core/boost/multiprecision/traits/std_integer_traits.hpp +90 -0
- mqt/core/include/mqt-core/boost/multiprecision/traits/transcendental_reduction_type.hpp +21 -0
- mqt/core/include/mqt-core/boost/version.hpp +32 -0
- mqt/core/include/mqt-core/circuit_optimizer/CircuitOptimizer.hpp +119 -0
- mqt/core/include/mqt-core/circuit_optimizer/mqt_core_circuit_optimizer_export.h +43 -0
- mqt/core/include/mqt-core/datastructures/DirectedAcyclicGraph.hpp +117 -0
- mqt/core/include/mqt-core/datastructures/DirectedGraph.hpp +158 -0
- mqt/core/include/mqt-core/datastructures/DisjointSet.hpp +50 -0
- mqt/core/include/mqt-core/datastructures/Layer.hpp +172 -0
- mqt/core/include/mqt-core/datastructures/SymmetricMatrix.hpp +57 -0
- mqt/core/include/mqt-core/datastructures/UndirectedGraph.hpp +227 -0
- mqt/core/include/mqt-core/datastructures/mqt_core_ds_export.h +43 -0
- mqt/core/include/mqt-core/dd/Approximation.hpp +45 -0
- mqt/core/include/mqt-core/dd/CachedEdge.hpp +174 -0
- mqt/core/include/mqt-core/dd/Complex.hpp +165 -0
- mqt/core/include/mqt-core/dd/ComplexNumbers.hpp +150 -0
- mqt/core/include/mqt-core/dd/ComplexValue.hpp +184 -0
- mqt/core/include/mqt-core/dd/ComputeTable.hpp +183 -0
- mqt/core/include/mqt-core/dd/DDDefinitions.hpp +139 -0
- mqt/core/include/mqt-core/dd/DDpackageConfig.hpp +104 -0
- mqt/core/include/mqt-core/dd/DensityNoiseTable.hpp +114 -0
- mqt/core/include/mqt-core/dd/Edge.hpp +416 -0
- mqt/core/include/mqt-core/dd/Export.hpp +438 -0
- mqt/core/include/mqt-core/dd/FunctionalityConstruction.hpp +75 -0
- mqt/core/include/mqt-core/dd/GateMatrixDefinitions.hpp +43 -0
- mqt/core/include/mqt-core/dd/LinkedListBase.hpp +45 -0
- mqt/core/include/mqt-core/dd/MemoryManager.hpp +193 -0
- mqt/core/include/mqt-core/dd/Node.hpp +223 -0
- mqt/core/include/mqt-core/dd/NoiseFunctionality.hpp +144 -0
- mqt/core/include/mqt-core/dd/Operations.hpp +306 -0
- mqt/core/include/mqt-core/dd/Package.hpp +2036 -0
- mqt/core/include/mqt-core/dd/Package_fwd.hpp +22 -0
- mqt/core/include/mqt-core/dd/RealNumber.hpp +255 -0
- mqt/core/include/mqt-core/dd/RealNumberUniqueTable.hpp +217 -0
- mqt/core/include/mqt-core/dd/Simulation.hpp +98 -0
- mqt/core/include/mqt-core/dd/StateGeneration.hpp +143 -0
- mqt/core/include/mqt-core/dd/StochasticNoiseOperationTable.hpp +88 -0
- mqt/core/include/mqt-core/dd/UnaryComputeTable.hpp +121 -0
- mqt/core/include/mqt-core/dd/UniqueTable.hpp +243 -0
- mqt/core/include/mqt-core/dd/mqt_core_dd_export.h +43 -0
- mqt/core/include/mqt-core/dd/statistics/MemoryManagerStatistics.hpp +84 -0
- mqt/core/include/mqt-core/dd/statistics/PackageStatistics.hpp +55 -0
- mqt/core/include/mqt-core/dd/statistics/Statistics.hpp +48 -0
- mqt/core/include/mqt-core/dd/statistics/TableStatistics.hpp +79 -0
- mqt/core/include/mqt-core/dd/statistics/UniqueTableStatistics.hpp +31 -0
- mqt/core/include/mqt-core/fomac/FoMaC.hpp +568 -0
- mqt/core/include/mqt-core/ir/Definitions.hpp +108 -0
- mqt/core/include/mqt-core/ir/Permutation.hpp +213 -0
- mqt/core/include/mqt-core/ir/QuantumComputation.hpp +596 -0
- mqt/core/include/mqt-core/ir/Register.hpp +125 -0
- mqt/core/include/mqt-core/ir/mqt_core_ir_export.h +43 -0
- mqt/core/include/mqt-core/ir/operations/AodOperation.hpp +92 -0
- mqt/core/include/mqt-core/ir/operations/CompoundOperation.hpp +212 -0
- mqt/core/include/mqt-core/ir/operations/Control.hpp +142 -0
- mqt/core/include/mqt-core/ir/operations/Expression.hpp +847 -0
- mqt/core/include/mqt-core/ir/operations/IfElseOperation.hpp +169 -0
- mqt/core/include/mqt-core/ir/operations/NonUnitaryOperation.hpp +118 -0
- mqt/core/include/mqt-core/ir/operations/OpType.hpp +120 -0
- mqt/core/include/mqt-core/ir/operations/OpType.inc +76 -0
- mqt/core/include/mqt-core/ir/operations/Operation.hpp +247 -0
- mqt/core/include/mqt-core/ir/operations/StandardOperation.hpp +140 -0
- mqt/core/include/mqt-core/ir/operations/SymbolicOperation.hpp +144 -0
- mqt/core/include/mqt-core/mqt_na_qdmi/device.h +602 -0
- mqt/core/include/mqt-core/mqt_na_qdmi/types.h +78 -0
- mqt/core/include/mqt-core/na/NAComputation.hpp +185 -0
- mqt/core/include/mqt-core/na/device/Device.hpp +410 -0
- mqt/core/include/mqt-core/na/device/DeviceMemberInitializers.hpp +724 -0
- mqt/core/include/mqt-core/na/device/Generator.hpp +447 -0
- mqt/core/include/mqt-core/na/entities/Atom.hpp +62 -0
- mqt/core/include/mqt-core/na/entities/Location.hpp +154 -0
- mqt/core/include/mqt-core/na/entities/Zone.hpp +95 -0
- mqt/core/include/mqt-core/na/fomac/Device.hpp +169 -0
- mqt/core/include/mqt-core/na/mqt_core_na_export.h +43 -0
- mqt/core/include/mqt-core/na/operations/GlobalCZOp.hpp +38 -0
- mqt/core/include/mqt-core/na/operations/GlobalOp.hpp +58 -0
- mqt/core/include/mqt-core/na/operations/GlobalRYOp.hpp +42 -0
- mqt/core/include/mqt-core/na/operations/LoadOp.hpp +89 -0
- mqt/core/include/mqt-core/na/operations/LocalOp.hpp +56 -0
- mqt/core/include/mqt-core/na/operations/LocalRZOp.hpp +42 -0
- mqt/core/include/mqt-core/na/operations/LocalUOp.hpp +49 -0
- mqt/core/include/mqt-core/na/operations/MoveOp.hpp +66 -0
- mqt/core/include/mqt-core/na/operations/Op.hpp +62 -0
- mqt/core/include/mqt-core/na/operations/ShuttlingOp.hpp +51 -0
- mqt/core/include/mqt-core/na/operations/StoreOp.hpp +87 -0
- mqt/core/include/mqt-core/qasm3/Exception.hpp +85 -0
- mqt/core/include/mqt-core/qasm3/Gate.hpp +65 -0
- mqt/core/include/mqt-core/qasm3/Importer.hpp +192 -0
- mqt/core/include/mqt-core/qasm3/InstVisitor.hpp +145 -0
- mqt/core/include/mqt-core/qasm3/NestedEnvironment.hpp +41 -0
- mqt/core/include/mqt-core/qasm3/Parser.hpp +170 -0
- mqt/core/include/mqt-core/qasm3/Scanner.hpp +73 -0
- mqt/core/include/mqt-core/qasm3/Statement.hpp +486 -0
- mqt/core/include/mqt-core/qasm3/Statement_fwd.hpp +39 -0
- mqt/core/include/mqt-core/qasm3/StdGates.hpp +232 -0
- mqt/core/include/mqt-core/qasm3/Token.hpp +198 -0
- mqt/core/include/mqt-core/qasm3/Types.hpp +238 -0
- mqt/core/include/mqt-core/qasm3/Types_fwd.hpp +22 -0
- mqt/core/include/mqt-core/qasm3/mqt_core_qasm_export.h +43 -0
- mqt/core/include/mqt-core/qasm3/passes/CompilerPass.hpp +22 -0
- mqt/core/include/mqt-core/qasm3/passes/ConstEvalPass.hpp +102 -0
- mqt/core/include/mqt-core/qasm3/passes/TypeCheckPass.hpp +124 -0
- mqt/core/include/mqt-core/qdmi/Driver.hpp +431 -0
- mqt/core/include/mqt-core/zx/FunctionalityConstruction.hpp +125 -0
- mqt/core/include/mqt-core/zx/Rational.hpp +318 -0
- mqt/core/include/mqt-core/zx/Rules.hpp +132 -0
- mqt/core/include/mqt-core/zx/Simplify.hpp +182 -0
- mqt/core/include/mqt-core/zx/Utils.hpp +212 -0
- mqt/core/include/mqt-core/zx/ZXDefinitions.hpp +93 -0
- mqt/core/include/mqt-core/zx/ZXDiagram.hpp +480 -0
- mqt/core/include/mqt-core/zx/mqt_core_zx_export.h +43 -0
- mqt/core/include/nlohmann/adl_serializer.hpp +55 -0
- mqt/core/include/nlohmann/byte_container_with_subtype.hpp +103 -0
- mqt/core/include/nlohmann/detail/abi_macros.hpp +111 -0
- mqt/core/include/nlohmann/detail/conversions/from_json.hpp +577 -0
- mqt/core/include/nlohmann/detail/conversions/to_chars.hpp +1118 -0
- mqt/core/include/nlohmann/detail/conversions/to_json.hpp +479 -0
- mqt/core/include/nlohmann/detail/exceptions.hpp +291 -0
- mqt/core/include/nlohmann/detail/hash.hpp +129 -0
- mqt/core/include/nlohmann/detail/input/binary_reader.hpp +3068 -0
- mqt/core/include/nlohmann/detail/input/input_adapters.hpp +549 -0
- mqt/core/include/nlohmann/detail/input/json_sax.hpp +986 -0
- mqt/core/include/nlohmann/detail/input/lexer.hpp +1643 -0
- mqt/core/include/nlohmann/detail/input/parser.hpp +519 -0
- mqt/core/include/nlohmann/detail/input/position_t.hpp +37 -0
- mqt/core/include/nlohmann/detail/iterators/internal_iterator.hpp +35 -0
- mqt/core/include/nlohmann/detail/iterators/iter_impl.hpp +760 -0
- mqt/core/include/nlohmann/detail/iterators/iteration_proxy.hpp +235 -0
- mqt/core/include/nlohmann/detail/iterators/iterator_traits.hpp +61 -0
- mqt/core/include/nlohmann/detail/iterators/json_reverse_iterator.hpp +130 -0
- mqt/core/include/nlohmann/detail/iterators/primitive_iterator.hpp +132 -0
- mqt/core/include/nlohmann/detail/json_custom_base_class.hpp +39 -0
- mqt/core/include/nlohmann/detail/json_pointer.hpp +988 -0
- mqt/core/include/nlohmann/detail/json_ref.hpp +78 -0
- mqt/core/include/nlohmann/detail/macro_scope.hpp +595 -0
- mqt/core/include/nlohmann/detail/macro_unscope.hpp +46 -0
- mqt/core/include/nlohmann/detail/meta/call_std/begin.hpp +17 -0
- mqt/core/include/nlohmann/detail/meta/call_std/end.hpp +17 -0
- mqt/core/include/nlohmann/detail/meta/cpp_future.hpp +171 -0
- mqt/core/include/nlohmann/detail/meta/detected.hpp +70 -0
- mqt/core/include/nlohmann/detail/meta/identity_tag.hpp +21 -0
- mqt/core/include/nlohmann/detail/meta/is_sax.hpp +159 -0
- mqt/core/include/nlohmann/detail/meta/std_fs.hpp +29 -0
- mqt/core/include/nlohmann/detail/meta/type_traits.hpp +795 -0
- mqt/core/include/nlohmann/detail/meta/void_t.hpp +24 -0
- mqt/core/include/nlohmann/detail/output/binary_writer.hpp +1850 -0
- mqt/core/include/nlohmann/detail/output/output_adapters.hpp +147 -0
- mqt/core/include/nlohmann/detail/output/serializer.hpp +988 -0
- mqt/core/include/nlohmann/detail/string_concat.hpp +146 -0
- mqt/core/include/nlohmann/detail/string_escape.hpp +72 -0
- mqt/core/include/nlohmann/detail/string_utils.hpp +37 -0
- mqt/core/include/nlohmann/detail/value_t.hpp +118 -0
- mqt/core/include/nlohmann/json.hpp +5306 -0
- mqt/core/include/nlohmann/json_fwd.hpp +75 -0
- mqt/core/include/nlohmann/ordered_map.hpp +359 -0
- mqt/core/include/nlohmann/thirdparty/hedley/hedley.hpp +2045 -0
- mqt/core/include/nlohmann/thirdparty/hedley/hedley_undef.hpp +158 -0
- mqt/core/include/qdmi/qdmi/client.h +990 -0
- mqt/core/include/qdmi/qdmi/constants.h +1139 -0
- mqt/core/include/qdmi/qdmi/device.h +602 -0
- mqt/core/include/qdmi/qdmi/types.h +78 -0
- mqt/core/include/spdlog/async.h +99 -0
- mqt/core/include/spdlog/async_logger-inl.h +84 -0
- mqt/core/include/spdlog/async_logger.h +74 -0
- mqt/core/include/spdlog/cfg/argv.h +40 -0
- mqt/core/include/spdlog/cfg/env.h +36 -0
- mqt/core/include/spdlog/cfg/helpers-inl.h +107 -0
- mqt/core/include/spdlog/cfg/helpers.h +29 -0
- mqt/core/include/spdlog/common-inl.h +68 -0
- mqt/core/include/spdlog/common.h +406 -0
- mqt/core/include/spdlog/details/backtracer-inl.h +63 -0
- mqt/core/include/spdlog/details/backtracer.h +45 -0
- mqt/core/include/spdlog/details/circular_q.h +115 -0
- mqt/core/include/spdlog/details/console_globals.h +28 -0
- mqt/core/include/spdlog/details/file_helper-inl.h +153 -0
- mqt/core/include/spdlog/details/file_helper.h +61 -0
- mqt/core/include/spdlog/details/fmt_helper.h +141 -0
- mqt/core/include/spdlog/details/log_msg-inl.h +44 -0
- mqt/core/include/spdlog/details/log_msg.h +40 -0
- mqt/core/include/spdlog/details/log_msg_buffer-inl.h +54 -0
- mqt/core/include/spdlog/details/log_msg_buffer.h +32 -0
- mqt/core/include/spdlog/details/mpmc_blocking_q.h +177 -0
- mqt/core/include/spdlog/details/null_mutex.h +35 -0
- mqt/core/include/spdlog/details/os-inl.h +606 -0
- mqt/core/include/spdlog/details/os.h +127 -0
- mqt/core/include/spdlog/details/periodic_worker-inl.h +26 -0
- mqt/core/include/spdlog/details/periodic_worker.h +58 -0
- mqt/core/include/spdlog/details/registry-inl.h +270 -0
- mqt/core/include/spdlog/details/registry.h +131 -0
- mqt/core/include/spdlog/details/synchronous_factory.h +22 -0
- mqt/core/include/spdlog/details/tcp_client-windows.h +135 -0
- mqt/core/include/spdlog/details/tcp_client.h +127 -0
- mqt/core/include/spdlog/details/thread_pool-inl.h +126 -0
- mqt/core/include/spdlog/details/thread_pool.h +117 -0
- mqt/core/include/spdlog/details/udp_client-windows.h +98 -0
- mqt/core/include/spdlog/details/udp_client.h +81 -0
- mqt/core/include/spdlog/details/windows_include.h +11 -0
- mqt/core/include/spdlog/fmt/bin_to_hex.h +224 -0
- mqt/core/include/spdlog/fmt/bundled/args.h +220 -0
- mqt/core/include/spdlog/fmt/bundled/base.h +2989 -0
- mqt/core/include/spdlog/fmt/bundled/chrono.h +2330 -0
- mqt/core/include/spdlog/fmt/bundled/color.h +637 -0
- mqt/core/include/spdlog/fmt/bundled/compile.h +539 -0
- mqt/core/include/spdlog/fmt/bundled/core.h +5 -0
- mqt/core/include/spdlog/fmt/bundled/fmt.license.rst +27 -0
- mqt/core/include/spdlog/fmt/bundled/format-inl.h +1948 -0
- mqt/core/include/spdlog/fmt/bundled/format.h +4244 -0
- mqt/core/include/spdlog/fmt/bundled/os.h +427 -0
- mqt/core/include/spdlog/fmt/bundled/ostream.h +167 -0
- mqt/core/include/spdlog/fmt/bundled/printf.h +633 -0
- mqt/core/include/spdlog/fmt/bundled/ranges.h +850 -0
- mqt/core/include/spdlog/fmt/bundled/std.h +728 -0
- mqt/core/include/spdlog/fmt/bundled/xchar.h +369 -0
- mqt/core/include/spdlog/fmt/chrono.h +23 -0
- mqt/core/include/spdlog/fmt/compile.h +23 -0
- mqt/core/include/spdlog/fmt/fmt.h +30 -0
- mqt/core/include/spdlog/fmt/ostr.h +23 -0
- mqt/core/include/spdlog/fmt/ranges.h +23 -0
- mqt/core/include/spdlog/fmt/std.h +24 -0
- mqt/core/include/spdlog/fmt/xchar.h +23 -0
- mqt/core/include/spdlog/formatter.h +17 -0
- mqt/core/include/spdlog/fwd.h +18 -0
- mqt/core/include/spdlog/logger-inl.h +198 -0
- mqt/core/include/spdlog/logger.h +379 -0
- mqt/core/include/spdlog/mdc.h +52 -0
- mqt/core/include/spdlog/pattern_formatter-inl.h +1340 -0
- mqt/core/include/spdlog/pattern_formatter.h +118 -0
- mqt/core/include/spdlog/sinks/android_sink.h +137 -0
- mqt/core/include/spdlog/sinks/ansicolor_sink-inl.h +142 -0
- mqt/core/include/spdlog/sinks/ansicolor_sink.h +116 -0
- mqt/core/include/spdlog/sinks/base_sink-inl.h +59 -0
- mqt/core/include/spdlog/sinks/base_sink.h +51 -0
- mqt/core/include/spdlog/sinks/basic_file_sink-inl.h +48 -0
- mqt/core/include/spdlog/sinks/basic_file_sink.h +66 -0
- mqt/core/include/spdlog/sinks/callback_sink.h +56 -0
- mqt/core/include/spdlog/sinks/daily_file_sink.h +254 -0
- mqt/core/include/spdlog/sinks/dist_sink.h +81 -0
- mqt/core/include/spdlog/sinks/dup_filter_sink.h +91 -0
- mqt/core/include/spdlog/sinks/hourly_file_sink.h +193 -0
- mqt/core/include/spdlog/sinks/kafka_sink.h +119 -0
- mqt/core/include/spdlog/sinks/mongo_sink.h +108 -0
- mqt/core/include/spdlog/sinks/msvc_sink.h +68 -0
- mqt/core/include/spdlog/sinks/null_sink.h +41 -0
- mqt/core/include/spdlog/sinks/ostream_sink.h +43 -0
- mqt/core/include/spdlog/sinks/qt_sinks.h +304 -0
- mqt/core/include/spdlog/sinks/ringbuffer_sink.h +67 -0
- mqt/core/include/spdlog/sinks/rotating_file_sink-inl.h +179 -0
- mqt/core/include/spdlog/sinks/rotating_file_sink.h +93 -0
- mqt/core/include/spdlog/sinks/sink-inl.h +22 -0
- mqt/core/include/spdlog/sinks/sink.h +34 -0
- mqt/core/include/spdlog/sinks/stdout_color_sinks-inl.h +38 -0
- mqt/core/include/spdlog/sinks/stdout_color_sinks.h +49 -0
- mqt/core/include/spdlog/sinks/stdout_sinks-inl.h +127 -0
- mqt/core/include/spdlog/sinks/stdout_sinks.h +84 -0
- mqt/core/include/spdlog/sinks/syslog_sink.h +104 -0
- mqt/core/include/spdlog/sinks/systemd_sink.h +121 -0
- mqt/core/include/spdlog/sinks/tcp_sink.h +75 -0
- mqt/core/include/spdlog/sinks/udp_sink.h +69 -0
- mqt/core/include/spdlog/sinks/win_eventlog_sink.h +260 -0
- mqt/core/include/spdlog/sinks/wincolor_sink-inl.h +172 -0
- mqt/core/include/spdlog/sinks/wincolor_sink.h +82 -0
- mqt/core/include/spdlog/spdlog-inl.h +96 -0
- mqt/core/include/spdlog/spdlog.h +357 -0
- mqt/core/include/spdlog/stopwatch.h +66 -0
- mqt/core/include/spdlog/tweakme.h +148 -0
- mqt/core/include/spdlog/version.h +11 -0
- mqt/core/ir/__init__.pyi +2078 -0
- mqt/core/ir/operations.pyi +1011 -0
- mqt/core/ir/registers.pyi +91 -0
- mqt/core/ir/symbolic.pyi +177 -0
- mqt/core/ir.cp312-win_amd64.pyd +0 -0
- mqt/core/lib/mqt-core-algorithms.lib +0 -0
- mqt/core/lib/mqt-core-circuit-optimizer.lib +0 -0
- mqt/core/lib/mqt-core-dd.lib +0 -0
- mqt/core/lib/mqt-core-ds.lib +0 -0
- mqt/core/lib/mqt-core-fomac.lib +0 -0
- mqt/core/lib/mqt-core-ir.lib +0 -0
- mqt/core/lib/mqt-core-na-fomac.lib +0 -0
- mqt/core/lib/mqt-core-na.lib +0 -0
- mqt/core/lib/mqt-core-qasm.lib +0 -0
- mqt/core/lib/mqt-core-qdmi-driver.lib +0 -0
- mqt/core/lib/mqt-core-qdmi-na-device-gen.lib +0 -0
- mqt/core/lib/mqt-core-qdmi-na-device.lib +0 -0
- mqt/core/lib/mqt-core-zx.lib +0 -0
- mqt/core/lib/pkgconfig/spdlog.pc +13 -0
- mqt/core/lib/spdlog.lib +0 -0
- mqt/core/na/__init__.py +12 -0
- mqt/core/na/fomac.cp312-win_amd64.pyd +0 -0
- mqt/core/na/fomac.pyi +117 -0
- mqt/core/nlohmann_json.natvis +278 -0
- mqt/core/plugins/__init__.py +9 -0
- mqt/core/plugins/qiskit/__init__.py +19 -0
- mqt/core/plugins/qiskit/mqt_to_qiskit.py +420 -0
- mqt/core/plugins/qiskit/qiskit_to_mqt.py +562 -0
- mqt/core/py.typed +2 -0
- mqt/core/share/cmake/mqt-core/AddMQTPythonBinding.cmake +55 -0
- mqt/core/share/cmake/mqt-core/Cache.cmake +33 -0
- mqt/core/share/cmake/mqt-core/FindGMP.cmake +103 -0
- mqt/core/share/cmake/mqt-core/PackageAddTest.cmake +46 -0
- mqt/core/share/cmake/mqt-core/PreventInSourceBuilds.cmake +25 -0
- mqt/core/share/cmake/mqt-core/StandardProjectSettings.cmake +87 -0
- mqt/core/share/cmake/mqt-core/mqt-core-config-version.cmake +85 -0
- mqt/core/share/cmake/mqt-core/mqt-core-config.cmake +52 -0
- mqt/core/share/cmake/mqt-core/mqt-core-targets-release.cmake +141 -0
- mqt/core/share/cmake/mqt-core/mqt-core-targets.cmake +445 -0
- mqt/core/share/cmake/nlohmann_json/nlohmann_jsonConfig.cmake +15 -0
- mqt/core/share/cmake/nlohmann_json/nlohmann_jsonConfigVersion.cmake +20 -0
- mqt/core/share/cmake/nlohmann_json/nlohmann_jsonTargets.cmake +110 -0
- mqt/core/share/cmake/qdmi/Cache.cmake +44 -0
- mqt/core/share/cmake/qdmi/PrefixHandling.cmake +78 -0
- mqt/core/share/cmake/qdmi/prefix_defs.txt +26 -0
- mqt/core/share/cmake/qdmi/qdmi-config-version.cmake +85 -0
- mqt/core/share/cmake/qdmi/qdmi-config.cmake +42 -0
- mqt/core/share/cmake/qdmi/qdmi-targets.cmake +129 -0
- mqt/core/share/cmake/spdlog/spdlogConfig.cmake +44 -0
- mqt/core/share/cmake/spdlog/spdlogConfigTargets-release.cmake +19 -0
- mqt/core/share/cmake/spdlog/spdlogConfigTargets.cmake +121 -0
- mqt/core/share/cmake/spdlog/spdlogConfigVersion.cmake +65 -0
- mqt/core/share/pkgconfig/nlohmann_json.pc +7 -0
- mqt_core-3.3.2.dist-info/DELVEWHEEL +2 -0
- mqt_core-3.3.2.dist-info/METADATA +210 -0
- mqt_core-3.3.2.dist-info/RECORD +537 -0
- mqt_core-3.3.2.dist-info/WHEEL +5 -0
- mqt_core-3.3.2.dist-info/entry_points.txt +4 -0
- mqt_core-3.3.2.dist-info/licenses/LICENSE.md +22 -0
- mqt_core.libs/msvcp140.dll +0 -0
|
@@ -0,0 +1,2036 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023 - 2025 Chair for Design Automation, TUM
|
|
3
|
+
* Copyright (c) 2025 Munich Quantum Software Company GmbH
|
|
4
|
+
* All rights reserved.
|
|
5
|
+
*
|
|
6
|
+
* SPDX-License-Identifier: MIT
|
|
7
|
+
*
|
|
8
|
+
* Licensed under the MIT License
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#pragma once
|
|
12
|
+
|
|
13
|
+
#include "dd/CachedEdge.hpp"
|
|
14
|
+
#include "dd/Complex.hpp"
|
|
15
|
+
#include "dd/ComplexNumbers.hpp"
|
|
16
|
+
#include "dd/ComplexValue.hpp"
|
|
17
|
+
#include "dd/ComputeTable.hpp"
|
|
18
|
+
#include "dd/DDDefinitions.hpp"
|
|
19
|
+
#include "dd/DDpackageConfig.hpp"
|
|
20
|
+
#include "dd/DensityNoiseTable.hpp"
|
|
21
|
+
#include "dd/Edge.hpp"
|
|
22
|
+
#include "dd/MemoryManager.hpp"
|
|
23
|
+
#include "dd/Node.hpp"
|
|
24
|
+
#include "dd/Package_fwd.hpp" // IWYU pragma: export
|
|
25
|
+
#include "dd/RealNumber.hpp"
|
|
26
|
+
#include "dd/RealNumberUniqueTable.hpp"
|
|
27
|
+
#include "dd/StochasticNoiseOperationTable.hpp"
|
|
28
|
+
#include "dd/UnaryComputeTable.hpp"
|
|
29
|
+
#include "dd/UniqueTable.hpp"
|
|
30
|
+
#include "ir/Definitions.hpp"
|
|
31
|
+
#include "ir/Permutation.hpp"
|
|
32
|
+
#include "ir/operations/Control.hpp"
|
|
33
|
+
|
|
34
|
+
#include <algorithm>
|
|
35
|
+
#include <array>
|
|
36
|
+
#include <cassert>
|
|
37
|
+
#include <cmath>
|
|
38
|
+
#include <cstddef>
|
|
39
|
+
#include <cstdint>
|
|
40
|
+
#include <fstream>
|
|
41
|
+
#include <iostream>
|
|
42
|
+
#include <limits>
|
|
43
|
+
#include <random>
|
|
44
|
+
#include <regex>
|
|
45
|
+
#include <stack>
|
|
46
|
+
#include <stdexcept>
|
|
47
|
+
#include <string>
|
|
48
|
+
#include <type_traits>
|
|
49
|
+
#include <unordered_map>
|
|
50
|
+
#include <unordered_set>
|
|
51
|
+
#include <utility>
|
|
52
|
+
#include <vector>
|
|
53
|
+
|
|
54
|
+
namespace dd {
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @brief The DD package class
|
|
58
|
+
*
|
|
59
|
+
* @details This is the main class of the decision diagram module in MQT Core.
|
|
60
|
+
* It contains the core functionality for working with quantum decision
|
|
61
|
+
* diagrams. Specifically, it provides the means to
|
|
62
|
+
* - represent quantum states as decision diagrams,
|
|
63
|
+
* - represent quantum operations as decision diagrams,
|
|
64
|
+
* - multiply decision diagrams (MxV, MxM, etc.),
|
|
65
|
+
* - perform collapsing measurements on decision diagrams,
|
|
66
|
+
* - sample from decision diagrams.
|
|
67
|
+
*
|
|
68
|
+
* To this end, it maintains several internal data structures, such as unique
|
|
69
|
+
* tables, compute tables, and memory managers, which are used to manage the
|
|
70
|
+
* nodes of the decision diagrams.
|
|
71
|
+
*/
|
|
72
|
+
class Package {
|
|
73
|
+
|
|
74
|
+
///
|
|
75
|
+
/// Construction, destruction, information, and reset
|
|
76
|
+
///
|
|
77
|
+
public:
|
|
78
|
+
static constexpr std::size_t MAX_POSSIBLE_QUBITS =
|
|
79
|
+
static_cast<std::size_t>(std::numeric_limits<Qubit>::max()) + 1U;
|
|
80
|
+
static constexpr std::size_t DEFAULT_QUBITS = 32U;
|
|
81
|
+
/**
|
|
82
|
+
* @brief Construct a new DD Package instance
|
|
83
|
+
*
|
|
84
|
+
* @param nq The maximum number of qubits to allocate memory for. This can
|
|
85
|
+
* always be extended later using @ref resize.
|
|
86
|
+
* @param config The configuration of the package
|
|
87
|
+
*/
|
|
88
|
+
explicit Package(std::size_t nq = DEFAULT_QUBITS,
|
|
89
|
+
const DDPackageConfig& config = DDPackageConfig{});
|
|
90
|
+
~Package() = default;
|
|
91
|
+
Package(const Package& package) = delete;
|
|
92
|
+
|
|
93
|
+
Package& operator=(const Package& package) = delete;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @brief Resize the package to a new number of qubits
|
|
97
|
+
*
|
|
98
|
+
* @details This method will resize all the unique tables appropriately so
|
|
99
|
+
* that they can handle the new number of qubits.
|
|
100
|
+
*
|
|
101
|
+
* @param nq The new number of qubits
|
|
102
|
+
*/
|
|
103
|
+
void resize(std::size_t nq);
|
|
104
|
+
|
|
105
|
+
/// Reset package state
|
|
106
|
+
void reset();
|
|
107
|
+
|
|
108
|
+
/// Get the number of qubits
|
|
109
|
+
[[nodiscard]] auto qubits() const { return nqubits; }
|
|
110
|
+
|
|
111
|
+
private:
|
|
112
|
+
std::size_t nqubits;
|
|
113
|
+
DDPackageConfig config_;
|
|
114
|
+
|
|
115
|
+
public:
|
|
116
|
+
/// The memory manager for vector nodes
|
|
117
|
+
MemoryManager vMemoryManager{
|
|
118
|
+
MemoryManager::create<vNode>(config_.utVecInitialAllocationSize)};
|
|
119
|
+
/// The memory manager for matrix nodes
|
|
120
|
+
MemoryManager mMemoryManager{
|
|
121
|
+
MemoryManager::create<mNode>(config_.utMatInitialAllocationSize)};
|
|
122
|
+
/// The memory manager for density matrix nodes
|
|
123
|
+
MemoryManager dMemoryManager{
|
|
124
|
+
MemoryManager::create<dNode>(config_.utDmInitialAllocationSize)};
|
|
125
|
+
/**
|
|
126
|
+
* @brief The memory manager for complex numbers
|
|
127
|
+
* @note The real and imaginary part of complex numbers are treated
|
|
128
|
+
* separately. Hence, it suffices for the manager to only manage real numbers.
|
|
129
|
+
*/
|
|
130
|
+
MemoryManager cMemoryManager{MemoryManager::create<RealNumber>()};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @brief Get the memory manager for a given type
|
|
134
|
+
* @tparam T The type to get the manager for
|
|
135
|
+
* @return A reference to the manager
|
|
136
|
+
*/
|
|
137
|
+
template <class T> [[nodiscard]] auto& getMemoryManager() {
|
|
138
|
+
if constexpr (std::is_same_v<T, vNode>) {
|
|
139
|
+
return vMemoryManager;
|
|
140
|
+
} else if constexpr (std::is_same_v<T, mNode>) {
|
|
141
|
+
return mMemoryManager;
|
|
142
|
+
} else if constexpr (std::is_same_v<T, dNode>) {
|
|
143
|
+
return dMemoryManager;
|
|
144
|
+
} else if constexpr (std::is_same_v<T, RealNumber>) {
|
|
145
|
+
return cMemoryManager;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @brief Reset all memory managers
|
|
151
|
+
* @arg resizeToTotal If set to true, each manager allocates one chunk of
|
|
152
|
+
* memory as large as all chunks combined before the reset.
|
|
153
|
+
* @see MemoryManager::reset
|
|
154
|
+
*/
|
|
155
|
+
void resetMemoryManagers(bool resizeToTotal = false);
|
|
156
|
+
|
|
157
|
+
/// The unique table used for vector nodes
|
|
158
|
+
UniqueTable vUniqueTable{vMemoryManager, {0U, config_.utVecNumBucket}};
|
|
159
|
+
/// The unique table used for matrix nodes
|
|
160
|
+
UniqueTable mUniqueTable{mMemoryManager, {0U, config_.utMatNumBucket}};
|
|
161
|
+
/// The unique table used for density matrix nodes
|
|
162
|
+
UniqueTable dUniqueTable{dMemoryManager, {0U, config_.utDmNumBucket}};
|
|
163
|
+
/**
|
|
164
|
+
* @brief The unique table used for complex numbers
|
|
165
|
+
* @note The table actually only stores real numbers in the interval [0, 1],
|
|
166
|
+
* but is used to manages all complex numbers throughout the package.
|
|
167
|
+
* @see RealNumberUniqueTable
|
|
168
|
+
*/
|
|
169
|
+
RealNumberUniqueTable cUniqueTable{cMemoryManager};
|
|
170
|
+
ComplexNumbers cn{cUniqueTable};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @brief Get the unique table for a given type
|
|
174
|
+
* @tparam T The type to get the unique table for
|
|
175
|
+
* @return A reference to the unique table
|
|
176
|
+
*/
|
|
177
|
+
template <class T> [[nodiscard]] auto& getUniqueTable() {
|
|
178
|
+
if constexpr (std::is_same_v<T, vNode>) {
|
|
179
|
+
return vUniqueTable;
|
|
180
|
+
} else if constexpr (std::is_same_v<T, mNode>) {
|
|
181
|
+
return mUniqueTable;
|
|
182
|
+
} else if constexpr (std::is_same_v<T, dNode>) {
|
|
183
|
+
return dUniqueTable;
|
|
184
|
+
} else if constexpr (std::is_same_v<T, RealNumber>) {
|
|
185
|
+
return cUniqueTable;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @brief Clear all unique tables
|
|
191
|
+
* @see UniqueTable::clear
|
|
192
|
+
* @see RealNumberUniqueTable::clear
|
|
193
|
+
*/
|
|
194
|
+
void clearUniqueTables();
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @brief Add the DD to a tracking hashset and update its reference count.
|
|
198
|
+
* @tparam Node The node type of the edge.
|
|
199
|
+
* @param e The edge to increase the reference count of.
|
|
200
|
+
*/
|
|
201
|
+
template <class Node> void incRef(const Edge<Node>& e) noexcept {
|
|
202
|
+
if (Edge<Node>::trackingRequired(e)) {
|
|
203
|
+
roots.addToRoots(e);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* @brief Decrease the DD's reference count and remove it from the tracking
|
|
209
|
+
* hashset if the count hits zero.
|
|
210
|
+
* @tparam Node The node type of the edge.
|
|
211
|
+
* @param e The edge to decrease the reference count of.
|
|
212
|
+
* @throws std::invalid_argument If the edge is not part of the tracking
|
|
213
|
+
* hashset.
|
|
214
|
+
*/
|
|
215
|
+
template <class Node> void decRef(const Edge<Node>& e) {
|
|
216
|
+
if (Edge<Node>::trackingRequired(e)) {
|
|
217
|
+
roots.removeFromRoots(e);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
template <class Node> [[nodiscard]] auto& getRootSet() noexcept {
|
|
222
|
+
return roots.getRoots<Node>();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private:
|
|
226
|
+
struct RootSetManager {
|
|
227
|
+
public:
|
|
228
|
+
template <class Node>
|
|
229
|
+
using RootSet = std::unordered_map<Edge<Node>, std::size_t>;
|
|
230
|
+
|
|
231
|
+
/// @brief Add to respective root set.
|
|
232
|
+
template <class Node> void addToRoots(const Edge<Node>& e) noexcept {
|
|
233
|
+
getRoots<Node>()[e]++;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/// @brief Remove from respective root set.
|
|
237
|
+
template <class Node> void removeFromRoots(const Edge<Node>& e) {
|
|
238
|
+
auto& set = getRoots<Node>();
|
|
239
|
+
auto it = set.find(e);
|
|
240
|
+
if (it == set.end()) {
|
|
241
|
+
throw std::invalid_argument("Edge is not part of the root set.");
|
|
242
|
+
}
|
|
243
|
+
if (--it->second == 0U) {
|
|
244
|
+
set.erase(it);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/// @brief Execute mark() -> op() -> unmark().
|
|
249
|
+
template <class Result, typename Fn> Result execute(Fn& op) noexcept {
|
|
250
|
+
mark();
|
|
251
|
+
Result res = op();
|
|
252
|
+
unmark();
|
|
253
|
+
return res;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/// @brief Clear all root sets.
|
|
257
|
+
void reset() {
|
|
258
|
+
vRoots.clear();
|
|
259
|
+
mRoots.clear();
|
|
260
|
+
dRoots.clear();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private:
|
|
264
|
+
/// @brief Mark edges contained in @p roots.
|
|
265
|
+
template <class Node> static void mark(const RootSet<Node>& roots) {
|
|
266
|
+
for (auto& [edge, _] : roots) {
|
|
267
|
+
edge.mark();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/// @brief Unmark edges contained in @p roots.
|
|
272
|
+
template <class Node> static void unmark(const RootSet<Node>& roots) {
|
|
273
|
+
for (auto& [edge, _] : roots) {
|
|
274
|
+
edge.unmark();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/// @brief Mark edges contained in all root sets.
|
|
279
|
+
void mark() noexcept {
|
|
280
|
+
RootSetManager::mark(vRoots);
|
|
281
|
+
RootSetManager::mark(mRoots);
|
|
282
|
+
RootSetManager::mark(dRoots);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/// @brief Unmark edges contained in all root sets.
|
|
286
|
+
void unmark() noexcept {
|
|
287
|
+
RootSetManager::unmark(vRoots);
|
|
288
|
+
RootSetManager::unmark(mRoots);
|
|
289
|
+
RootSetManager::unmark(dRoots);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/// @brief Return vector roots.
|
|
293
|
+
template <class Node,
|
|
294
|
+
std::enable_if_t<std::is_same_v<Node, vNode>, bool> = true>
|
|
295
|
+
auto& getRoots() noexcept {
|
|
296
|
+
return vRoots;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/// @brief Return matrix roots.
|
|
300
|
+
template <class Node,
|
|
301
|
+
std::enable_if_t<std::is_same_v<Node, mNode>, bool> = true>
|
|
302
|
+
auto& getRoots() noexcept {
|
|
303
|
+
return mRoots;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/// @brief Return density roots.
|
|
307
|
+
template <class Node,
|
|
308
|
+
std::enable_if_t<std::is_same_v<Node, dNode>, bool> = true>
|
|
309
|
+
auto& getRoots() noexcept {
|
|
310
|
+
return dRoots;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
RootSet<vNode> vRoots;
|
|
314
|
+
RootSet<mNode> mRoots;
|
|
315
|
+
RootSet<dNode> dRoots;
|
|
316
|
+
|
|
317
|
+
template <class Node> friend auto& Package::getRootSet() noexcept;
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
RootSetManager roots;
|
|
321
|
+
|
|
322
|
+
public:
|
|
323
|
+
/**
|
|
324
|
+
* @brief Trigger garbage collection on all unique tables.
|
|
325
|
+
* @details Mark-and-sweep algorithm: First, mark all nodes and complex
|
|
326
|
+
* numbers tracked in @p roots. Second, remove any unmarked nodes and numbers
|
|
327
|
+
* from the respective unique tables. Lastly, unmark all nodes and complex
|
|
328
|
+
* numbers again.
|
|
329
|
+
* @note By default, garbage collection is only triggered if the unique tables
|
|
330
|
+
* report that a collection might be necessary.
|
|
331
|
+
*
|
|
332
|
+
* @param force Force garbage collect, regardless of whether any
|
|
333
|
+
* table reports that it may need collecting.
|
|
334
|
+
* @returns Whether at least one vector, matrix, density-matrix node, or
|
|
335
|
+
* any complex number was reclaimed.
|
|
336
|
+
*/
|
|
337
|
+
bool garbageCollect(bool force = false);
|
|
338
|
+
|
|
339
|
+
struct ActiveCounts {
|
|
340
|
+
std::size_t vector = 0U;
|
|
341
|
+
std::size_t matrix = 0U;
|
|
342
|
+
std::size_t density = 0U;
|
|
343
|
+
std::size_t reals = 0U;
|
|
344
|
+
};
|
|
345
|
+
/**
|
|
346
|
+
* @brief Compute the active number of nodes and numbers
|
|
347
|
+
* @note This traverses every currently tracked DD twice.
|
|
348
|
+
*/
|
|
349
|
+
[[nodiscard]] ActiveCounts computeActiveCounts();
|
|
350
|
+
|
|
351
|
+
///
|
|
352
|
+
/// Vector nodes, edges and quantum states
|
|
353
|
+
///
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* @brief Construct the all-zero density operator
|
|
357
|
+
\f$|0...0\rangle\langle0...0|\f$
|
|
358
|
+
* @param n The number of qubits
|
|
359
|
+
* @return A decision diagram for the all-zero density operator
|
|
360
|
+
*/
|
|
361
|
+
dEdge makeZeroDensityOperator(std::size_t n);
|
|
362
|
+
|
|
363
|
+
///
|
|
364
|
+
/// Matrix nodes, edges and quantum gates
|
|
365
|
+
///
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* @brief Construct the DD for a single-qubit gate
|
|
369
|
+
* @param mat The matrix representation of the gate
|
|
370
|
+
* @param target The target qubit
|
|
371
|
+
* @return A decision diagram for the gate
|
|
372
|
+
*/
|
|
373
|
+
mEdge makeGateDD(const GateMatrix& mat, qc::Qubit target);
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* @brief Construct the DD for a single-qubit controlled gate
|
|
377
|
+
* @param mat The matrix representation of the gate
|
|
378
|
+
* @param control The control qubit
|
|
379
|
+
* @param target The target qubit
|
|
380
|
+
* @return A decision diagram for the gate
|
|
381
|
+
*/
|
|
382
|
+
mEdge makeGateDD(const GateMatrix& mat, const qc::Control& control,
|
|
383
|
+
qc::Qubit target);
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* @brief Construct the DD for a multi-controlled single-qubit gate
|
|
387
|
+
* @param mat The matrix representation of the gate
|
|
388
|
+
* @param controls The control qubits
|
|
389
|
+
* @param target The target qubit
|
|
390
|
+
* @return A decision diagram for the gate
|
|
391
|
+
*/
|
|
392
|
+
mEdge makeGateDD(const GateMatrix& mat, const qc::Controls& controls,
|
|
393
|
+
qc::Qubit target);
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* @brief Creates the DD for a two-qubit gate
|
|
397
|
+
* @param mat Matrix representation of the gate
|
|
398
|
+
* @param target0 First target qubit
|
|
399
|
+
* @param target1 Second target qubit
|
|
400
|
+
* @return DD representing the gate
|
|
401
|
+
* @throws std::runtime_error if the number of qubits is larger than the
|
|
402
|
+
* package configuration
|
|
403
|
+
*/
|
|
404
|
+
mEdge makeTwoQubitGateDD(const TwoQubitGateMatrix& mat, qc::Qubit target0,
|
|
405
|
+
qc::Qubit target1);
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* @brief Creates the DD for a two-qubit gate
|
|
409
|
+
* @param mat Matrix representation of the gate
|
|
410
|
+
* @param control Control qubit of the two-qubit gate
|
|
411
|
+
* @param target0 First target qubit
|
|
412
|
+
* @param target1 Second target qubit
|
|
413
|
+
* @return DD representing the gate
|
|
414
|
+
* @throws std::runtime_error if the number of qubits is larger than the
|
|
415
|
+
* package configuration
|
|
416
|
+
*/
|
|
417
|
+
mEdge makeTwoQubitGateDD(const TwoQubitGateMatrix& mat,
|
|
418
|
+
const qc::Control& control, qc::Qubit target0,
|
|
419
|
+
qc::Qubit target1);
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* @brief Creates the DD for a two-qubit gate
|
|
423
|
+
* @param mat Matrix representation of the gate
|
|
424
|
+
* @param controls Control qubits of the two-qubit gate
|
|
425
|
+
* @param target0 First target qubit
|
|
426
|
+
* @param target1 Second target qubit
|
|
427
|
+
* @return DD representing the gate
|
|
428
|
+
* @throws std::runtime_error if the number of qubits is larger than the
|
|
429
|
+
* package configuration
|
|
430
|
+
*/
|
|
431
|
+
mEdge makeTwoQubitGateDD(const TwoQubitGateMatrix& mat,
|
|
432
|
+
const qc::Controls& controls, qc::Qubit target0,
|
|
433
|
+
qc::Qubit target1);
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* @brief Converts a given matrix to a decision diagram
|
|
437
|
+
* @param matrix A complex matrix to convert to a DD.
|
|
438
|
+
* @return A decision diagram representing the matrix.
|
|
439
|
+
* @throws std::invalid_argument If the given matrix is not square or its
|
|
440
|
+
* length is not a power of two.
|
|
441
|
+
*/
|
|
442
|
+
mEdge makeDDFromMatrix(const CMat& matrix);
|
|
443
|
+
|
|
444
|
+
private:
|
|
445
|
+
/**
|
|
446
|
+
* @brief Constructs a decision diagram (DD) from a complex matrix using a
|
|
447
|
+
* recursive algorithm.
|
|
448
|
+
*
|
|
449
|
+
* @param matrix The complex matrix from which to create the DD.
|
|
450
|
+
* @param level The current level of recursion. Starts at the highest level of
|
|
451
|
+
* the matrix (log base 2 of the matrix size - 1).
|
|
452
|
+
* @param rowStart The starting row of the quadrant being processed.
|
|
453
|
+
* @param rowEnd The ending row of the quadrant being processed.
|
|
454
|
+
* @param colStart The starting column of the quadrant being processed.
|
|
455
|
+
* @param colEnd The ending column of the quadrant being processed.
|
|
456
|
+
* @return An mCachedEdge representing the root node of the created DD.
|
|
457
|
+
*
|
|
458
|
+
* @details This function recursively breaks down the matrix into quadrants
|
|
459
|
+
* until each quadrant has only one element. At each level of recursion, four
|
|
460
|
+
* new edges are created, one for each quadrant of the matrix. The four
|
|
461
|
+
* resulting decision diagram edges are used to create a new decision diagram
|
|
462
|
+
* node at the current level, and this node is returned as the result of the
|
|
463
|
+
* current recursive call. At the base case of recursion, the matrix has only
|
|
464
|
+
* one element, which is converted into a terminal node of the decision
|
|
465
|
+
* diagram.
|
|
466
|
+
*
|
|
467
|
+
* @note This function assumes that the matrix size is a power of two.
|
|
468
|
+
*/
|
|
469
|
+
mCachedEdge makeDDFromMatrix(const CMat& matrix, Qubit level,
|
|
470
|
+
std::size_t rowStart, std::size_t rowEnd,
|
|
471
|
+
std::size_t colStart, std::size_t colEnd);
|
|
472
|
+
|
|
473
|
+
public:
|
|
474
|
+
/**
|
|
475
|
+
* @brief Create a normalized DD node and return an edge pointing to it.
|
|
476
|
+
*
|
|
477
|
+
* @details The node is not recreated if it already exists. This function
|
|
478
|
+
* retrieves a node from the memory manager, sets its variable, and normalizes
|
|
479
|
+
* the edges. If the node resembles the identity, it is skipped. The function
|
|
480
|
+
* then looks up the node in the unique table and returns an edge pointing to
|
|
481
|
+
* it.
|
|
482
|
+
*
|
|
483
|
+
* @tparam Node The type of the node.
|
|
484
|
+
* @tparam EdgeType The type of the edge.
|
|
485
|
+
* @param var The variable associated with the node.
|
|
486
|
+
* @param edges The edges of the node.
|
|
487
|
+
* @param generateDensityMatrix Flag to indicate if a density matrix node
|
|
488
|
+
* should be generated.
|
|
489
|
+
* @return An edge pointing to the normalized DD node.
|
|
490
|
+
*/
|
|
491
|
+
template <class Node, template <class> class EdgeType>
|
|
492
|
+
EdgeType<Node>
|
|
493
|
+
makeDDNode(const Qubit var,
|
|
494
|
+
const std::array<EdgeType<Node>,
|
|
495
|
+
std::tuple_size_v<decltype(Node::e)>>& edges,
|
|
496
|
+
[[maybe_unused]] const bool generateDensityMatrix = false) {
|
|
497
|
+
auto& memoryManager = getMemoryManager<Node>();
|
|
498
|
+
auto p = memoryManager.template get<Node>();
|
|
499
|
+
|
|
500
|
+
p->v = var;
|
|
501
|
+
if constexpr (std::is_same_v<Node, mNode> || std::is_same_v<Node, dNode>) {
|
|
502
|
+
p->flags = 0;
|
|
503
|
+
if constexpr (std::is_same_v<Node, dNode>) {
|
|
504
|
+
p->setDensityMatrixNodeFlag(generateDensityMatrix);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
auto e = EdgeType<Node>::normalize(p, edges, memoryManager, cn);
|
|
509
|
+
if constexpr (std::is_same_v<Node, mNode> || std::is_same_v<Node, dNode>) {
|
|
510
|
+
if (!e.isTerminal()) {
|
|
511
|
+
const auto& es = e.p->e;
|
|
512
|
+
// Check if node resembles the identity. If so, skip it.
|
|
513
|
+
if ((es[0].p == es[3].p) &&
|
|
514
|
+
(es[0].w.exactlyOne() && es[1].w.exactlyZero() &&
|
|
515
|
+
es[2].w.exactlyZero() && es[3].w.exactlyOne())) {
|
|
516
|
+
auto* ptr = es[0].p;
|
|
517
|
+
memoryManager.returnEntry(*e.p);
|
|
518
|
+
return EdgeType<Node>{ptr, e.w};
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// look it up in the unique tables
|
|
524
|
+
auto& uniqueTable = getUniqueTable<Node>();
|
|
525
|
+
auto* l = uniqueTable.lookup(e.p);
|
|
526
|
+
|
|
527
|
+
return EdgeType<Node>{l, e.w};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* @brief Delete an edge from the decision diagram.
|
|
532
|
+
*
|
|
533
|
+
* @tparam Node The type of the node.
|
|
534
|
+
* @param e The edge to delete.
|
|
535
|
+
* @param v The variable associated with the edge.
|
|
536
|
+
* @param edgeIdx The index of the edge to delete.
|
|
537
|
+
* @return The modified edge after deletion.
|
|
538
|
+
*/
|
|
539
|
+
template <class Node>
|
|
540
|
+
Edge<Node> deleteEdge(const Edge<Node>& e, const Qubit v,
|
|
541
|
+
const std::size_t edgeIdx) {
|
|
542
|
+
std::unordered_map<Node*, Edge<Node>> nodes{};
|
|
543
|
+
return deleteEdge(e, v, edgeIdx, nodes);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* @brief Helper function to delete an edge from the decision diagram.
|
|
548
|
+
*
|
|
549
|
+
* @tparam Node The type of the node.
|
|
550
|
+
* @param e The edge to delete.
|
|
551
|
+
* @param v The variable associated with the edge.
|
|
552
|
+
* @param edgeIdx The index of the edge to delete.
|
|
553
|
+
* @param nodes A map to keep track of processed nodes.
|
|
554
|
+
* @return The modified edge after deletion.
|
|
555
|
+
*/
|
|
556
|
+
template <class Node>
|
|
557
|
+
Edge<Node> deleteEdge(const Edge<Node>& e, const Qubit v,
|
|
558
|
+
const std::size_t edgeIdx,
|
|
559
|
+
std::unordered_map<Node*, Edge<Node>>& nodes) {
|
|
560
|
+
if (e.isTerminal()) {
|
|
561
|
+
return e;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const auto& nodeIt = nodes.find(e.p);
|
|
565
|
+
Edge<Node> r{};
|
|
566
|
+
if (nodeIt != nodes.end()) {
|
|
567
|
+
r = nodeIt->second;
|
|
568
|
+
} else {
|
|
569
|
+
constexpr std::size_t n = std::tuple_size_v<decltype(e.p->e)>;
|
|
570
|
+
std::array<Edge<Node>, n> edges{};
|
|
571
|
+
if (e.p->v == v) {
|
|
572
|
+
for (std::size_t i = 0; i < n; i++) {
|
|
573
|
+
edges[i] = i == edgeIdx
|
|
574
|
+
? Edge<Node>::zero()
|
|
575
|
+
: e.p->e[i]; // optimization -> node cannot occur below
|
|
576
|
+
// again, since dd is assumed to be free
|
|
577
|
+
}
|
|
578
|
+
} else {
|
|
579
|
+
for (std::size_t i = 0; i < n; i++) {
|
|
580
|
+
edges[i] = deleteEdge(e.p->e[i], v, edgeIdx, nodes);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
r = makeDDNode(e.p->v, edges);
|
|
585
|
+
nodes[e.p] = r;
|
|
586
|
+
}
|
|
587
|
+
r.w = cn.lookup(r.w * e.w);
|
|
588
|
+
return r;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
///
|
|
592
|
+
/// Compute table definitions
|
|
593
|
+
///
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* @brief Clear all compute tables.
|
|
597
|
+
*
|
|
598
|
+
* @details This method clears all entries in the compute tables used for
|
|
599
|
+
* various operations. It resets the state of the compute tables, making them
|
|
600
|
+
* ready for new computations.
|
|
601
|
+
*/
|
|
602
|
+
void clearComputeTables();
|
|
603
|
+
|
|
604
|
+
///
|
|
605
|
+
/// Measurements from state decision diagrams
|
|
606
|
+
///
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* @brief Measure all qubits in the given decision diagram.
|
|
610
|
+
*
|
|
611
|
+
* @details This function measures all qubits in the decision diagram
|
|
612
|
+
* represented by `rootEdge`. It checks for numerical instabilities and
|
|
613
|
+
* collapses the state if requested.
|
|
614
|
+
*
|
|
615
|
+
* @param rootEdge The decision diagram to measure.
|
|
616
|
+
* @param collapse If true, the state is collapsed after measurement.
|
|
617
|
+
* @param mt A random number generator.
|
|
618
|
+
* @param epsilon The tolerance for numerical instabilities.
|
|
619
|
+
* @return A string representing the measurement result.
|
|
620
|
+
* @throws std::runtime_error If numerical instabilities are detected or if
|
|
621
|
+
* probabilities do not sum to 1.
|
|
622
|
+
*/
|
|
623
|
+
std::string measureAll(vEdge& rootEdge, bool collapse, std::mt19937_64& mt,
|
|
624
|
+
fp epsilon = 0.001);
|
|
625
|
+
|
|
626
|
+
private:
|
|
627
|
+
/**
|
|
628
|
+
* @brief Assigns probabilities to nodes in a decision diagram.
|
|
629
|
+
*
|
|
630
|
+
* @details This function recursively assigns probabilities to nodes in a
|
|
631
|
+
* decision diagram. It calculates the probability of reaching each node and
|
|
632
|
+
* stores the result in a map.
|
|
633
|
+
*
|
|
634
|
+
* @param edge The edge to start the probability assignment from.
|
|
635
|
+
* @param probs A map to store the probabilities of each node.
|
|
636
|
+
* @return The probability of the given edge.
|
|
637
|
+
*/
|
|
638
|
+
static fp assignProbabilities(const vEdge& edge,
|
|
639
|
+
std::unordered_map<const vNode*, fp>& probs);
|
|
640
|
+
|
|
641
|
+
public:
|
|
642
|
+
/**
|
|
643
|
+
* @brief Determine the measurement probabilities for a given qubit index.
|
|
644
|
+
*
|
|
645
|
+
* @param rootEdge The root edge of the decision diagram.
|
|
646
|
+
* @param index The qubit index to determine the measurement probabilities
|
|
647
|
+
* for.
|
|
648
|
+
* @return A pair of floating-point values representing the probabilities of
|
|
649
|
+
* measuring 0 and 1, respectively.
|
|
650
|
+
*
|
|
651
|
+
* @details This function calculates the probabilities of measuring 0 and 1
|
|
652
|
+
* for a given qubit index in the decision diagram. It uses a breadth-first
|
|
653
|
+
* search to traverse the decision diagram and accumulate the measurement
|
|
654
|
+
* probabilities. The function maintains a map of measurement probabilities
|
|
655
|
+
* for each node and a set of visited nodes to avoid redundant calculations.
|
|
656
|
+
* It also uses a queue to process nodes level by level.
|
|
657
|
+
*/
|
|
658
|
+
static std::pair<fp, fp>
|
|
659
|
+
determineMeasurementProbabilities(const vEdge& rootEdge, Qubit index);
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* @brief Measures the qubit with the given index in the given state vector
|
|
663
|
+
* decision diagram. Collapses the state according to the measurement result.
|
|
664
|
+
* @param rootEdge the root edge of the state vector decision diagram
|
|
665
|
+
* @param index the index of the qubit to be measured
|
|
666
|
+
* @param mt the random number generator
|
|
667
|
+
* @param epsilon the numerical precision used for checking the normalization
|
|
668
|
+
* of the state vector decision diagram
|
|
669
|
+
* @return the measurement result ('0' or '1')
|
|
670
|
+
* @throws std::runtime_error if a numerical instability is detected during
|
|
671
|
+
* the measurement.
|
|
672
|
+
*/
|
|
673
|
+
char measureOneCollapsing(vEdge& rootEdge, Qubit index, std::mt19937_64& mt,
|
|
674
|
+
fp epsilon = 0.001);
|
|
675
|
+
|
|
676
|
+
char measureOneCollapsing(dEdge& e, Qubit index, std::mt19937_64& mt);
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* @brief Performs a specific measurement on the given state vector decision
|
|
680
|
+
* diagram. Collapses the state according to the measurement result.
|
|
681
|
+
* @param rootEdge the root edge of the state vector decision diagram
|
|
682
|
+
* @param index the index of the qubit to be measured
|
|
683
|
+
* @param probability the probability of the measurement result (required for
|
|
684
|
+
* normalization)
|
|
685
|
+
* @param measureZero whether or not to measure '0' (otherwise '1' is
|
|
686
|
+
* measured)
|
|
687
|
+
*/
|
|
688
|
+
void performCollapsingMeasurement(vEdge& rootEdge, Qubit index,
|
|
689
|
+
fp probability, bool measureZero);
|
|
690
|
+
|
|
691
|
+
///
|
|
692
|
+
/// Addition
|
|
693
|
+
///
|
|
694
|
+
ComputeTable<vCachedEdge, vCachedEdge, vCachedEdge> vectorAdd{
|
|
695
|
+
config_.ctVecAddNumBucket};
|
|
696
|
+
ComputeTable<mCachedEdge, mCachedEdge, mCachedEdge> matrixAdd{
|
|
697
|
+
config_.ctMatAddNumBucket};
|
|
698
|
+
ComputeTable<dCachedEdge, dCachedEdge, dCachedEdge> densityAdd{
|
|
699
|
+
config_.ctDmAddNumBucket};
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* @brief Get the compute table for addition operations.
|
|
703
|
+
*
|
|
704
|
+
* @tparam Node The type of the node.
|
|
705
|
+
* @return A reference to the appropriate compute table for the given node
|
|
706
|
+
* type.
|
|
707
|
+
*/
|
|
708
|
+
template <class Node> [[nodiscard]] auto& getAddComputeTable() {
|
|
709
|
+
if constexpr (std::is_same_v<Node, vNode>) {
|
|
710
|
+
return vectorAdd;
|
|
711
|
+
} else if constexpr (std::is_same_v<Node, mNode>) {
|
|
712
|
+
return matrixAdd;
|
|
713
|
+
} else if constexpr (std::is_same_v<Node, dNode>) {
|
|
714
|
+
return densityAdd;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
ComputeTable<vCachedEdge, vCachedEdge, vCachedEdge> vectorAddMagnitudes{
|
|
719
|
+
config_.ctVecAddMagNumBucket};
|
|
720
|
+
ComputeTable<mCachedEdge, mCachedEdge, mCachedEdge> matrixAddMagnitudes{
|
|
721
|
+
config_.ctMatAddMagNumBucket};
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* @brief Get the compute table for addition operations with magnitudes.
|
|
725
|
+
*
|
|
726
|
+
* @tparam Node The type of the node.
|
|
727
|
+
* @return A reference to the appropriate compute table for the given node
|
|
728
|
+
* type.
|
|
729
|
+
*/
|
|
730
|
+
template <class Node> [[nodiscard]] auto& getAddMagnitudesComputeTable() {
|
|
731
|
+
if constexpr (std::is_same_v<Node, vNode>) {
|
|
732
|
+
return vectorAddMagnitudes;
|
|
733
|
+
} else if constexpr (std::is_same_v<Node, mNode>) {
|
|
734
|
+
return matrixAddMagnitudes;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* @brief Add two decision diagrams.
|
|
740
|
+
*
|
|
741
|
+
* @tparam Node The type of the node.
|
|
742
|
+
* @param x The first DD.
|
|
743
|
+
* @param y The second DD.
|
|
744
|
+
* @return The resulting DD after addition.
|
|
745
|
+
*
|
|
746
|
+
* @details This function performs the addition of two decision diagrams
|
|
747
|
+
* (DDs). It uses a compute table to cache intermediate results and avoid
|
|
748
|
+
* redundant computations. The addition is conducted recursively, where the
|
|
749
|
+
* function traverses the nodes of the DDs, adds corresponding edges, and
|
|
750
|
+
* normalizes the resulting edges. If the nodes are terminal, their weights
|
|
751
|
+
* are directly added. The function ensures that the resulting DD is properly
|
|
752
|
+
* normalized and stored in the unique table to maintain the canonical form.
|
|
753
|
+
*/
|
|
754
|
+
template <class Node>
|
|
755
|
+
Edge<Node> add(const Edge<Node>& x, const Edge<Node>& y) {
|
|
756
|
+
Qubit var{};
|
|
757
|
+
if (!x.isTerminal()) {
|
|
758
|
+
var = x.p->v;
|
|
759
|
+
}
|
|
760
|
+
if (!y.isTerminal() && (y.p->v) > var) {
|
|
761
|
+
var = y.p->v;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
const auto result = add2(CachedEdge{x.p, x.w}, {y.p, y.w}, var);
|
|
765
|
+
return cn.lookup(result);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* @brief Internal function to add two decision diagrams.
|
|
770
|
+
*
|
|
771
|
+
* This function is used internally to add two decision diagrams (DDs) of type
|
|
772
|
+
* Node. It is not intended to be called directly.
|
|
773
|
+
*
|
|
774
|
+
* @tparam Node The type of the node.
|
|
775
|
+
* @param x The first DD.
|
|
776
|
+
* @param y The second DD.
|
|
777
|
+
* @param var The variable associated with the current level of recursion.
|
|
778
|
+
* @return The resulting DD after addition.
|
|
779
|
+
*/
|
|
780
|
+
template <class Node>
|
|
781
|
+
CachedEdge<Node> add2(const CachedEdge<Node>& x, const CachedEdge<Node>& y,
|
|
782
|
+
const Qubit var) {
|
|
783
|
+
if (x.w.exactlyZero()) {
|
|
784
|
+
if (y.w.exactlyZero()) {
|
|
785
|
+
return CachedEdge<Node>::zero();
|
|
786
|
+
}
|
|
787
|
+
return y;
|
|
788
|
+
}
|
|
789
|
+
if (y.w.exactlyZero()) {
|
|
790
|
+
return x;
|
|
791
|
+
}
|
|
792
|
+
if (x.p == y.p) {
|
|
793
|
+
const auto rWeight = x.w + y.w;
|
|
794
|
+
return {x.p, rWeight};
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
auto& computeTable = getAddComputeTable<Node>();
|
|
798
|
+
if (const auto* r = computeTable.lookup(x, y); r != nullptr) {
|
|
799
|
+
return *r;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
constexpr std::size_t n = std::tuple_size_v<decltype(x.p->e)>;
|
|
803
|
+
std::array<CachedEdge<Node>, n> edge{};
|
|
804
|
+
for (std::size_t i = 0U; i < n; i++) {
|
|
805
|
+
CachedEdge<Node> e1{};
|
|
806
|
+
if constexpr (std::is_same_v<Node, mNode> ||
|
|
807
|
+
std::is_same_v<Node, dNode>) {
|
|
808
|
+
if (x.isIdentity() || x.p->v < var) {
|
|
809
|
+
// [ 0 | 1 ] [ x | 0 ]
|
|
810
|
+
// --------- = ---------
|
|
811
|
+
// [ 2 | 3 ] [ 0 | x ]
|
|
812
|
+
if (i == 0 || i == 3) {
|
|
813
|
+
e1 = x;
|
|
814
|
+
}
|
|
815
|
+
} else {
|
|
816
|
+
auto& xSuccessor = x.p->e[i];
|
|
817
|
+
e1 = {xSuccessor.p, 0};
|
|
818
|
+
if (!xSuccessor.w.exactlyZero()) {
|
|
819
|
+
e1.w = x.w * xSuccessor.w;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
} else {
|
|
823
|
+
auto& xSuccessor = x.p->e[i];
|
|
824
|
+
e1 = {xSuccessor.p, 0};
|
|
825
|
+
if (!xSuccessor.w.exactlyZero()) {
|
|
826
|
+
e1.w = x.w * xSuccessor.w;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
CachedEdge<Node> e2{};
|
|
830
|
+
if constexpr (std::is_same_v<Node, mNode> ||
|
|
831
|
+
std::is_same_v<Node, dNode>) {
|
|
832
|
+
if (y.isIdentity() || y.p->v < var) {
|
|
833
|
+
// [ 0 | 1 ] [ y | 0 ]
|
|
834
|
+
// --------- = ---------
|
|
835
|
+
// [ 2 | 3 ] [ 0 | y ]
|
|
836
|
+
if (i == 0 || i == 3) {
|
|
837
|
+
e2 = y;
|
|
838
|
+
}
|
|
839
|
+
} else {
|
|
840
|
+
auto& ySuccessor = y.p->e[i];
|
|
841
|
+
e2 = {ySuccessor.p, 0};
|
|
842
|
+
if (!ySuccessor.w.exactlyZero()) {
|
|
843
|
+
e2.w = y.w * ySuccessor.w;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
} else {
|
|
847
|
+
auto& ySuccessor = y.p->e[i];
|
|
848
|
+
e2 = {ySuccessor.p, 0};
|
|
849
|
+
if (!ySuccessor.w.exactlyZero()) {
|
|
850
|
+
e2.w = y.w * ySuccessor.w;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
if constexpr (std::is_same_v<Node, dNode>) {
|
|
855
|
+
dNode::applyDmChangesToNode(e1.p);
|
|
856
|
+
dNode::applyDmChangesToNode(e2.p);
|
|
857
|
+
edge[i] = add2(e1, e2, var - 1);
|
|
858
|
+
dNode::revertDmChangesToNode(e2.p);
|
|
859
|
+
dNode::revertDmChangesToNode(e1.p);
|
|
860
|
+
} else {
|
|
861
|
+
edge[i] = add2(e1, e2, var - 1);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
auto r = makeDDNode(var, edge);
|
|
865
|
+
computeTable.insert(x, y, r);
|
|
866
|
+
return r;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* @brief Compute the element-wise magnitude sum of two vectors or matrices.
|
|
871
|
+
*
|
|
872
|
+
* For two vectors (or matrices) \p x and \p y, this function returns a result
|
|
873
|
+
* \p r such that for each index \p i:
|
|
874
|
+
* \f$ r[i] = \sqrt{|x[i]|^2 + |y[i]|^2} \f$
|
|
875
|
+
*
|
|
876
|
+
* @param x DD representation of the first operand.
|
|
877
|
+
* @param y DD representation of the second operand.
|
|
878
|
+
* @param var Number of qubits in the DD.
|
|
879
|
+
* @return DD representing the result.
|
|
880
|
+
*/
|
|
881
|
+
template <class Node>
|
|
882
|
+
CachedEdge<Node> addMagnitudes(const CachedEdge<Node>& x,
|
|
883
|
+
const CachedEdge<Node>& y, const Qubit var) {
|
|
884
|
+
if (x.w.exactlyZero()) {
|
|
885
|
+
if (y.w.exactlyZero()) {
|
|
886
|
+
return CachedEdge<Node>::zero();
|
|
887
|
+
}
|
|
888
|
+
const auto rWeight = y.w.mag();
|
|
889
|
+
return {y.p, rWeight};
|
|
890
|
+
}
|
|
891
|
+
if (y.w.exactlyZero()) {
|
|
892
|
+
const auto rWeight = x.w.mag();
|
|
893
|
+
return {x.p, rWeight};
|
|
894
|
+
}
|
|
895
|
+
if (x.p == y.p) {
|
|
896
|
+
const auto rWeight = std::sqrt(x.w.mag2() + y.w.mag2());
|
|
897
|
+
return {x.p, rWeight};
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
auto& computeTable = getAddMagnitudesComputeTable<Node>();
|
|
901
|
+
if (const auto* r = computeTable.lookup(x, y); r != nullptr) {
|
|
902
|
+
return *r;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
constexpr std::size_t n = std::tuple_size_v<decltype(x.p->e)>;
|
|
906
|
+
std::array<CachedEdge<Node>, n> edge{};
|
|
907
|
+
for (std::size_t i = 0U; i < n; i++) {
|
|
908
|
+
CachedEdge<Node> e1{};
|
|
909
|
+
if constexpr (std::is_same_v<Node, mNode> ||
|
|
910
|
+
std::is_same_v<Node, dNode>) {
|
|
911
|
+
if (x.isIdentity() || x.p->v < var) {
|
|
912
|
+
if (i == 0 || i == 3) {
|
|
913
|
+
e1 = x;
|
|
914
|
+
}
|
|
915
|
+
} else {
|
|
916
|
+
auto& xSuccessor = x.p->e[i];
|
|
917
|
+
e1 = {xSuccessor.p, 0};
|
|
918
|
+
if (!xSuccessor.w.exactlyZero()) {
|
|
919
|
+
e1.w = x.w * xSuccessor.w;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
} else {
|
|
923
|
+
auto& xSuccessor = x.p->e[i];
|
|
924
|
+
e1 = {xSuccessor.p, 0};
|
|
925
|
+
if (!xSuccessor.w.exactlyZero()) {
|
|
926
|
+
e1.w = x.w * xSuccessor.w;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
CachedEdge<Node> e2{};
|
|
930
|
+
if constexpr (std::is_same_v<Node, mNode> ||
|
|
931
|
+
std::is_same_v<Node, dNode>) {
|
|
932
|
+
if (y.isIdentity() || y.p->v < var) {
|
|
933
|
+
if (i == 0 || i == 3) {
|
|
934
|
+
e2 = y;
|
|
935
|
+
}
|
|
936
|
+
} else {
|
|
937
|
+
auto& ySuccessor = y.p->e[i];
|
|
938
|
+
e2 = {ySuccessor.p, 0};
|
|
939
|
+
if (!ySuccessor.w.exactlyZero()) {
|
|
940
|
+
e2.w = y.w * ySuccessor.w;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
} else {
|
|
944
|
+
auto& ySuccessor = y.p->e[i];
|
|
945
|
+
e2 = {ySuccessor.p, 0};
|
|
946
|
+
if (!ySuccessor.w.exactlyZero()) {
|
|
947
|
+
e2.w = y.w * ySuccessor.w;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
edge[i] = addMagnitudes(e1, e2, var - 1);
|
|
951
|
+
}
|
|
952
|
+
auto r = makeDDNode(var, edge);
|
|
953
|
+
computeTable.insert(x, y, r);
|
|
954
|
+
return r;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
///
|
|
958
|
+
/// Vector conjugation
|
|
959
|
+
///
|
|
960
|
+
UnaryComputeTable<vNode*, vCachedEdge> conjugateVector{
|
|
961
|
+
config_.ctVecConjNumBucket};
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* @brief Conjugates a given decision diagram edge.
|
|
965
|
+
*
|
|
966
|
+
* @param a The decision diagram edge to conjugate.
|
|
967
|
+
* @return The conjugated decision diagram edge.
|
|
968
|
+
*/
|
|
969
|
+
vEdge conjugate(const vEdge& a);
|
|
970
|
+
/**
|
|
971
|
+
* @brief Recursively conjugates a given decision diagram edge.
|
|
972
|
+
*
|
|
973
|
+
* @param a The decision diagram edge to conjugate.
|
|
974
|
+
* @return The conjugated decision diagram edge.
|
|
975
|
+
*/
|
|
976
|
+
vCachedEdge conjugateRec(const vEdge& a);
|
|
977
|
+
|
|
978
|
+
///
|
|
979
|
+
/// Matrix (conjugate) transpose
|
|
980
|
+
///
|
|
981
|
+
UnaryComputeTable<mNode*, mCachedEdge> conjugateMatrixTranspose{
|
|
982
|
+
config_.ctMatConjTransNumBucket};
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* @brief Computes the conjugate transpose of a given matrix edge.
|
|
986
|
+
*
|
|
987
|
+
* @param a The matrix edge to conjugate transpose.
|
|
988
|
+
* @return The conjugated transposed matrix edge.
|
|
989
|
+
*/
|
|
990
|
+
mEdge conjugateTranspose(const mEdge& a);
|
|
991
|
+
/**
|
|
992
|
+
* @brief Recursively computes the conjugate transpose of a given matrix edge.
|
|
993
|
+
*
|
|
994
|
+
* @param a The matrix edge to conjugate transpose.
|
|
995
|
+
* @return The conjugated transposed matrix edge.
|
|
996
|
+
*/
|
|
997
|
+
mCachedEdge conjugateTransposeRec(const mEdge& a);
|
|
998
|
+
|
|
999
|
+
///
|
|
1000
|
+
/// Multiplication
|
|
1001
|
+
///
|
|
1002
|
+
ComputeTable<mNode*, vNode*, vCachedEdge> matrixVectorMultiplication{
|
|
1003
|
+
config_.ctMatVecMultNumBucket};
|
|
1004
|
+
ComputeTable<mNode*, mNode*, mCachedEdge> matrixMatrixMultiplication{
|
|
1005
|
+
config_.ctMatMatMultNumBucket};
|
|
1006
|
+
ComputeTable<dNode*, dNode*, dCachedEdge> densityDensityMultiplication{
|
|
1007
|
+
config_.ctDmDmMultNumBucket};
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* @brief Get the compute table for multiplication operations.
|
|
1011
|
+
*
|
|
1012
|
+
* @tparam RightOperandNode The type of the right operand node.
|
|
1013
|
+
* @return A reference to the appropriate compute table for the given node
|
|
1014
|
+
* type.
|
|
1015
|
+
*/
|
|
1016
|
+
template <class RightOperandNode>
|
|
1017
|
+
[[nodiscard]] auto& getMultiplicationComputeTable() {
|
|
1018
|
+
if constexpr (std::is_same_v<RightOperandNode, vNode>) {
|
|
1019
|
+
return matrixVectorMultiplication;
|
|
1020
|
+
} else if constexpr (std::is_same_v<RightOperandNode, mNode>) {
|
|
1021
|
+
return matrixMatrixMultiplication;
|
|
1022
|
+
} else if constexpr (std::is_same_v<RightOperandNode, dNode>) {
|
|
1023
|
+
return densityDensityMultiplication;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
/**
|
|
1028
|
+
* @brief Applies a matrix operation to a vector.
|
|
1029
|
+
*
|
|
1030
|
+
* @details The reference count of the input vector is decreased,
|
|
1031
|
+
* while the reference count of the result is increased. After the operation,
|
|
1032
|
+
* garbage collection is triggered.
|
|
1033
|
+
*
|
|
1034
|
+
* @param operation Matrix operation to apply
|
|
1035
|
+
* @param e Vector to apply the operation to
|
|
1036
|
+
* @return The appropriately reference-counted result.
|
|
1037
|
+
*/
|
|
1038
|
+
VectorDD applyOperation(const MatrixDD& operation, const VectorDD& e);
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* @brief Applies a matrix operation to a matrix.
|
|
1042
|
+
*
|
|
1043
|
+
* @details The reference count of the input matrix is decreased,
|
|
1044
|
+
* while the reference count of the result is increased. After the operation,
|
|
1045
|
+
* garbage collection is triggered.
|
|
1046
|
+
*
|
|
1047
|
+
* @param operation Matrix operation to apply
|
|
1048
|
+
* @param e Matrix to apply the operation to
|
|
1049
|
+
* @param applyFromLeft Flag to indicate if the operation should be applied
|
|
1050
|
+
* from the left (default) or right.
|
|
1051
|
+
* @return The appropriately reference-counted result.
|
|
1052
|
+
*/
|
|
1053
|
+
MatrixDD applyOperation(const MatrixDD& operation, const MatrixDD& e,
|
|
1054
|
+
bool applyFromLeft = true);
|
|
1055
|
+
|
|
1056
|
+
dEdge applyOperationToDensity(dEdge& e, const mEdge& operation);
|
|
1057
|
+
|
|
1058
|
+
/**
|
|
1059
|
+
* @brief Multiplies two decision diagrams.
|
|
1060
|
+
*
|
|
1061
|
+
* @tparam LeftOperandNode The type of the left operand node.
|
|
1062
|
+
* @tparam RightOperandNode The type of the right operand node.
|
|
1063
|
+
* @param x The left operand decision diagram.
|
|
1064
|
+
* @param y The right operand decision diagram.
|
|
1065
|
+
* @param generateDensityMatrix Flag to indicate if a density matrix node
|
|
1066
|
+
* should be generated.
|
|
1067
|
+
* @return The resulting decision diagram after multiplication.
|
|
1068
|
+
*
|
|
1069
|
+
* @details This function performs the multiplication of two decision diagrams
|
|
1070
|
+
* (DDs). It uses a compute table to cache intermediate results and avoid
|
|
1071
|
+
* redundant computations. The multiplication is conducted recursively, where
|
|
1072
|
+
* the function traverses the nodes of the DDs, multiplies corresponding
|
|
1073
|
+
* edges, and normalizes the resulting edges. If the nodes are terminal, their
|
|
1074
|
+
* weights are directly multiplied. The function ensures that the resulting DD
|
|
1075
|
+
* is properly normalized and stored in the unique table to maintain the
|
|
1076
|
+
* canonical form.
|
|
1077
|
+
*/
|
|
1078
|
+
template <class LeftOperandNode, class RightOperandNode>
|
|
1079
|
+
Edge<RightOperandNode>
|
|
1080
|
+
multiply(const Edge<LeftOperandNode>& x, const Edge<RightOperandNode>& y,
|
|
1081
|
+
[[maybe_unused]] const bool generateDensityMatrix = false) {
|
|
1082
|
+
using LEdge = Edge<LeftOperandNode>;
|
|
1083
|
+
using REdge = Edge<RightOperandNode>;
|
|
1084
|
+
static_assert(std::disjunction_v<std::is_same<LEdge, mEdge>,
|
|
1085
|
+
std::is_same<LEdge, dEdge>>,
|
|
1086
|
+
"Left operand must be a matrix or density matrix");
|
|
1087
|
+
static_assert(std::disjunction_v<std::is_same<REdge, vEdge>,
|
|
1088
|
+
std::is_same<REdge, mEdge>,
|
|
1089
|
+
std::is_same<REdge, dEdge>>,
|
|
1090
|
+
"Right operand must be a vector, matrix or density matrix");
|
|
1091
|
+
Qubit var{};
|
|
1092
|
+
if constexpr (std::is_same_v<LEdge, dEdge>) {
|
|
1093
|
+
auto xCopy = x;
|
|
1094
|
+
auto yCopy = y;
|
|
1095
|
+
dEdge::applyDmChangesToEdges(xCopy, yCopy);
|
|
1096
|
+
|
|
1097
|
+
if (!xCopy.isTerminal()) {
|
|
1098
|
+
var = xCopy.p->v;
|
|
1099
|
+
}
|
|
1100
|
+
if (!y.isTerminal() && yCopy.p->v > var) {
|
|
1101
|
+
var = yCopy.p->v;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
const auto e = multiply2(xCopy, yCopy, var, generateDensityMatrix);
|
|
1105
|
+
dEdge::revertDmChangesToEdges(xCopy, yCopy);
|
|
1106
|
+
return cn.lookup(e);
|
|
1107
|
+
} else {
|
|
1108
|
+
if (!x.isTerminal()) {
|
|
1109
|
+
var = x.p->v;
|
|
1110
|
+
}
|
|
1111
|
+
if (!y.isTerminal() && y.p->v > var) {
|
|
1112
|
+
var = y.p->v;
|
|
1113
|
+
}
|
|
1114
|
+
const auto e = multiply2(x, y, var);
|
|
1115
|
+
return cn.lookup(e);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
private:
|
|
1120
|
+
/**
|
|
1121
|
+
* @brief Internal function to multiply two decision diagrams.
|
|
1122
|
+
*
|
|
1123
|
+
* This function is used internally to multiply two decision diagrams (DDs) of
|
|
1124
|
+
* type Node. It is not intended to be called directly.
|
|
1125
|
+
*
|
|
1126
|
+
* @tparam LeftOperandNode The type of the left operand node.
|
|
1127
|
+
* @tparam RightOperandNode The type of the right operand node.
|
|
1128
|
+
* @param x The left operand decision diagram.
|
|
1129
|
+
* @param y The right operand decision diagram.
|
|
1130
|
+
* @param var The variable associated with the current level of recursion.
|
|
1131
|
+
* @param generateDensityMatrix Flag to indicate if a density matrix node
|
|
1132
|
+
* should be generated.
|
|
1133
|
+
* @return The resulting DD after multiplication.
|
|
1134
|
+
*/
|
|
1135
|
+
template <class LeftOperandNode, class RightOperandNode>
|
|
1136
|
+
CachedEdge<RightOperandNode>
|
|
1137
|
+
multiply2(const Edge<LeftOperandNode>& x, const Edge<RightOperandNode>& y,
|
|
1138
|
+
const Qubit var,
|
|
1139
|
+
[[maybe_unused]] const bool generateDensityMatrix = false) {
|
|
1140
|
+
using LEdge = Edge<LeftOperandNode>;
|
|
1141
|
+
using REdge = Edge<RightOperandNode>;
|
|
1142
|
+
using ResultEdge = CachedEdge<RightOperandNode>;
|
|
1143
|
+
|
|
1144
|
+
if (x.w.exactlyZero() || y.w.exactlyZero()) {
|
|
1145
|
+
return ResultEdge::zero();
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
const auto xWeight = static_cast<ComplexValue>(x.w);
|
|
1149
|
+
const auto yWeight = static_cast<ComplexValue>(y.w);
|
|
1150
|
+
const auto rWeight = xWeight * yWeight;
|
|
1151
|
+
if (x.isIdentity()) {
|
|
1152
|
+
if constexpr (!std::is_same_v<RightOperandNode, dNode>) {
|
|
1153
|
+
return {y.p, rWeight};
|
|
1154
|
+
} else {
|
|
1155
|
+
if (y.isIdentity() ||
|
|
1156
|
+
(dNode::isDensityMatrixTempFlagSet(y.p->flags) &&
|
|
1157
|
+
generateDensityMatrix) ||
|
|
1158
|
+
(!dNode::isDensityMatrixTempFlagSet(y.p->flags) &&
|
|
1159
|
+
!generateDensityMatrix)) {
|
|
1160
|
+
return {y.p, rWeight};
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
if constexpr (std::is_same_v<RightOperandNode, mNode> ||
|
|
1166
|
+
std::is_same_v<RightOperandNode, dNode>) {
|
|
1167
|
+
if (y.isIdentity()) {
|
|
1168
|
+
if constexpr (!std::is_same_v<LeftOperandNode, dNode>) {
|
|
1169
|
+
return {x.p, rWeight};
|
|
1170
|
+
} else {
|
|
1171
|
+
if (x.isIdentity() ||
|
|
1172
|
+
(dNode::isDensityMatrixTempFlagSet(x.p->flags) &&
|
|
1173
|
+
generateDensityMatrix) ||
|
|
1174
|
+
(!dNode::isDensityMatrixTempFlagSet(x.p->flags) &&
|
|
1175
|
+
!generateDensityMatrix)) {
|
|
1176
|
+
return {x.p, rWeight};
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
auto& computeTable = getMultiplicationComputeTable<RightOperandNode>();
|
|
1183
|
+
if (const auto* r = computeTable.lookup(x.p, y.p, generateDensityMatrix);
|
|
1184
|
+
r != nullptr) {
|
|
1185
|
+
return {r->p, r->w * rWeight};
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
constexpr std::size_t n = std::tuple_size_v<decltype(y.p->e)>;
|
|
1189
|
+
|
|
1190
|
+
constexpr std::size_t rows = RADIX;
|
|
1191
|
+
constexpr std::size_t cols = n == NEDGE ? RADIX : 1U;
|
|
1192
|
+
|
|
1193
|
+
std::array<ResultEdge, n> edge{};
|
|
1194
|
+
for (auto i = 0U; i < rows; i++) {
|
|
1195
|
+
for (auto j = 0U; j < cols; j++) {
|
|
1196
|
+
auto idx = (cols * i) + j;
|
|
1197
|
+
edge[idx] = ResultEdge::zero();
|
|
1198
|
+
for (auto k = 0U; k < rows; k++) {
|
|
1199
|
+
const auto xIdx = (rows * i) + k;
|
|
1200
|
+
LEdge e1{};
|
|
1201
|
+
if (x.p != nullptr && x.p->v == var) {
|
|
1202
|
+
e1 = x.p->e[xIdx];
|
|
1203
|
+
} else {
|
|
1204
|
+
if (xIdx == 0 || xIdx == 3) {
|
|
1205
|
+
e1 = LEdge{x.p, Complex::one()};
|
|
1206
|
+
} else {
|
|
1207
|
+
e1 = LEdge::zero();
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
const auto yIdx = j + (cols * k);
|
|
1212
|
+
REdge e2{};
|
|
1213
|
+
if (y.p != nullptr && y.p->v == var) {
|
|
1214
|
+
e2 = y.p->e[yIdx];
|
|
1215
|
+
} else {
|
|
1216
|
+
if (yIdx == 0 || yIdx == 3) {
|
|
1217
|
+
e2 = REdge{y.p, Complex::one()};
|
|
1218
|
+
} else {
|
|
1219
|
+
e2 = REdge::zero();
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
const auto v = static_cast<Qubit>(var - 1);
|
|
1224
|
+
if constexpr (std::is_same_v<LeftOperandNode, dNode>) {
|
|
1225
|
+
dCachedEdge m;
|
|
1226
|
+
dEdge::applyDmChangesToEdges(e1, e2);
|
|
1227
|
+
if (!generateDensityMatrix || idx == 1) {
|
|
1228
|
+
// When generateDensityMatrix is false or I have the first edge I
|
|
1229
|
+
// don't optimize anything and set generateDensityMatrix to false
|
|
1230
|
+
// for all child edges
|
|
1231
|
+
m = multiply2(e1, e2, v, false);
|
|
1232
|
+
} else if (idx == 2) {
|
|
1233
|
+
// When I have the second edge and generateDensityMatrix == false,
|
|
1234
|
+
// then edge[2] == edge[1]
|
|
1235
|
+
if (k == 0) {
|
|
1236
|
+
if (edge[1].w.approximatelyZero()) {
|
|
1237
|
+
edge[2] = ResultEdge::zero();
|
|
1238
|
+
} else {
|
|
1239
|
+
edge[2] = edge[1];
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
continue;
|
|
1243
|
+
} else {
|
|
1244
|
+
m = multiply2(e1, e2, v, generateDensityMatrix);
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
if (k == 0 || edge[idx].w.exactlyZero()) {
|
|
1248
|
+
edge[idx] = m;
|
|
1249
|
+
} else if (!m.w.exactlyZero()) {
|
|
1250
|
+
dNode::applyDmChangesToNode(edge[idx].p);
|
|
1251
|
+
dNode::applyDmChangesToNode(m.p);
|
|
1252
|
+
edge[idx] = add2(edge[idx], m, v);
|
|
1253
|
+
dNode::revertDmChangesToNode(m.p);
|
|
1254
|
+
dNode::revertDmChangesToNode(edge[idx].p);
|
|
1255
|
+
}
|
|
1256
|
+
// Undo modifications on density matrices
|
|
1257
|
+
dEdge::revertDmChangesToEdges(e1, e2);
|
|
1258
|
+
} else {
|
|
1259
|
+
auto m = multiply2(e1, e2, v);
|
|
1260
|
+
|
|
1261
|
+
if (k == 0 || edge[idx].w.exactlyZero()) {
|
|
1262
|
+
edge[idx] = m;
|
|
1263
|
+
} else if (!m.w.exactlyZero()) {
|
|
1264
|
+
edge[idx] = add2(edge[idx], m, v);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
auto e = makeDDNode(var, edge, generateDensityMatrix);
|
|
1272
|
+
computeTable.insert(x.p, y.p, e);
|
|
1273
|
+
|
|
1274
|
+
e.w = e.w * rWeight;
|
|
1275
|
+
return e;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
///
|
|
1279
|
+
/// Inner product, fidelity, expectation value
|
|
1280
|
+
///
|
|
1281
|
+
public:
|
|
1282
|
+
ComputeTable<vNode*, vNode*, vCachedEdge> vectorInnerProduct{
|
|
1283
|
+
config_.ctVecInnerProdNumBucket};
|
|
1284
|
+
|
|
1285
|
+
/**
|
|
1286
|
+
* @brief Calculates the inner product of two vector decision diagrams.
|
|
1287
|
+
*
|
|
1288
|
+
* @param x A vector DD representing a quantum state.
|
|
1289
|
+
* @param y A vector DD representing a quantum state.
|
|
1290
|
+
* @return A complex number representing the scalar product of the DDs.
|
|
1291
|
+
*/
|
|
1292
|
+
ComplexValue innerProduct(const vEdge& x, const vEdge& y);
|
|
1293
|
+
|
|
1294
|
+
/**
|
|
1295
|
+
* @brief Calculates the fidelity between two vector decision diagrams.
|
|
1296
|
+
*
|
|
1297
|
+
* @param x A vector DD representing a quantum state.
|
|
1298
|
+
* @param y A vector DD representing a quantum state.
|
|
1299
|
+
* @return The fidelity between the two quantum states.
|
|
1300
|
+
*/
|
|
1301
|
+
fp fidelity(const vEdge& x, const vEdge& y);
|
|
1302
|
+
|
|
1303
|
+
/**
|
|
1304
|
+
* @brief Calculates the fidelity between a vector decision diagram and a
|
|
1305
|
+
* sparse probability vector.
|
|
1306
|
+
*
|
|
1307
|
+
* @details This function computes the fidelity between a quantum state
|
|
1308
|
+
* represented by a vector decision diagram and a sparse probability vector.
|
|
1309
|
+
* The optional permutation of qubits can be provided to match the qubit
|
|
1310
|
+
* ordering.
|
|
1311
|
+
*
|
|
1312
|
+
* @param e The root edge of the decision diagram.
|
|
1313
|
+
* @param probs A map of probabilities for each measurement outcome.
|
|
1314
|
+
* @param permutation An optional permutation of qubits.
|
|
1315
|
+
* @return The fidelity of the measurement outcomes.
|
|
1316
|
+
*/
|
|
1317
|
+
static fp
|
|
1318
|
+
fidelityOfMeasurementOutcomes(const vEdge& e, const SparsePVec& probs,
|
|
1319
|
+
const qc::Permutation& permutation = {});
|
|
1320
|
+
|
|
1321
|
+
private:
|
|
1322
|
+
/**
|
|
1323
|
+
* @brief Recursively calculates the inner product of two vector decision
|
|
1324
|
+
* diagrams.
|
|
1325
|
+
*
|
|
1326
|
+
* @param x A vector DD representing a quantum state.
|
|
1327
|
+
* @param y A vector DD representing a quantum state.
|
|
1328
|
+
* @param var The number of levels contained in each vector DD.
|
|
1329
|
+
* @return A complex number representing the scalar product of the DDs.
|
|
1330
|
+
* @note This function is called recursively such that the number of levels
|
|
1331
|
+
* decreases each time to traverse the DDs.
|
|
1332
|
+
*/
|
|
1333
|
+
ComplexValue innerProduct(const vEdge& x, const vEdge& y, Qubit var);
|
|
1334
|
+
|
|
1335
|
+
/**
|
|
1336
|
+
* @brief Recursively calculates the fidelity of measurement outcomes.
|
|
1337
|
+
*
|
|
1338
|
+
* @details This function computes the fidelity between a quantum state
|
|
1339
|
+
* represented by a vector decision diagram and a sparse probability vector.
|
|
1340
|
+
* It traverses the decision diagram recursively, calculating the contribution
|
|
1341
|
+
* of each path to the overall fidelity. An optional permutation of qubits can
|
|
1342
|
+
* be provided to match the qubit ordering.
|
|
1343
|
+
*
|
|
1344
|
+
* @param e The root edge of the decision diagram.
|
|
1345
|
+
* @param probs A map of probabilities for each measurement outcome.
|
|
1346
|
+
* @param i The current index in the decision diagram traversal.
|
|
1347
|
+
* @param permutation An optional permutation of qubits.
|
|
1348
|
+
* @param nQubits The number of qubits in the decision diagram.
|
|
1349
|
+
* @return The fidelity of the measurement outcomes.
|
|
1350
|
+
*/
|
|
1351
|
+
static fp fidelityOfMeasurementOutcomesRecursive(
|
|
1352
|
+
const vEdge& e, const SparsePVec& probs, std::size_t i,
|
|
1353
|
+
const qc::Permutation& permutation, std::size_t nQubits);
|
|
1354
|
+
|
|
1355
|
+
public:
|
|
1356
|
+
/**
|
|
1357
|
+
* @brief Calculates the expectation value of an operator with respect to a
|
|
1358
|
+
* quantum state.
|
|
1359
|
+
*
|
|
1360
|
+
* @param x A matrix decision diagram (DD) representing the operator.
|
|
1361
|
+
* @param y A vector decision diagram (DD) representing the quantum state.
|
|
1362
|
+
* @return A floating-point value representing the expectation value of the
|
|
1363
|
+
* operator with respect to the quantum state.
|
|
1364
|
+
* @throws std::runtime_error if the edges are not on the same level or if the
|
|
1365
|
+
* expectation value is non-real.
|
|
1366
|
+
*
|
|
1367
|
+
* @details This function calls the multiply() function to apply the operator
|
|
1368
|
+
* to the quantum state, then calls innerProduct() to calculate the overlap
|
|
1369
|
+
* between the original state and the applied state (i.e., <Psi| Psi'> = <Psi|
|
|
1370
|
+
* (Op|Psi>)). It also calls the garbageCollect() function to free up any
|
|
1371
|
+
* unused memory.
|
|
1372
|
+
*/
|
|
1373
|
+
fp expectationValue(const mEdge& x, const vEdge& y);
|
|
1374
|
+
|
|
1375
|
+
///
|
|
1376
|
+
/// Kronecker/tensor product
|
|
1377
|
+
///
|
|
1378
|
+
|
|
1379
|
+
ComputeTable<vNode*, vNode*, vCachedEdge> vectorKronecker{
|
|
1380
|
+
config_.ctVecKronNumBucket};
|
|
1381
|
+
ComputeTable<mNode*, mNode*, mCachedEdge> matrixKronecker{
|
|
1382
|
+
config_.ctMatKronNumBucket};
|
|
1383
|
+
|
|
1384
|
+
/**
|
|
1385
|
+
* @brief Get the compute table for Kronecker product operations.
|
|
1386
|
+
*
|
|
1387
|
+
* @tparam Node The type of the node.
|
|
1388
|
+
* @return A reference to the appropriate compute table for the given node
|
|
1389
|
+
* type.
|
|
1390
|
+
*/
|
|
1391
|
+
template <class Node> [[nodiscard]] auto& getKroneckerComputeTable() {
|
|
1392
|
+
if constexpr (std::is_same_v<Node, vNode>) {
|
|
1393
|
+
return vectorKronecker;
|
|
1394
|
+
} else {
|
|
1395
|
+
return matrixKronecker;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
/**
|
|
1400
|
+
* @brief Computes the Kronecker product of two decision diagrams.
|
|
1401
|
+
*
|
|
1402
|
+
* @tparam Node The type of the node.
|
|
1403
|
+
* @param x The first decision diagram.
|
|
1404
|
+
* @param y The second decision diagram.
|
|
1405
|
+
* @param yNumQubits The number of qubits in the second decision diagram.
|
|
1406
|
+
* @param incIdx Whether to increment the index of the nodes in the second
|
|
1407
|
+
* decision diagram.
|
|
1408
|
+
* @return The resulting decision diagram after computing the Kronecker
|
|
1409
|
+
* product.
|
|
1410
|
+
* @throws std::invalid_argument if the node type is `dNode` (density
|
|
1411
|
+
* matrices).
|
|
1412
|
+
*/
|
|
1413
|
+
template <class Node>
|
|
1414
|
+
Edge<Node> kronecker(const Edge<Node>& x, const Edge<Node>& y,
|
|
1415
|
+
const std::size_t yNumQubits, const bool incIdx = true) {
|
|
1416
|
+
if constexpr (std::is_same_v<Node, dNode>) {
|
|
1417
|
+
throw std::invalid_argument(
|
|
1418
|
+
"Kronecker is currently not supported for density matrices");
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
const auto e = kronecker2(x, y, yNumQubits, incIdx);
|
|
1422
|
+
return cn.lookup(e);
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
private:
|
|
1426
|
+
/**
|
|
1427
|
+
* @brief Internal function to compute the Kronecker product of two decision
|
|
1428
|
+
* diagrams.
|
|
1429
|
+
*
|
|
1430
|
+
* This function is used internally to compute the Kronecker product of two
|
|
1431
|
+
* decision diagrams (DDs) of type Node. It is not intended to be called
|
|
1432
|
+
* directly.
|
|
1433
|
+
*
|
|
1434
|
+
* @tparam Node The type of the node.
|
|
1435
|
+
* @param x The first decision diagram.
|
|
1436
|
+
* @param y The second decision diagram.
|
|
1437
|
+
* @param yNumQubits The number of qubits in the second decision diagram.
|
|
1438
|
+
* @param incIdx Whether to increment the qubit index.
|
|
1439
|
+
* @return The resulting decision diagram after the Kronecker product.
|
|
1440
|
+
*/
|
|
1441
|
+
template <class Node>
|
|
1442
|
+
CachedEdge<Node> kronecker2(const Edge<Node>& x, const Edge<Node>& y,
|
|
1443
|
+
const std::size_t yNumQubits,
|
|
1444
|
+
const bool incIdx = true) {
|
|
1445
|
+
if (x.w.exactlyZero() || y.w.exactlyZero()) {
|
|
1446
|
+
return CachedEdge<Node>::zero();
|
|
1447
|
+
}
|
|
1448
|
+
const auto xWeight = static_cast<ComplexValue>(x.w);
|
|
1449
|
+
if (xWeight.approximatelyZero()) {
|
|
1450
|
+
return CachedEdge<Node>::zero();
|
|
1451
|
+
}
|
|
1452
|
+
const auto yWeight = static_cast<ComplexValue>(y.w);
|
|
1453
|
+
if (yWeight.approximatelyZero()) {
|
|
1454
|
+
return CachedEdge<Node>::zero();
|
|
1455
|
+
}
|
|
1456
|
+
const auto rWeight = xWeight * yWeight;
|
|
1457
|
+
if (rWeight.approximatelyZero()) {
|
|
1458
|
+
return CachedEdge<Node>::zero();
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
if (x.isTerminal() && y.isTerminal()) {
|
|
1462
|
+
return {x.p, rWeight};
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
if constexpr (std::is_same_v<Node, mNode> || std::is_same_v<Node, dNode>) {
|
|
1466
|
+
if (x.isIdentity()) {
|
|
1467
|
+
return {y.p, rWeight};
|
|
1468
|
+
}
|
|
1469
|
+
} else {
|
|
1470
|
+
if (x.isTerminal()) {
|
|
1471
|
+
return {y.p, rWeight};
|
|
1472
|
+
}
|
|
1473
|
+
if (y.isTerminal()) {
|
|
1474
|
+
return {x.p, rWeight};
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
// check if we already computed the product before and return the result
|
|
1479
|
+
auto& computeTable = getKroneckerComputeTable<Node>();
|
|
1480
|
+
if (const auto* r = computeTable.lookup(x.p, y.p); r != nullptr) {
|
|
1481
|
+
return {r->p, rWeight};
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
constexpr std::size_t n = std::tuple_size_v<decltype(x.p->e)>;
|
|
1485
|
+
std::array<CachedEdge<Node>, n> edge{};
|
|
1486
|
+
for (auto i = 0U; i < n; ++i) {
|
|
1487
|
+
edge[i] = kronecker2(x.p->e[i], y, yNumQubits, incIdx);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
// Increase the qubit index
|
|
1491
|
+
Qubit idx = x.p->v;
|
|
1492
|
+
if (incIdx) {
|
|
1493
|
+
// use the given number of qubits if y is an identity
|
|
1494
|
+
if constexpr (std::is_same_v<Node, mNode> ||
|
|
1495
|
+
std::is_same_v<Node, dNode>) {
|
|
1496
|
+
if (y.isIdentity()) {
|
|
1497
|
+
idx += static_cast<Qubit>(yNumQubits);
|
|
1498
|
+
} else {
|
|
1499
|
+
idx += static_cast<Qubit>(y.p->v + 1U);
|
|
1500
|
+
}
|
|
1501
|
+
} else {
|
|
1502
|
+
idx += static_cast<Qubit>(y.p->v + 1U);
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
auto e = makeDDNode(idx, edge, true);
|
|
1506
|
+
computeTable.insert(x.p, y.p, {e.p, e.w});
|
|
1507
|
+
return {e.p, rWeight};
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
///
|
|
1511
|
+
/// (Partial) trace
|
|
1512
|
+
///
|
|
1513
|
+
public:
|
|
1514
|
+
UnaryComputeTable<dNode*, dCachedEdge> densityTrace{
|
|
1515
|
+
config_.ctDmTraceNumBucket};
|
|
1516
|
+
UnaryComputeTable<mNode*, mCachedEdge> matrixTrace{
|
|
1517
|
+
config_.ctMatTraceNumBucket};
|
|
1518
|
+
|
|
1519
|
+
/**
|
|
1520
|
+
* @brief Get the compute table for trace operations.
|
|
1521
|
+
*
|
|
1522
|
+
* @tparam Node The type of the node.
|
|
1523
|
+
* @return A reference to the appropriate compute table for the given node
|
|
1524
|
+
* type.
|
|
1525
|
+
*/
|
|
1526
|
+
template <class Node> [[nodiscard]] auto& getTraceComputeTable() {
|
|
1527
|
+
if constexpr (std::is_same_v<Node, mNode>) {
|
|
1528
|
+
return matrixTrace;
|
|
1529
|
+
} else {
|
|
1530
|
+
return densityTrace;
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
/**
|
|
1535
|
+
* @brief Computes the partial trace of a matrix decision diagram.
|
|
1536
|
+
*
|
|
1537
|
+
* @param a The matrix decision diagram.
|
|
1538
|
+
* @param eliminate A vector of booleans indicating which qubits to trace out.
|
|
1539
|
+
* @return The resulting matrix decision diagram after the partial trace.
|
|
1540
|
+
*/
|
|
1541
|
+
mEdge partialTrace(const mEdge& a, const std::vector<bool>& eliminate);
|
|
1542
|
+
|
|
1543
|
+
/**
|
|
1544
|
+
* @brief Computes the trace of a decision diagram.
|
|
1545
|
+
*
|
|
1546
|
+
* @tparam Node The type of the node.
|
|
1547
|
+
* @param a The decision diagram.
|
|
1548
|
+
* @param numQubits The number of qubits in the decision diagram.
|
|
1549
|
+
* @return The trace of the decision diagram as a complex value.
|
|
1550
|
+
*/
|
|
1551
|
+
template <class Node>
|
|
1552
|
+
ComplexValue trace(const Edge<Node>& a, const std::size_t numQubits) {
|
|
1553
|
+
if (a.isIdentity()) {
|
|
1554
|
+
return static_cast<ComplexValue>(a.w);
|
|
1555
|
+
}
|
|
1556
|
+
const auto eliminate = std::vector<bool>(numQubits, true);
|
|
1557
|
+
return trace(a, eliminate, numQubits).w;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
/**
|
|
1561
|
+
* @brief Checks if a given matrix is close to the identity matrix.
|
|
1562
|
+
* @details This function checks if a given matrix is close to the identity
|
|
1563
|
+
* matrix, while ignoring any potential garbage qubits and ignoring the
|
|
1564
|
+
* diagonal weights if `checkCloseToOne` is set to false.
|
|
1565
|
+
* @param m An mEdge that represents the DD of the matrix.
|
|
1566
|
+
* @param tol The accepted tolerance for the edge weights of the DD.
|
|
1567
|
+
* @param garbage A vector of boolean values that defines which qubits are
|
|
1568
|
+
* considered garbage qubits. If it's empty, then no qubit is considered to be
|
|
1569
|
+
* a garbage qubit.
|
|
1570
|
+
* @param checkCloseToOne If false, the function only checks if the matrix is
|
|
1571
|
+
* close to a diagonal matrix.
|
|
1572
|
+
*/
|
|
1573
|
+
[[nodiscard]] bool isCloseToIdentity(const mEdge& m, fp tol = 1e-10,
|
|
1574
|
+
const std::vector<bool>& garbage = {},
|
|
1575
|
+
bool checkCloseToOne = true) const;
|
|
1576
|
+
|
|
1577
|
+
private:
|
|
1578
|
+
/**
|
|
1579
|
+
* @brief Computes the normalized (partial) trace using a compute table to
|
|
1580
|
+
* store results for eliminated nodes.
|
|
1581
|
+
* @details At each level, perform a lookup and store results in the compute
|
|
1582
|
+
* table only if all lower-level qubits are eliminated as well.
|
|
1583
|
+
*
|
|
1584
|
+
* This optimization allows the full trace
|
|
1585
|
+
* computation to scale linearly with respect to the number of nodes.
|
|
1586
|
+
* However, the partial trace computation still scales with the number of
|
|
1587
|
+
* paths to the lowest level in the DD that should be traced out.
|
|
1588
|
+
*
|
|
1589
|
+
* For matrices, normalization is continuously applied, dividing by two at
|
|
1590
|
+
* each level marked for elimination, thereby ensuring that the result is
|
|
1591
|
+
* mapped to the interval [0,1] (as opposed to the interval [0,2^N]).
|
|
1592
|
+
*
|
|
1593
|
+
* For density matrices, such normalization is not applied as the trace of
|
|
1594
|
+
* density matrices is always 1 by definition.
|
|
1595
|
+
*/
|
|
1596
|
+
template <class Node>
|
|
1597
|
+
CachedEdge<Node> trace(const Edge<Node>& a,
|
|
1598
|
+
const std::vector<bool>& eliminate, std::size_t level,
|
|
1599
|
+
std::size_t alreadyEliminated = 0) {
|
|
1600
|
+
const auto aWeight = static_cast<ComplexValue>(a.w);
|
|
1601
|
+
if (aWeight.approximatelyZero()) {
|
|
1602
|
+
return CachedEdge<Node>::zero();
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
// If `a` is the identity matrix or there is nothing left to eliminate,
|
|
1606
|
+
// then simply return `a`
|
|
1607
|
+
if (a.isIdentity() ||
|
|
1608
|
+
std::none_of(eliminate.begin(),
|
|
1609
|
+
eliminate.begin() +
|
|
1610
|
+
static_cast<std::vector<bool>::difference_type>(level),
|
|
1611
|
+
[](bool v) { return v; })) {
|
|
1612
|
+
return CachedEdge<Node>{a.p, aWeight};
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
const auto v = a.p->v;
|
|
1616
|
+
if (eliminate[v]) {
|
|
1617
|
+
// Lookup nodes marked for elimination in the compute table if all
|
|
1618
|
+
// lower-level qubits are eliminated as well: if the trace has already
|
|
1619
|
+
// been computed, return the result
|
|
1620
|
+
const auto eliminateAll = std::all_of(
|
|
1621
|
+
eliminate.begin(),
|
|
1622
|
+
eliminate.begin() +
|
|
1623
|
+
static_cast<std::vector<bool>::difference_type>(level),
|
|
1624
|
+
[](bool e) { return e; });
|
|
1625
|
+
if (eliminateAll) {
|
|
1626
|
+
if (const auto* r = getTraceComputeTable<Node>().lookup(a.p);
|
|
1627
|
+
r != nullptr) {
|
|
1628
|
+
return {r->p, r->w * aWeight};
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
const auto elims = alreadyEliminated + 1;
|
|
1633
|
+
auto r = add2(trace(a.p->e[0], eliminate, level - 1, elims),
|
|
1634
|
+
trace(a.p->e[3], eliminate, level - 1, elims), v - 1);
|
|
1635
|
+
|
|
1636
|
+
// The resulting weight is continuously normalized to the range [0,1] for
|
|
1637
|
+
// matrix nodes
|
|
1638
|
+
if constexpr (std::is_same_v<Node, mNode>) {
|
|
1639
|
+
r.w = r.w / 2.0;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
// Insert result into compute table if all lower-level qubits are
|
|
1643
|
+
// eliminated as well
|
|
1644
|
+
if (eliminateAll) {
|
|
1645
|
+
getTraceComputeTable<Node>().insert(a.p, r);
|
|
1646
|
+
}
|
|
1647
|
+
r.w = r.w * aWeight;
|
|
1648
|
+
return r;
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
std::array<CachedEdge<Node>, NEDGE> edge{};
|
|
1652
|
+
std::transform(a.p->e.cbegin(), a.p->e.cend(), edge.begin(),
|
|
1653
|
+
[this, &eliminate, &alreadyEliminated,
|
|
1654
|
+
&level](const Edge<Node>& e) -> CachedEdge<Node> {
|
|
1655
|
+
return trace(e, eliminate, level - 1, alreadyEliminated);
|
|
1656
|
+
});
|
|
1657
|
+
const auto adjustedV =
|
|
1658
|
+
static_cast<Qubit>(static_cast<std::size_t>(a.p->v) -
|
|
1659
|
+
(static_cast<std::size_t>(std::count(
|
|
1660
|
+
eliminate.begin(), eliminate.end(), true)) -
|
|
1661
|
+
alreadyEliminated));
|
|
1662
|
+
auto r = makeDDNode(adjustedV, edge);
|
|
1663
|
+
r.w = r.w * aWeight;
|
|
1664
|
+
return r;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
/**
|
|
1668
|
+
* @brief Recursively checks if a given matrix is close to the identity
|
|
1669
|
+
* matrix.
|
|
1670
|
+
*
|
|
1671
|
+
* @param m The matrix edge to check.
|
|
1672
|
+
* @param visited A set of visited nodes to avoid redundant checks.
|
|
1673
|
+
* @param tol The tolerance for comparing edge weights.
|
|
1674
|
+
* @param garbage A vector of boolean values indicating which qubits are
|
|
1675
|
+
* considered garbage.
|
|
1676
|
+
* @param checkCloseToOne A flag to indicate whether to check if diagonal
|
|
1677
|
+
* elements are close to one.
|
|
1678
|
+
* @return True if the matrix is close to the identity matrix, false
|
|
1679
|
+
* otherwise.
|
|
1680
|
+
*/
|
|
1681
|
+
static bool isCloseToIdentityRecursive(
|
|
1682
|
+
const mEdge& m, std::unordered_set<decltype(m.p)>& visited, fp tol,
|
|
1683
|
+
const std::vector<bool>& garbage, bool checkCloseToOne);
|
|
1684
|
+
|
|
1685
|
+
public:
|
|
1686
|
+
///
|
|
1687
|
+
/// Identity matrices
|
|
1688
|
+
///
|
|
1689
|
+
|
|
1690
|
+
/// Create identity DD represented by the one-terminal.
|
|
1691
|
+
static mEdge makeIdent();
|
|
1692
|
+
|
|
1693
|
+
mEdge createInitialMatrix(const std::vector<bool>& ancillary);
|
|
1694
|
+
|
|
1695
|
+
///
|
|
1696
|
+
/// Noise Operations
|
|
1697
|
+
///
|
|
1698
|
+
StochasticNoiseOperationTable<mEdge> stochasticNoiseOperationCache{
|
|
1699
|
+
nqubits, config_.stochasticCacheOps};
|
|
1700
|
+
DensityNoiseTable<dEdge, dEdge> densityNoise{config_.ctDmNoiseNumBucket};
|
|
1701
|
+
|
|
1702
|
+
///
|
|
1703
|
+
/// Ancillary and garbage reduction
|
|
1704
|
+
///
|
|
1705
|
+
|
|
1706
|
+
/**
|
|
1707
|
+
* @brief Reduces the decision diagram by handling ancillary qubits.
|
|
1708
|
+
*
|
|
1709
|
+
* @param e The matrix decision diagram edge to be reduced.
|
|
1710
|
+
* @param ancillary A boolean vector indicating which qubits are ancillary
|
|
1711
|
+
* (true) or not (false).
|
|
1712
|
+
* @param regular Flag indicating whether to perform regular (true) or inverse
|
|
1713
|
+
* (false) reduction.
|
|
1714
|
+
* @return The reduced matrix decision diagram edge.
|
|
1715
|
+
*
|
|
1716
|
+
* @details This function modifies the decision diagram to account for
|
|
1717
|
+
* ancillary qubits by:
|
|
1718
|
+
* 1. Early returning if there are no ancillary qubits or if the edge is zero
|
|
1719
|
+
* 2. Special handling for identity matrices by creating appropriate zero
|
|
1720
|
+
* nodes
|
|
1721
|
+
* 3. Finding the lowest ancillary qubit as a starting point
|
|
1722
|
+
* 4. Recursively reducing nodes starting from the lowest ancillary qubit
|
|
1723
|
+
* 5. Adding zero nodes for any remaining higher ancillary qubits
|
|
1724
|
+
*
|
|
1725
|
+
* The function maintains proper reference counting by incrementing the
|
|
1726
|
+
* reference count of the result and decrementing the reference count of the
|
|
1727
|
+
* input edge.
|
|
1728
|
+
*/
|
|
1729
|
+
mEdge reduceAncillae(mEdge e, const std::vector<bool>& ancillary,
|
|
1730
|
+
bool regular = true);
|
|
1731
|
+
|
|
1732
|
+
/**
|
|
1733
|
+
* @brief Reduces the given decision diagram by summing entries for garbage
|
|
1734
|
+
* qubits.
|
|
1735
|
+
*
|
|
1736
|
+
* For each garbage qubit q, this function sums all the entries for q = 0 and
|
|
1737
|
+
* q = 1, setting the entry for q = 0 to the sum and the entry for q = 1 to
|
|
1738
|
+
* zero. To ensure that the probabilities of the resulting state are the sum
|
|
1739
|
+
* of the probabilities of the initial state, the function computes
|
|
1740
|
+
* `sqrt(|a|^2 + |b|^2)` for two entries `a` and `b`.
|
|
1741
|
+
*
|
|
1742
|
+
* @param e DD representation of the matrix/vector.
|
|
1743
|
+
* @param garbage Vector that describes which qubits are garbage and which
|
|
1744
|
+
* ones are not. If garbage[i] = true, then qubit q_i is considered garbage.
|
|
1745
|
+
* @param normalizeWeights By default set to `false`. If set to `true`, the
|
|
1746
|
+
* function changes all weights in the DD to their magnitude, also for
|
|
1747
|
+
* non-garbage qubits. This is used for checking
|
|
1748
|
+
* partial equivalence of circuits. For partial equivalence, only the
|
|
1749
|
+
* measurement probabilities are considered, so we
|
|
1750
|
+
* need to consider only the magnitudes of each entry.
|
|
1751
|
+
* @return DD representing the reduced matrix/vector.
|
|
1752
|
+
*/
|
|
1753
|
+
vEdge reduceGarbage(vEdge& e, const std::vector<bool>& garbage,
|
|
1754
|
+
bool normalizeWeights = false);
|
|
1755
|
+
|
|
1756
|
+
/**
|
|
1757
|
+
* @brief Reduces garbage qubits in a matrix decision diagram.
|
|
1758
|
+
*
|
|
1759
|
+
* @param e The matrix decision diagram edge to be reduced.
|
|
1760
|
+
* @param garbage A boolean vector indicating which qubits are garbage (true)
|
|
1761
|
+
* or not (false).
|
|
1762
|
+
* @param regular Flag indicating whether to apply regular (true) or inverse
|
|
1763
|
+
* (false) reduction. In regular mode, garbage entries are summed in the first
|
|
1764
|
+
* two components, in inverse mode, they are summed in the first and third
|
|
1765
|
+
* components.
|
|
1766
|
+
* @param normalizeWeights Flag indicating whether to normalize weights to
|
|
1767
|
+
* their magnitudes. When true, all weights in the DD are changed to their
|
|
1768
|
+
* magnitude, also for non-garbage qubits. This is used for checking partial
|
|
1769
|
+
* equivalence where only measurement probabilities matter.
|
|
1770
|
+
* @return The reduced matrix decision diagram edge.
|
|
1771
|
+
*
|
|
1772
|
+
* @details For each garbage qubit q, this function sums all the entries for
|
|
1773
|
+
* q=0 and q=1, setting the entry for q=0 to the sum and the entry for q=1 to
|
|
1774
|
+
* zero. To maintain proper probabilities, the function computes sqrt(|a|^2 +
|
|
1775
|
+
* |b|^2) for two entries a and b. The function handles special cases like
|
|
1776
|
+
* zero terminals and identity matrices separately and maintains proper
|
|
1777
|
+
* reference counting throughout the reduction process.
|
|
1778
|
+
*/
|
|
1779
|
+
mEdge reduceGarbage(const mEdge& e, const std::vector<bool>& garbage,
|
|
1780
|
+
bool regular = true, bool normalizeWeights = false);
|
|
1781
|
+
|
|
1782
|
+
private:
|
|
1783
|
+
mCachedEdge reduceAncillaeRecursion(mNode* p,
|
|
1784
|
+
const std::vector<bool>& ancillary,
|
|
1785
|
+
Qubit lowerbound, bool regular = true);
|
|
1786
|
+
|
|
1787
|
+
vCachedEdge reduceGarbageRecursion(vNode* p, const std::vector<bool>& garbage,
|
|
1788
|
+
Qubit lowerbound,
|
|
1789
|
+
bool normalizeWeights = false);
|
|
1790
|
+
mCachedEdge reduceGarbageRecursion(mNode* p, const std::vector<bool>& garbage,
|
|
1791
|
+
Qubit lowerbound, bool regular = true,
|
|
1792
|
+
bool normalizeWeights = false);
|
|
1793
|
+
|
|
1794
|
+
///
|
|
1795
|
+
/// Vector and matrix extraction from DDs
|
|
1796
|
+
///
|
|
1797
|
+
public:
|
|
1798
|
+
/// transfers a decision diagram from another package to this package
|
|
1799
|
+
template <class Node> Edge<Node> transfer(Edge<Node>& original) {
|
|
1800
|
+
if (original.isTerminal()) {
|
|
1801
|
+
return {original.p, cn.lookup(original.w)};
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
// POST ORDER TRAVERSAL USING ONE STACK
|
|
1805
|
+
// https://www.geeksforgeeks.org/iterative-postorder-traversal-using-stack/
|
|
1806
|
+
Edge<Node> root{};
|
|
1807
|
+
std::stack<Edge<Node>*> stack;
|
|
1808
|
+
|
|
1809
|
+
std::unordered_map<decltype(original.p), decltype(original.p)> mappedNode{};
|
|
1810
|
+
|
|
1811
|
+
Edge<Node>* currentEdge = &original;
|
|
1812
|
+
constexpr std::size_t n = std::tuple_size_v<decltype(original.p->e)>;
|
|
1813
|
+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-do-while)
|
|
1814
|
+
do {
|
|
1815
|
+
while (currentEdge != nullptr && !currentEdge->isTerminal()) {
|
|
1816
|
+
for (std::size_t i = n - 1; i > 0; --i) {
|
|
1817
|
+
auto& edge = currentEdge->p->e[i];
|
|
1818
|
+
if (edge.isTerminal()) {
|
|
1819
|
+
continue;
|
|
1820
|
+
}
|
|
1821
|
+
if (edge.w.approximatelyZero()) {
|
|
1822
|
+
continue;
|
|
1823
|
+
}
|
|
1824
|
+
if (mappedNode.contains(edge.p)) {
|
|
1825
|
+
continue;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
// non-zero edge to be included
|
|
1829
|
+
stack.push(&edge);
|
|
1830
|
+
}
|
|
1831
|
+
stack.push(currentEdge);
|
|
1832
|
+
currentEdge = ¤tEdge->p->e[0];
|
|
1833
|
+
}
|
|
1834
|
+
currentEdge = stack.top();
|
|
1835
|
+
stack.pop();
|
|
1836
|
+
|
|
1837
|
+
bool hasChild = false;
|
|
1838
|
+
for (std::size_t i = 1; i < n && !hasChild; ++i) {
|
|
1839
|
+
auto& edge = currentEdge->p->e[i];
|
|
1840
|
+
if (edge.w.approximatelyZero()) {
|
|
1841
|
+
continue;
|
|
1842
|
+
}
|
|
1843
|
+
if (mappedNode.contains(edge.p)) {
|
|
1844
|
+
continue;
|
|
1845
|
+
}
|
|
1846
|
+
hasChild = edge.p == stack.top()->p;
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
if (hasChild) {
|
|
1850
|
+
Edge<Node>* temp = stack.top();
|
|
1851
|
+
stack.pop();
|
|
1852
|
+
stack.push(currentEdge);
|
|
1853
|
+
currentEdge = temp;
|
|
1854
|
+
} else {
|
|
1855
|
+
if (mappedNode.contains(currentEdge->p)) {
|
|
1856
|
+
currentEdge = nullptr;
|
|
1857
|
+
continue;
|
|
1858
|
+
}
|
|
1859
|
+
std::array<Edge<Node>, n> edges{};
|
|
1860
|
+
for (std::size_t i = 0; i < n; i++) {
|
|
1861
|
+
if (currentEdge->p->e[i].isTerminal()) {
|
|
1862
|
+
edges[i].p = currentEdge->p->e[i].p;
|
|
1863
|
+
} else {
|
|
1864
|
+
edges[i].p = mappedNode[currentEdge->p->e[i].p];
|
|
1865
|
+
}
|
|
1866
|
+
edges[i].w = cn.lookup(currentEdge->p->e[i].w);
|
|
1867
|
+
}
|
|
1868
|
+
root = makeDDNode(currentEdge->p->v, edges);
|
|
1869
|
+
mappedNode[currentEdge->p] = root.p;
|
|
1870
|
+
currentEdge = nullptr;
|
|
1871
|
+
}
|
|
1872
|
+
} while (!stack.empty());
|
|
1873
|
+
root.w = cn.lookup(original.w * root.w);
|
|
1874
|
+
return root;
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
///
|
|
1878
|
+
/// Deserialization
|
|
1879
|
+
/// Note: do not rely on the binary format being portable across different
|
|
1880
|
+
/// architectures/platforms
|
|
1881
|
+
///
|
|
1882
|
+
|
|
1883
|
+
template <class Node, class Edge = Edge<Node>,
|
|
1884
|
+
std::size_t N = std::tuple_size_v<decltype(Node::e)>>
|
|
1885
|
+
Edge deserialize(std::istream& is, const bool readBinary = false) {
|
|
1886
|
+
auto result = CachedEdge<Node>{};
|
|
1887
|
+
ComplexValue rootweight{};
|
|
1888
|
+
|
|
1889
|
+
std::unordered_map<std::int64_t, Node*> nodes{};
|
|
1890
|
+
std::int64_t nodeIndex{};
|
|
1891
|
+
Qubit v{};
|
|
1892
|
+
std::array<ComplexValue, N> edgeWeights{};
|
|
1893
|
+
std::array<std::int64_t, N> edgeIndices{};
|
|
1894
|
+
edgeIndices.fill(-2);
|
|
1895
|
+
|
|
1896
|
+
if (readBinary) {
|
|
1897
|
+
std::remove_const_t<decltype(SERIALIZATION_VERSION)> version{};
|
|
1898
|
+
is.read(reinterpret_cast<char*>(&version),
|
|
1899
|
+
sizeof(decltype(SERIALIZATION_VERSION)));
|
|
1900
|
+
if (version != SERIALIZATION_VERSION) {
|
|
1901
|
+
throw std::runtime_error(
|
|
1902
|
+
"Wrong Version of serialization file version. version of file: " +
|
|
1903
|
+
std::to_string(version) +
|
|
1904
|
+
"; current version: " + std::to_string(SERIALIZATION_VERSION));
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
if (!is.eof()) {
|
|
1908
|
+
rootweight.readBinary(is);
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
while (is.read(reinterpret_cast<char*>(&nodeIndex),
|
|
1912
|
+
sizeof(decltype(nodeIndex)))) {
|
|
1913
|
+
is.read(reinterpret_cast<char*>(&v), sizeof(decltype(v)));
|
|
1914
|
+
for (std::size_t i = 0U; i < N; i++) {
|
|
1915
|
+
is.read(reinterpret_cast<char*>(&edgeIndices[i]),
|
|
1916
|
+
sizeof(decltype(edgeIndices[i])));
|
|
1917
|
+
edgeWeights[i].readBinary(is);
|
|
1918
|
+
}
|
|
1919
|
+
result = deserializeNode(nodeIndex, v, edgeIndices, edgeWeights, nodes);
|
|
1920
|
+
}
|
|
1921
|
+
} else {
|
|
1922
|
+
std::string version;
|
|
1923
|
+
std::getline(is, version);
|
|
1924
|
+
if (std::stoi(version) != SERIALIZATION_VERSION) {
|
|
1925
|
+
throw std::runtime_error(
|
|
1926
|
+
"Wrong Version of serialization file version. version of file: " +
|
|
1927
|
+
version +
|
|
1928
|
+
"; current version: " + std::to_string(SERIALIZATION_VERSION));
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
const std::string complexRealRegex =
|
|
1932
|
+
R"(([+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?(?![ \d\.]*(?:[eE][+-])?\d*[iI]))?)";
|
|
1933
|
+
const std::string complexImagRegex =
|
|
1934
|
+
R"(( ?[+-]? ?(?:(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?)?[iI])?)";
|
|
1935
|
+
const std::string edgeRegex =
|
|
1936
|
+
" \\(((-?\\d+) (" + complexRealRegex + complexImagRegex + "))?\\)";
|
|
1937
|
+
const std::regex complexWeightRegex(complexRealRegex + complexImagRegex);
|
|
1938
|
+
|
|
1939
|
+
std::string lineConstruct = "(\\d+) (\\d+)";
|
|
1940
|
+
for (std::size_t i = 0U; i < N; ++i) {
|
|
1941
|
+
lineConstruct += "(?:" + edgeRegex + ")";
|
|
1942
|
+
}
|
|
1943
|
+
lineConstruct += " *(?:#.*)?";
|
|
1944
|
+
const std::regex lineRegex(lineConstruct);
|
|
1945
|
+
std::smatch m;
|
|
1946
|
+
|
|
1947
|
+
std::string line;
|
|
1948
|
+
if (std::getline(is, line)) {
|
|
1949
|
+
if (!std::regex_match(line, m, complexWeightRegex)) {
|
|
1950
|
+
throw std::runtime_error("Regex did not match second line: " + line);
|
|
1951
|
+
}
|
|
1952
|
+
rootweight.fromString(m.str(1), m.str(2));
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
while (std::getline(is, line)) {
|
|
1956
|
+
if (line.empty() || line.size() == 1) {
|
|
1957
|
+
continue;
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
if (!std::regex_match(line, m, lineRegex)) {
|
|
1961
|
+
throw std::runtime_error("Regex did not match line: " + line);
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
// match 1: node_idx
|
|
1965
|
+
// match 2: qubit_idx
|
|
1966
|
+
|
|
1967
|
+
// repeats for every edge
|
|
1968
|
+
// match 3: edge content
|
|
1969
|
+
// match 4: edge_target_idx
|
|
1970
|
+
// match 5: real + imag (without i)
|
|
1971
|
+
// match 6: real
|
|
1972
|
+
// match 7: imag (without i)
|
|
1973
|
+
nodeIndex = std::stoi(m.str(1));
|
|
1974
|
+
v = static_cast<Qubit>(std::stoi(m.str(2)));
|
|
1975
|
+
|
|
1976
|
+
for (auto edgeIdx = 3U, i = 0U; i < N; i++, edgeIdx += 5) {
|
|
1977
|
+
if (m.str(edgeIdx).empty()) {
|
|
1978
|
+
continue;
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
edgeIndices[i] = std::stoi(m.str(edgeIdx + 1));
|
|
1982
|
+
edgeWeights[i].fromString(m.str(edgeIdx + 3), m.str(edgeIdx + 4));
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
result = deserializeNode(nodeIndex, v, edgeIndices, edgeWeights, nodes);
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
return {result.p, cn.lookup(result.w * rootweight)};
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
template <class Node, class Edge = Edge<Node>>
|
|
1992
|
+
Edge deserialize(const std::string& inputFilename, const bool readBinary) {
|
|
1993
|
+
auto ifs = std::ifstream(inputFilename, std::ios::binary);
|
|
1994
|
+
|
|
1995
|
+
if (!ifs.good()) {
|
|
1996
|
+
throw std::invalid_argument("Cannot open serialized file: " +
|
|
1997
|
+
inputFilename);
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
return deserialize<Node>(ifs, readBinary);
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
private:
|
|
2004
|
+
template <class Node, std::size_t N = std::tuple_size_v<decltype(Node::e)>>
|
|
2005
|
+
CachedEdge<Node>
|
|
2006
|
+
deserializeNode(const std::int64_t index, const Qubit v,
|
|
2007
|
+
std::array<std::int64_t, N>& edgeIdx,
|
|
2008
|
+
const std::array<ComplexValue, N>& edgeWeight,
|
|
2009
|
+
std::unordered_map<std::int64_t, Node*>& nodes) {
|
|
2010
|
+
if (index == -1) {
|
|
2011
|
+
return CachedEdge<Node>::zero();
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
std::array<CachedEdge<Node>, N> edges{};
|
|
2015
|
+
for (auto i = 0U; i < N; ++i) {
|
|
2016
|
+
if (edgeIdx[i] == -2) {
|
|
2017
|
+
edges[i] = CachedEdge<Node>::zero();
|
|
2018
|
+
} else {
|
|
2019
|
+
if (edgeIdx[i] == -1) {
|
|
2020
|
+
edges[i] = CachedEdge<Node>::one();
|
|
2021
|
+
} else {
|
|
2022
|
+
edges[i].p = nodes[edgeIdx[i]];
|
|
2023
|
+
}
|
|
2024
|
+
edges[i].w = edgeWeight[i];
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
// reset
|
|
2028
|
+
edgeIdx.fill(-2);
|
|
2029
|
+
|
|
2030
|
+
auto r = makeDDNode(v, edges);
|
|
2031
|
+
nodes[index] = r.p;
|
|
2032
|
+
return r;
|
|
2033
|
+
}
|
|
2034
|
+
};
|
|
2035
|
+
|
|
2036
|
+
} // namespace dd
|